The Problem

Oinkoin is a Flutter‑based expense‑tracker that runs completely offline, targeting users who need privacy‑first budgeting without cloud sync or ads. While the app delivers on its core promise, the source base shows several maintainability red flags that affect on‑board‑ing new contributors and long‑term evolution.

What This Does

Oinkoin is a Dart/Flutter project that stores records locally in a SQLite database (lib/services/database/sqlite-database.dart). The entry point lib/main.dart boots the Flutter framework and immediately delegates to feature modules such as budgeting, categories, and record management. Core data models live under lib/models/ (record.dart, category.dart, budget.dart) and are manipulated by helpers in lib/helpers/ (e.g., amount-input-utils.dart, date_picker_utils.dart). The app presents UI through page widgets in lib/budgets/, lib/categories/, and lib/records/. A separate website/ directory (React/Next‑js) provides the project’s public docs and release notes, while appium/ contains end‑to‑end mobile automation tests. No network calls are made; all data lives in the device’s local database.

How It Is Wired

The import graph resolves to 12 internal modules, 0 import edges, and no circular dependencies – the code base is deliberately flat, which reduces ripple effects when changing a single module. Execution starts at lib/main.dartFlutterMaterialApp → routing to the relevant feature widget (e.g., budgets-page.dart). From there, database writes flow through sqlite-database.dart (1 353 LOC, an oversized file flagged for splitting). Helper functions such as parseAmount and formatCurrency are called from multiple pages, creating the duplicated‑code blocks identified across 96 files. The app never leaves the device: there are no network‑related functions, no API keys, and no environment‑variable requirements beyond the standard Flutter setup.

How To Use It

  • Setup ``bash git clone https://github.com/moses-y/oinkoin cd oinkoin flutter pub get # installs Dart dependencies listed in pubspec.yaml ` The root also contains package.json (for the website) and pubspec.yaml` (for the Flutter app).
  • Configuration No secrets or API keys are required; the app is offline by design. If a custom locale is needed, place JSON files under assets/locales/ – the i18n_helper.dart reads them at runtime.
  • Running it ``bash flutter run -d android # launches the Android debug build # For a Windows build: flutter build windows `` The README documents these commands verbatim; no extra flags are needed.

Real‑World Use

A user opens the app, taps Add Expense, fills amount, selects a category (e.g., “Food”), and saves. The record is persisted instantly to the local SQLite database via sqlite-database.insertRecord(). Later, the user can view daily totals generated by records-per-day.dart and export a CSV backup using the backup.dart model – all without an internet connection.

Code Health & Issues

  • 94 total findings (34 high, 60 medium, 0 low) from static analysis:
  • [HIGH / cognitive_load] Deep nesting – max indentation depth 8 in lib/budgets/budgets-page.dart, lib/categories/categories-tab-page-edit.dart, lib/categories/categories-tab-page-view.dart.
  • [HIGH / clarity] Duplicated code blocks – 1 039 repeated 6‑line snippets across 96 files, including lib/budgets/budgets-page.dart and lib/recurrent_record_patterns/patterns-page-view.dart.
  • [HIGH / cognitive_load] Oversized files – lib/budgets/budgets-page.dart, lib/records/edit-record-page.dart, lib/services/database/sqlite-database.dart each exceed 1 300 LOC, making changes ripple widely.
  • [MEDIUM / resilience] Broad exception handling – scripts/oinkoin_from_csv_importer.py uses bare except Exception: that swallows errors indiscriminately.
  • Code‑health audit (7 findings, 0 critical):
  • HIGH – Pin third‑party GitHub Actions to commit SHAs (.github/workflows): stefanzweifel/git-auto-commit-action@v5, softprops/action-gh-release@v1, r0adkll/upload-google-play@v1.
  • HIGH – Commit a lockfile beside the manifest (package.json); run the package manager once and commit the generated lockfile.
  • HIGH – Make CI invoke the test suite it has (.github/workflows): add a test step to the existing 4 workflows; currently 94 test files are never executed in CI.
  • MEDIUM – Enable Dependabot or Renovate (.github/dependabot.yml covering npm and GitHub‑Actions).
  • MEDIUM – Gate pull requests on a dependency vulnerability scan (add dependency-review-action or osv-scanner).
  • LOW – Set timeout-minutes on workflow jobs (.github/workflows/build-alpha-arm64.yml and others).
  • LOW – Add convention files (.editorconfig, .gitattributes with text=auto eol=lf, formatter config).

The Bottom Line

Oinkoin delivers a clean, offline expense‑tracking experience with a modest Flutter code base, but the presence of deep nesting, large duplicated blocks, and oversized files hampers future feature work and onboarding. The CI pipeline is green yet never runs assertions, and third‑party Actions are unpinned, exposing the repo to secret‑leak risk. Teams that value privacy‑first, ad‑free budgeting will find the app functional today, but should allocate effort to restructure the identified hot‑spot files and lock down Action versions before scaling or opening the code to external contributors.