diff --git a/README.md b/README.md index d1a0ef0..2bb2c04 100644 --- a/README.md +++ b/README.md @@ -1,83 +1,159 @@ -# MyApp +# IngTrak -Generic Flutter Android starter built with Flutter **3.47** (Dart **3.13**), Material 3, null safety, and [Provider](https://pub.dev/packages/provider) for state management. +Personal finance app for **Enable Banking** (ING and other supported banks): transactions, labels, charts, budgets, look-ahead projections, and **PocketBase** cloud sync. -Package name: `com.ingtrak.app` +- **Package:** `com.ingtrak.app` +- **Platform:** Android (Flutter) +- **Flutter:** 3.47+ · **Dart:** 3.13+ ## Features -- Splash screen that fades into the main shell -- Bottom navigation: Home, Search, Profile, Settings -- `ListView.builder` catalog with rounded, shadowed cards -- Detail screen with a matching `Hero` animation -- Light / dark / system theme, persisted with `shared_preferences` -- Add-item form with validation -- Mock catalog plus an `http` client aimed at JSONPlaceholder (toggle in Settings) +- **Bank connect** — Enable Banking via backend API + in-app WebView; optional deep link `ingtrak://connected` +- **Home** — Balance, customizable layout: built-in sections (Transactions / Categorized / Recent) and filter widgets (stats or budget, half/full width, bar/pie/ring) +- **Transactions** — Search, filters, month heatmap, labels, notes, merchant / “apply to similar” rules +- **Overview** — Income/expense and label charts; save **favourite** chart filters +- **Look ahead** — Projected balance from recurring templates +- **Sync** — Bidirectional PocketBase sync (transactions, labels, annotations, favourites, home widgets, app prefs) +- **Theme** — Light / dark / system (local) +- **CSV** — Import ING CSV exports (Settings) -## Folder structure +## Prerequisites + +- [Flutter SDK](https://docs.flutter.dev/get-started/install) **3.47** or newer (Dart **3.13**) +- Android Studio / SDK (Java **17**), emulator or device +- Reachable **bank API** (`API_BASE_URL`) and **PocketBase** (`POCKETBASE_URL`) +- PocketBase auth collection **`usersapp`** (create this before running migrations) + +```bash +flutter doctor +flutter doctor --android-licenses # if prompted +``` + +## Quick start + +```bash +cd ingtrakapp +cp .env.example .env # edit URLs if needed +flutter pub get +flutter devices +flutter run # or: flutter run -d android +``` + +First Android build downloads Gradle and may take a few minutes. + +### Useful commands + +```bash +flutter analyze +flutter test +flutter run --release +flutter build apk +``` + +### Override config without editing `.env` + +```bash +flutter run \ + --dart-define=API_BASE_URL=https://your-api.example \ + --dart-define=POCKETBASE_URL=https://your-pb.example \ + --dart-define=POCKETBASE_USERS_COLLECTION=usersapp +``` + +`--dart-define` values win over `.env`. + +## Configuration + +Copy [`.env.example`](.env.example) to `.env` (both are listed as Flutter assets). No secrets belong in these files. + +| Variable | Purpose | Example | +|----------|---------|---------| +| `API_BASE_URL` | FastAPI bank / Enable Banking proxy | `https://ingtrak.satoshinakamoto.win` | +| `POCKETBASE_URL` | PocketBase instance | `https://pb.satoshinakamoto.win` | +| `POCKETBASE_USERS_COLLECTION` | Auth collection name | `usersapp` | + +Loaded in `lib/utils/app_config.dart` at startup (`flutter_dotenv`). Fallbacks: hosted API URL, PocketBase `http://127.0.0.1:8090`. + +### Bank redirect URIs + +1. **HTTP callback (Enable Banking Control Panel / backend `REDIRECT_URI`):** + `{API_BASE_URL}/api/connect/callback` +2. **Optional app deep link** (after HTML bounce; not registered with Enable Banking): + `ingtrak://connected?code=…` + Declared in `AndroidManifest.xml` and handled via `app_links`. + +## PocketBase setup + +Migrations live in [`pocketbase/pb_migrations/`](pocketbase/pb_migrations/). They assume collection **`usersapp`** already exists. + +### Collections + +| Migration | Collection | +|-----------|------------| +| `1740000000` | `labels`, `tx_meta`, `merchant_rules`, `exact_templates` | +| `1740000001` | `transactions` | +| `1740000002` | `recurring_templates` | +| `1740000003` | `overview_favourites` | +| `1740000004`–`0006` | `home_filter_widgets` (+ half width, budget fields) | +| `1740000007` | `app_prefs` (Home section toggles + Look ahead years) | + +Built-in labels stay in the app; only **custom** labels sync. Rows are scoped per user (`user = @request.auth.id`). + +### Apply migrations (typical Linux host) + +```bash +sudo cp pocketbase/pb_migrations/*.js /opt/pocketbase/pb_migrations/ +sudo chown pocketbase:pocketbase /opt/pocketbase/pb_migrations/*.js +sudo -u pocketbase /opt/pocketbase/pocketbase migrate up +sudo systemctl restart pocketbase +``` + +Adjust paths if your PocketBase install differs. After migrate, sign in on the app and run **Sync** (Account) so local data uploads. + +### What syncs + +- Bank transactions, labels, tx annotations, merchant/exact templates +- Recurring templates, overview favourites, home filter widgets +- App prefs: Home built-in section visibility, Look ahead max years + +Theme mode stays **device-local**. + +## Project layout ``` -android-flutter-app-generic/ -├── android/ # Android embedding, Gradle, manifest +ingtrakapp/ +├── android/ # Android embedding (com.ingtrak.app) +├── assets/branding/ # App icon source + generated launcher assets ├── lib/ -│ ├── main.dart # Entry point, Provider scope, MaterialApp -│ ├── models/ -│ │ └── item.dart # Item + ItemCategory -│ ├── providers/ -│ │ ├── item_provider.dart # Catalog ChangeNotifier -│ │ └── theme_provider.dart # ThemeMode ChangeNotifier -│ ├── screens/ -│ │ ├── splash_screen.dart -│ │ ├── main_shell.dart # NavigationBar host -│ │ ├── home_screen.dart -│ │ ├── search_screen.dart -│ │ ├── profile_screen.dart -│ │ ├── settings_screen.dart -│ │ ├── item_detail_screen.dart -│ │ └── add_item_screen.dart -│ ├── services/ -│ │ ├── storage_service.dart # shared_preferences wrapper -│ │ ├── item_api.dart # http placeholder -│ │ ├── fake_item_data.dart # Offline sample catalog -│ │ └── item_repository.dart # Mock vs remote switch -│ ├── utils/ -│ │ ├── app_theme.dart # Material 3 light/dark themes -│ │ ├── constants.dart -│ │ └── helpers.dart -│ └── widgets/ -│ ├── app_text_field.dart -│ ├── empty_state.dart -│ ├── item_card.dart -│ ├── item_hero_badge.dart -│ ├── loading_view.dart -│ └── responsive_body.dart +│ ├── main.dart # Providers, dotenv, deep links +│ ├── models/ # Bank, meta, favourites, home widgets, prefs +│ ├── providers/ # Auth, bank, sync, tx_meta, preferences, theme +│ ├── screens/ # Splash, auth, Home / Overview / Look ahead, … +│ ├── services/ # Bank API, PocketBase sync, CSV, storage +│ ├── utils/ # Config, theme, constants, connect helpers +│ └── widgets/ # Home widgets, filters, charts, sync UI +├── pocketbase/pb_migrations/ # Schema migrations ├── test/ -│ └── widget_test.dart +├── .env.example └── pubspec.yaml ``` -## Run the app +## Launcher icon -You need the [Flutter SDK](https://docs.flutter.dev/get-started/install) (3.47 or newer) and an Android emulator or device. +Source: `assets/branding/app_icon.png`. Regenerate Android mipmaps: ```bash -cd android-flutter-app-generic -flutter pub get -flutter run +dart run flutter_launcher_icons ``` -Useful extras: +(Configured in `pubspec.yaml`; iOS not enabled — no `ios/` folder yet.) -```bash -flutter devices # list emulators / devices -flutter run -d android # target Android explicitly -flutter analyze # static analysis -flutter test # widget + unit tests -``` +## First-run checklist -On first Android run, Flutter will download a Gradle distribution and compile the app. Accept any Android licenses with `flutter doctor --android-licenses` if prompted. +1. `.env` points at your API + PocketBase +2. PocketBase migrations applied; `usersapp` exists; create an account (or use Admin → Users) +3. Backend Enable Banking `REDIRECT_URI` matches `{API_BASE_URL}/api/connect/callback` +4. `flutter run` → sign in → **Settings** → connect bank (or import CSV) → **Account** → Sync -## Theme and data +## License / privacy -- Open **Settings** to switch System / Light / Dark. The choice is stored locally. -- Turn on **Use remote sample API** to fetch posts from `https://jsonplaceholder.typicode.com/posts` through the `http` package. If the request fails, the app falls back to the bundled mock list. +Private project. Bank credentials and tokens are handled by your backend and Enable Banking — keep API and PocketBase access controlled. diff --git a/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..d4908e2 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..e266103 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..f8bf60c Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..5a732f9 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..54f4736 Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..c79c58a --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index db77bb4..efed86a 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 17987b7..7ea834c 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 09d4391..c78d2c7 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index d5f1c8d..fd591ed 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 4d6372e..cbd1007 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..334fd89 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #1A1C1B + \ No newline at end of file diff --git a/assets/branding/app_icon.png b/assets/branding/app_icon.png new file mode 100644 index 0000000..9792689 Binary files /dev/null and b/assets/branding/app_icon.png differ diff --git a/assets/branding/app_icon_foreground.png b/assets/branding/app_icon_foreground.png new file mode 100644 index 0000000..9792689 Binary files /dev/null and b/assets/branding/app_icon_foreground.png differ diff --git a/pubspec.lock b/pubspec.lock index 9d9b516..19160e9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -33,6 +33,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + archive: + dependency: transitive + description: + name: archive + sha256: ace891da0862b0e4cabbb064ee3fd87b2728b898949fdb366d83fe98342c9f19 + url: "https://pub.dev" + source: hosted + version: "4.2.0" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" async: dependency: transitive description: @@ -57,6 +73,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" + url: "https://pub.dev" + source: hosted + version: "2.0.4" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c + url: "https://pub.dev" + source: hosted + version: "0.4.2" clock: dependency: transitive description: @@ -150,6 +182,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.1" + flutter_launcher_icons: + dependency: "direct dev" + description: + name: flutter_launcher_icons + sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7" + url: "https://pub.dev" + source: hosted + version: "0.14.4" flutter_lints: dependency: "direct dev" description: @@ -200,6 +240,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + image: + dependency: transitive + description: + name: image + sha256: "1976370a4df3091bb0f72409c187ad1f9132a818bc6b95ca59c0bae1c75c688e" + url: "https://pub.dev" + source: hosted + version: "4.9.2" json_annotation: dependency: transitive description: @@ -328,6 +376,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.25.1" + posix: + dependency: transitive + description: + name: posix + sha256: bc1bad54ad2b735816e31f8d4600cfde6c7839975085ddfbca48b6c9f7c4044e + url: "https://pub.dev" + source: hosted + version: "6.5.2" provider: dependency: "direct main" description: @@ -525,6 +581,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: f67cdd8e07d3c6329146aaef1ba043542b3134c12489f553ca9a7435d1068aea + url: "https://pub.dev" + source: hosted + version: "3.1.4" sdks: dart: ">=3.13.2 <4.0.0" flutter: ">=3.44.0" diff --git a/pubspec.yaml b/pubspec.yaml index 21f45d7..5ce2ca6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -53,6 +53,15 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^6.0.0 + flutter_launcher_icons: ^0.14.4 + +flutter_launcher_icons: + android: true + ios: false + image_path: assets/branding/app_icon.png + adaptive_icon_background: "#1A1C1B" + adaptive_icon_foreground: assets/branding/app_icon_foreground.png + min_sdk_android: 21 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec