From ef7e7baf19450b50bd1d3944990c0be704b87a20 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Sun, 15 Mar 2026 19:33:29 +0100 Subject: [PATCH] docs(01-foundation): research phase domain --- .planning/phases/01-foundation/01-RESEARCH.md | 550 ++++++++++++++++++ 1 file changed, 550 insertions(+) create mode 100644 .planning/phases/01-foundation/01-RESEARCH.md diff --git a/.planning/phases/01-foundation/01-RESEARCH.md b/.planning/phases/01-foundation/01-RESEARCH.md new file mode 100644 index 0000000..4591f48 --- /dev/null +++ b/.planning/phases/01-foundation/01-RESEARCH.md @@ -0,0 +1,550 @@ +# Phase 1: Foundation - Research + +**Researched:** 2026-03-15 +**Domain:** Flutter app scaffolding, Drift SQLite, Riverpod 3 state management, Material 3 theming, ARB localization +**Confidence:** HIGH + +## Summary + +Phase 1 establishes the foundational architecture for a greenfield Flutter app targeting Android. The core technologies are well-documented and stable: Flutter 3.41.2 (current stable), Riverpod 3.3 with code generation, Drift 2.32 for SQLite persistence, and Flutter's built-in gen_l10n tooling for ARB-based localization. All of these are mature, actively maintained, and have clear official documentation. + +The primary risk areas are: (1) Riverpod 3's new `analysis_server_plugin`-based lint setup replacing the older `custom_lint` approach, which may have IDE integration quirks, and (2) getting the Drift `make-migrations` workflow right from the start since retrofitting it later risks data loss. The developer is new to Drift, so the plan should include explicit verification steps for the migration workflow. + +**Primary recommendation:** Scaffold with `flutter create`, add all dependencies in one pass, establish code generation (`build_runner`) and the Drift migration pipeline before writing any feature code. Use `go_router` with `StatefulShellRoute.indexedStack` for bottom navigation with preserved tab state. + + + +## User Constraints (from CONTEXT.md) + +### Locked Decisions +- **Color palette & theme:** Sage & stone -- muted sage green primary, warm stone/beige surfaces, slate blue accents. Color-shy: mostly neutral surfaces, color only on key interactive elements. Light mode: warm stone/beige surface tones. Dark mode: warm charcoal-brown backgrounds (not pure #121212 black). Use M3 color system with ColorScheme.fromSeed, tuning the seed and surface tints. +- **Navigation & tabs:** Thematic icons (checklist/clipboard for Home, door for Rooms, sliders for Settings). German labels from ARB files: "Ubersicht", "Raume", "Einstellungen". Default tab: Home. Active tab style: standard Material 3 behavior with sage green palette. +- **Placeholder screens:** Playful & light tone with emoji-friendly German text. Pattern: Material icon + playful message + action button. Home empty state guides user to Rooms tab. Rooms empty state encourages creating first room. All text from ARB files. +- **Settings screen:** Working theme switcher (System/Hell/Dunkel) + About section (app name, version, tagline). Grouped with section headers "Darstellung" and "Uber". Tagline: "Dein Haushalt, entspannt organisiert." Language setting hidden until v1.1. + +### Claude's Discretion +- Theme picker widget style (segmented button, dropdown, or bottom sheet -- pick best M3 pattern) +- Exact icon choices for thematic tab icons (from Material Icons set) +- Loading skeleton and transition animations +- Exact spacing, typography scale, and component sizing +- Error state designs (database/initialization errors) + +### Deferred Ideas (OUT OF SCOPE) +None -- discussion stayed within phase scope + + + + + +## Phase Requirements + +| ID | Description | Research Support | +|----|-------------|-----------------| +| FOUND-01 | App uses Drift for local SQLite storage with proper schema migration workflow | Drift 2.32 setup with `drift_flutter`, `make-migrations` command, `build.yaml` config, stepByStep migration strategy, schema versioning and test generation | +| FOUND-02 | App uses Riverpod 3 for state management with code generation | `flutter_riverpod` 3.3.x + `riverpod_annotation` 4.0.x + `riverpod_generator` 4.0.x with `@riverpod` annotation and `build_runner` | +| FOUND-03 | App uses localization infrastructure (ARB files + AppLocalizations) with German locale | `l10n.yaml` with `template-arb-file: app_de.arb`, `flutter_localizations` SDK dependency, `generate: true` in pubspec | +| FOUND-04 | Bottom navigation with tabs: Home (Daily Plan), Rooms, Settings | `go_router` 17.x with `StatefulShellRoute.indexedStack` for preserved tab navigation state, `NavigationBar` (M3 widget) | +| THEME-01 | App supports light and dark themes, following the system setting by default | `ThemeMode.system` default, `ColorScheme.fromSeed` with brightness variants, Riverpod provider for theme preference persistence | +| THEME-02 | App uses a calm Material 3 palette with muted greens, warm grays, and gentle blues | Sage green seed color with `DynamicSchemeVariant.tonalSpot`, surface color overrides via `.copyWith()` for warm stone/charcoal tones | + + + +## Standard Stack + +### Core +| Library | Version | Purpose | Why Standard | +|---------|---------|---------|--------------| +| flutter | 3.41.2 | UI framework | Current stable, required by Riverpod 3.3 | +| flutter_riverpod | ^3.3.0 | State management (Flutter bindings) | Community standard for new Flutter projects in 2026, compile-time safety | +| riverpod_annotation | ^4.0.2 | `@riverpod` annotation for code generation | Required for Riverpod 3 code generation workflow | +| drift | ^2.32.0 | Reactive SQLite ORM | Type-safe queries, migration tooling, compile-time validation | +| drift_flutter | ^0.3.0 | Flutter-specific database opener | Simplifies platform-specific SQLite setup | +| go_router | ^17.1.0 | Declarative routing | Flutter team maintained, `StatefulShellRoute` for bottom nav | + +### Supporting +| Library | Version | Purpose | When to Use | +|---------|---------|---------|-------------| +| riverpod_generator | ^4.0.3 | Code gen for providers (dev dep) | Always -- generates provider boilerplate from `@riverpod` annotations | +| drift_dev | ^2.32.0 | Code gen for Drift tables (dev dep) | Always -- generates typed database code and migration helpers | +| build_runner | ^2.11.1 | Runs code generators (dev dep) | Always -- orchestrates riverpod_generator and drift_dev | +| riverpod_lint | ^3.1.3 | Lint rules for Riverpod (dev dep) | Always -- catches `ref.watch` outside `build()` at analysis time | +| path_provider | ^2.1.5 | Platform-specific file paths | Used by drift_flutter for database file location | +| flutter_localizations | SDK | Material/Cupertino l10n delegates | Required for ARB-based localization | +| intl | any | Internationalization utilities | Required by gen_l10n for date/number formatting | +| shared_preferences | ^2.3.0 | Key-value persistence | Theme mode preference persistence across app restarts | + +### Alternatives Considered +| Instead of | Could Use | Tradeoff | +|------------|-----------|----------| +| go_router | Auto-route | go_router is Flutter team maintained, has first-class StatefulShellRoute support; auto_route adds more codegen | +| shared_preferences (theme) | Drift table | Overkill for a single enum value; shared_preferences is simpler for settings | +| ColorScheme.fromSeed | flex_seed_scheme | flex_seed_scheme offers multi-seed colors and more control; but fromSeed with .copyWith() is sufficient for this palette and avoids extra dependency | + +### Installation +```bash +flutter create household_keeper --org com.jlmak --platforms android +cd household_keeper +flutter pub add flutter_riverpod riverpod_annotation drift drift_flutter go_router path_provider shared_preferences +flutter pub add -d riverpod_generator drift_dev build_runner riverpod_lint +``` + +Note: `flutter_localizations` and `intl` are added manually to `pubspec.yaml` under the `flutter_localizations: sdk: flutter` pattern. + +## Architecture Patterns + +### Recommended Project Structure +``` +lib/ +├── app.dart # MaterialApp.router with theme, localization, ProviderScope +├── main.dart # Entry point, ProviderScope wrapper +├── core/ +│ ├── database/ +│ │ ├── database.dart # @DriftDatabase class, schema version, migration strategy +│ │ ├── database.g.dart # Generated Drift code +│ │ └── database.steps.dart # Generated migration steps +│ ├── router/ +│ │ └── router.dart # GoRouter config with StatefulShellRoute +│ ├── theme/ +│ │ ├── app_theme.dart # Light/dark ThemeData with ColorScheme.fromSeed +│ │ └── theme_provider.dart # Riverpod provider for ThemeMode +│ └── providers/ +│ └── database_provider.dart # Riverpod provider exposing AppDatabase +├── features/ +│ ├── home/ +│ │ └── presentation/ +│ │ └── home_screen.dart # Placeholder with empty state +│ ├── rooms/ +│ │ └── presentation/ +│ │ └── rooms_screen.dart # Placeholder with empty state +│ └── settings/ +│ └── presentation/ +│ └── settings_screen.dart # Theme switcher + About section +├── l10n/ +│ └── app_de.arb # German strings (template file) +└── shell/ + └── app_shell.dart # Scaffold with NavigationBar, receives StatefulNavigationShell +``` + +### Pattern 1: Riverpod Provider with Code Generation +**What:** Define providers using `@riverpod` annotation, let build_runner generate the boilerplate +**When to use:** All state management -- no manual provider declarations +**Example:** +```dart +// Source: https://riverpod.dev/docs/introduction/getting_started +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'theme_provider.g.dart'; + +@riverpod +class ThemeNotifier extends _$ThemeNotifier { + @override + ThemeMode build() { + // Read persisted preference, default to system + return ThemeMode.system; + } + + void setThemeMode(ThemeMode mode) { + state = mode; + // Persist to shared_preferences + } +} +``` + +### Pattern 2: StatefulShellRoute for Bottom Navigation +**What:** `go_router`'s `StatefulShellRoute.indexedStack` preserves each tab's navigation stack independently +**When to use:** Bottom navigation with independent tab histories +**Example:** +```dart +// Source: https://pub.dev/packages/go_router +final router = GoRouter( + routes: [ + StatefulShellRoute.indexedStack( + builder: (context, state, navigationShell) { + return AppShell(navigationShell: navigationShell); + }, + branches: [ + StatefulShellBranch(routes: [ + GoRoute(path: '/', builder: (context, state) => const HomeScreen()), + ]), + StatefulShellBranch(routes: [ + GoRoute(path: '/rooms', builder: (context, state) => const RoomsScreen()), + ]), + StatefulShellBranch(routes: [ + GoRoute(path: '/settings', builder: (context, state) => const SettingsScreen()), + ]), + ], + ), + ], +); +``` + +### Pattern 3: Drift Database with DAO Separation +**What:** Each logical domain gets its own DAO class annotated with `@DriftAccessor` +**When to use:** From Phase 1 onward -- even with an empty schema, establish the pattern +**Example:** +```dart +// Source: https://drift.simonbinder.eu/dart_api/daos/ +@DriftDatabase(tables: [/* tables added in Phase 2 */], daos: [/* DAOs added in Phase 2 */]) +class AppDatabase extends _$AppDatabase { + AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection()); + + @override + int get schemaVersion => 1; + + static QueryExecutor _openConnection() { + return driftDatabase( + name: 'household_keeper', + native: const DriftNativeOptions( + databaseDirectory: getApplicationSupportDirectory, + ), + ); + } +} +``` + +### Pattern 4: ColorScheme.fromSeed with Surface Overrides +**What:** Generate a harmonious M3 color scheme from a sage green seed, then override surface colors for warmth +**When to use:** Theme definition +**Example:** +```dart +// Source: https://api.flutter.dev/flutter/material/ColorScheme/ColorScheme.fromSeed.html +ColorScheme _lightScheme() { + return ColorScheme.fromSeed( + seedColor: const Color(0xFF7A9A6D), // Sage green + brightness: Brightness.light, + dynamicSchemeVariant: DynamicSchemeVariant.tonalSpot, + ).copyWith( + surface: const Color(0xFFF5F0E8), // Warm stone + surfaceContainerLowest: const Color(0xFFFAF7F2), + surfaceContainerLow: const Color(0xFFF2EDE4), + surfaceContainer: const Color(0xFFEDE7DC), + surfaceContainerHigh: const Color(0xFFE7E0D5), + surfaceContainerHighest: const Color(0xFFE0D9CE), + ); +} + +ColorScheme _darkScheme() { + return ColorScheme.fromSeed( + seedColor: const Color(0xFF7A9A6D), // Same sage green seed + brightness: Brightness.dark, + dynamicSchemeVariant: DynamicSchemeVariant.tonalSpot, + ).copyWith( + surface: const Color(0xFF2A2520), // Warm charcoal-brown (not #121212) + surfaceContainerLowest: const Color(0xFF1E1A16), + surfaceContainerLow: const Color(0xFF322D27), + surfaceContainer: const Color(0xFF3A342E), + surfaceContainerHigh: const Color(0xFF433D36), + surfaceContainerHighest: const Color(0xFF4D463F), + ); +} +``` + +### Anti-Patterns to Avoid +- **Hardcoding strings in Dart:** All user-visible text MUST come from ARB files via `AppLocalizations.of(context)!.keyName`. Even placeholder screen text. +- **Manual provider declarations:** Always use `@riverpod` annotation + code generation. Never write `StateProvider`, `StateNotifierProvider`, or `ChangeNotifierProvider` -- these are legacy in Riverpod 3. +- **Skipping make-migrations on initial schema:** Run `dart run drift_dev make-migrations` immediately after creating the database class with `schemaVersion => 1`, BEFORE making any changes. This captures the baseline schema. +- **Using Navigator.push with go_router:** Use `context.go()` and `context.push()` from go_router. Never mix Navigator API with GoRouter. +- **Sharing ProviderContainer between tests:** Each test must get its own `ProviderContainer` or `ProviderScope`. + +## Don't Hand-Roll + +| Problem | Don't Build | Use Instead | Why | +|---------|-------------|-------------|-----| +| Database migrations | Manual SQL ALTER statements | Drift `make-migrations` + `stepByStep` | Drift generates type-safe migration steps, auto-generates tests, catches schema drift at compile time | +| Provider boilerplate | Manual `StateNotifierProvider` / `Provider` declarations | `@riverpod` + `riverpod_generator` | Eliminates AutoDispose/Family variants, unified Ref, compile-time error detection | +| Color scheme harmony | Manual hex color picking for all 30+ ColorScheme roles | `ColorScheme.fromSeed()` + `.copyWith()` | Algorithm ensures contrast ratios, accessibility compliance, and tonal harmony | +| Route management | Manual Navigator.push/pop with IndexedStack | `go_router` StatefulShellRoute | Handles back button, deep links, tab state preservation, URL-based navigation | +| Localization code | Manual Map lookups | `gen_l10n` with ARB files | Type-safe access via `AppLocalizations.of(context)!.key`, compile-time key validation | +| Theme mode persistence | Manual file I/O for settings | `shared_preferences` | Handles platform-specific key-value storage, async initialization, type safety | + +**Key insight:** This phase is infrastructure-heavy. Every "hand-rolled" solution here would need to be replaced later when the real features arrive in Phases 2-4. Using the standard tooling from day 1 prevents a rewrite. + +## Common Pitfalls + +### Pitfall 1: Forgetting to Run make-migrations Before First Schema Change +**What goes wrong:** You define tables, change them, bump schemaVersion to 2, but never captured schema version 1. Drift cannot generate the from1To2 migration step. +**Why it happens:** Developer creates tables and iterates on them during initial development before thinking about migrations. +**How to avoid:** Run `dart run drift_dev make-migrations` immediately after defining the initial database class with `schemaVersion => 1` and zero tables. This captures the empty schema as version 1. Then add tables and bump to version 2. +**Warning signs:** `make-migrations` errors about missing schema files in `drift_schemas/`. + +### Pitfall 2: Riverpod Code Generation Not Running +**What goes wrong:** `.g.dart` files are missing or stale, causing compilation errors referencing `_$ClassName`. +**Why it happens:** Developer forgets to run `dart run build_runner build` or the watcher (`build_runner watch`) is not running. +**How to avoid:** Start development with `dart run build_runner watch -d` in a terminal. The `-d` flag deletes conflicting outputs. Add a note in the project README. +**Warning signs:** `Target of URI hasn't been generated` errors in the IDE. + +### Pitfall 3: riverpod_lint Not Showing in IDE +**What goes wrong:** Lint rules like `ref.watch outside build` don't appear as analysis errors in VS Code / Android Studio. +**Why it happens:** Riverpod 3 uses `analysis_server_plugin` instead of `custom_lint`. The IDE may need a restart of the Dart analysis server, or the `analysis_options.yaml` is misconfigured. +**How to avoid:** Verify by running `dart analyze` from the terminal -- if lints appear there but not in the IDE, restart the analysis server. Ensure `analysis_options.yaml` has `plugins: riverpod_lint: ^3.1.3` (not the old `analyzer: plugins: - custom_lint` format). +**Warning signs:** No Riverpod-specific warnings anywhere in the project. Run `dart analyze` to verify. + +### Pitfall 4: ColorScheme.fromSeed Ignoring Surface Overrides +**What goes wrong:** You pass `surface:` to `ColorScheme.fromSeed()` constructor but the surface color doesn't change. +**Why it happens:** Known Flutter issue -- some color role parameters in `fromSeed()` constructor may be ignored by the algorithm. +**How to avoid:** Always use `.copyWith()` AFTER `ColorScheme.fromSeed()` to override surface colors. This reliably works. +**Warning signs:** Surfaces appear as cool gray instead of warm stone/beige. + +### Pitfall 5: ARB Template File Must Be the Source Language +**What goes wrong:** Code generation fails or produces incorrect AppLocalizations when template file doesn't match supported locale. +**Why it happens:** The `template-arb-file` in `l10n.yaml` must correspond to a supported locale. +**How to avoid:** Use `app_de.arb` as the template file since German is the only (and source) language. Set `template-arb-file: app_de.arb` in `l10n.yaml`. +**Warning signs:** `gen-l10n` errors about missing template or unsupported locale. + +### Pitfall 6: ThemeMode.system as Default Without Persistence +**What goes wrong:** User selects "Hell" (light) in settings, restarts app, and it reverts to system theme. +**Why it happens:** ThemeMode is stored only in Riverpod state (memory), not persisted to disk. +**How to avoid:** Use `shared_preferences` to persist the ThemeMode enum value. On app start, read persisted value; default to `ThemeMode.system` if no value stored. +**Warning signs:** Theme selection doesn't survive app restart. + +## Code Examples + +Verified patterns from official sources: + +### l10n.yaml Configuration (German-only) +```yaml +# Source: https://docs.flutter.dev/ui/internationalization +arb-dir: lib/l10n +template-arb-file: app_de.arb +output-localization-file: app_localizations.dart +nullable-getter: false +``` + +### pubspec.yaml Localization Dependencies +```yaml +# Source: https://docs.flutter.dev/ui/internationalization +dependencies: + flutter: + sdk: flutter + flutter_localizations: + sdk: flutter + intl: any + +flutter: + generate: true +``` + +### MaterialApp.router Setup +```dart +// Source: https://docs.flutter.dev/ui/internationalization + https://riverpod.dev +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +class App extends ConsumerWidget { + const App({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final themeMode = ref.watch(themeNotifierProvider); + + return MaterialApp.router( + routerConfig: router, + theme: ThemeData(colorScheme: lightColorScheme, useMaterial3: true), + darkTheme: ThemeData(colorScheme: darkColorScheme, useMaterial3: true), + themeMode: themeMode, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: const [Locale('de')], + locale: const Locale('de'), + ); + } +} +``` + +### SegmentedButton Theme Switcher (Recommended for Claude's Discretion) +```dart +// Source: https://api.flutter.dev/flutter/material/SegmentedButton-class.html +// Recommendation: SegmentedButton is the best M3 pattern for 3-option single-select +SegmentedButton( + segments: [ + ButtonSegment( + value: ThemeMode.system, + label: Text(l10n.themeSystem), // "System" + icon: const Icon(Icons.settings_suggest_outlined), + ), + ButtonSegment( + value: ThemeMode.light, + label: Text(l10n.themeLight), // "Hell" + icon: const Icon(Icons.light_mode_outlined), + ), + ButtonSegment( + value: ThemeMode.dark, + label: Text(l10n.themeDark), // "Dunkel" + icon: const Icon(Icons.dark_mode_outlined), + ), + ], + selected: {currentThemeMode}, + onSelectionChanged: (selection) { + ref.read(themeNotifierProvider.notifier).setThemeMode(selection.first); + }, +) +``` + +### Drift build.yaml Configuration +```yaml +# Source: https://drift.simonbinder.eu/migrations/ +targets: + $default: + builders: + drift_dev: + options: + databases: + household_keeper: lib/core/database/database.dart + sql: + dialect: sqlite + options: + version: "3.38" +``` + +### analysis_options.yaml with riverpod_lint +```yaml +# Source: https://riverpod.dev/docs/introduction/getting_started +include: package:flutter_lints/flutter.yaml + +plugins: + riverpod_lint: ^3.1.3 +``` + +### AppShell with NavigationBar +```dart +// Source: https://codewithandrea.com/articles/flutter-bottom-navigation-bar-nested-routes-gorouter/ +class AppShell extends StatelessWidget { + final StatefulNavigationShell navigationShell; + + const AppShell({super.key, required this.navigationShell}); + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); + + return Scaffold( + body: navigationShell, + bottomNavigationBar: NavigationBar( + selectedIndex: navigationShell.currentIndex, + onDestinationSelected: (index) { + navigationShell.goBranch( + index, + initialLocation: index == navigationShell.currentIndex, + ); + }, + destinations: [ + NavigationDestination( + icon: const Icon(Icons.checklist_outlined), + selectedIcon: const Icon(Icons.checklist), + label: l10n.tabHome, // "Ubersicht" + ), + NavigationDestination( + icon: const Icon(Icons.door_front_door_outlined), + selectedIcon: const Icon(Icons.door_front_door), + label: l10n.tabRooms, // "Raume" + ), + NavigationDestination( + icon: const Icon(Icons.tune_outlined), + selectedIcon: const Icon(Icons.tune), + label: l10n.tabSettings, // "Einstellungen" + ), + ], + ), + ); + } +} +``` + +## State of the Art + +| Old Approach | Current Approach | When Changed | Impact | +|--------------|------------------|--------------|--------| +| `StateNotifierProvider` / `ChangeNotifierProvider` | `@riverpod` annotation + code generation | Riverpod 3.0 (2025) | Legacy APIs still work but are deprecated; all new code should use code generation | +| `custom_lint` for riverpod_lint | `analysis_server_plugin` | Riverpod 3.0 (2025) | Setup via `plugins:` in analysis_options.yaml instead of `analyzer: plugins:` | +| `Ref` with generic parameter | Unified `Ref` (no generic) | Riverpod 3.0 (2025) | Simplified API -- one Ref type for all providers | +| `useMaterial3: true` flag needed | Material 3 is default | Flutter 3.16+ (2023) | No need to explicitly enable M3 | +| `background` / `onBackground` color roles | `surface` / `onSurface` | Flutter 3.22+ (2024) | Old roles deprecated; use new surface container hierarchy | +| Manual drift schema dumps | `dart run drift_dev make-migrations` | Drift 2.x (2024) | Automated step-by-step migration file generation and test scaffolding | + +**Deprecated/outdated:** +- `StateProvider`, `StateNotifierProvider`, `ChangeNotifierProvider`: Legacy in Riverpod 3 -- use `@riverpod` annotation +- `AutoDisposeNotifier` vs `Notifier` distinction: Unified in Riverpod 3 -- just use `Notifier` +- `ColorScheme.background` / `ColorScheme.onBackground`: Deprecated -- use `surface` / `onSurface` +- `ColorScheme.surfaceVariant`: Deprecated -- use `surfaceContainerHighest` + +## Open Questions + +1. **Exact sage green hex value for seed color** + - What we know: The palette should evoke "a calm kitchen with natural materials" -- sage green primary, slate blue accents + - What's unclear: The exact hex value that produces the best M3 tonal palette when run through `fromSeed` + - Recommendation: Start with `Color(0xFF7A9A6D)` (muted sage) and iterate visually. The `dynamicSchemeVariant: DynamicSchemeVariant.tonalSpot` will automatically produce muted tones. Fine-tune surface overrides for warmth. + +2. **Riverpod 3 package compatibility with Flutter stable channel** + - What we know: Riverpod docs specify `sdk: ^3.7.0` and `flutter: ">=3.0.0"`. Some reports mention beta channel needed for latest `json_serializable` compatibility. + - What's unclear: Whether `flutter_riverpod: ^3.3.0` works cleanly on Flutter 3.41.2 stable without issues + - Recommendation: Run `flutter pub get` early in the scaffolding wave. If version resolution fails, use `flutter_riverpod: ^3.1.0` as fallback. + +3. **Drift make-migrations with an empty initial schema** + - What we know: The docs say to run `make-migrations` before making changes. In Phase 1, we may have zero tables (tables come in Phase 2). + - What's unclear: Whether `make-migrations` works with an empty database (no tables) + - Recommendation: Define the database class with `schemaVersion => 1` and at minimum one placeholder table or zero tables, then run `make-migrations`. If it fails on empty, add a placeholder `AppSettings` table with a single column. + +## Validation Architecture + +### Test Framework +| Property | Value | +|----------|-------| +| Framework | flutter_test (bundled with Flutter SDK) | +| Config file | none -- see Wave 0 | +| Quick run command | `flutter test` | +| Full suite command | `flutter test --coverage` | + +### Phase Requirements to Test Map +| Req ID | Behavior | Test Type | Automated Command | File Exists? | +|--------|----------|-----------|-------------------|-------------| +| FOUND-01 | Drift database opens with schemaVersion 1, make-migrations runs | unit + smoke | `flutter test test/core/database/database_test.dart -x` | No -- Wave 0 | +| FOUND-02 | Riverpod providers generate correctly, riverpod_lint active | smoke | `dart analyze` | No -- Wave 0 | +| FOUND-03 | AppLocalizations.of(context) returns German strings, no hardcoded text | widget | `flutter test test/l10n/localization_test.dart -x` | No -- Wave 0 | +| FOUND-04 | Bottom nav shows 3 tabs, tapping switches content | widget | `flutter test test/shell/app_shell_test.dart -x` | No -- Wave 0 | +| THEME-01 | Light/dark themes switch correctly, system default works | widget | `flutter test test/core/theme/theme_test.dart -x` | No -- Wave 0 | +| THEME-02 | Color scheme uses sage green seed, surfaces are warm-toned | unit | `flutter test test/core/theme/color_scheme_test.dart -x` | No -- Wave 0 | + +### Sampling Rate +- **Per task commit:** `flutter test` +- **Per wave merge:** `flutter test --coverage` +- **Phase gate:** Full suite green + `dart analyze` clean (zero riverpod_lint issues) + +### Wave 0 Gaps +- [ ] `test/core/database/database_test.dart` -- covers FOUND-01 (database opens, schema version correct) +- [ ] `test/l10n/localization_test.dart` -- covers FOUND-03 (German strings load from ARB) +- [ ] `test/shell/app_shell_test.dart` -- covers FOUND-04 (navigation bar renders 3 tabs) +- [ ] `test/core/theme/theme_test.dart` -- covers THEME-01 (light/dark/system switching) +- [ ] `test/core/theme/color_scheme_test.dart` -- covers THEME-02 (sage green seed, warm surfaces) +- [ ] Drift migration test scaffolding via `dart run drift_dev make-migrations` (auto-generated) + +## Sources + +### Primary (HIGH confidence) +- [Riverpod official docs](https://riverpod.dev/docs/introduction/getting_started) - getting started, package versions, code generation setup, lint setup +- [Drift official docs](https://drift.simonbinder.eu/setup/) - setup, migration workflow, make-migrations, DAOs +- [Flutter official docs](https://docs.flutter.dev/ui/internationalization) - ARB localization setup, l10n.yaml +- [Flutter API reference](https://api.flutter.dev/flutter/material/ColorScheme/ColorScheme.fromSeed.html) - ColorScheme.fromSeed, DynamicSchemeVariant, SegmentedButton +- [pub.dev/packages/drift](https://pub.dev/packages/drift) - version 2.32.0 verified +- [pub.dev/packages/flutter_riverpod](https://pub.dev/packages/flutter_riverpod) - version 3.3.1 verified +- [pub.dev/packages/go_router](https://pub.dev/packages/go_router) - version 17.1.0 verified +- [pub.dev/packages/riverpod_lint](https://pub.dev/packages/riverpod_lint) - version 3.1.3, 14 lint rules documented + +### Secondary (MEDIUM confidence) +- [Flutter 3.41 blog post](https://blog.flutter.dev/whats-new-in-flutter-3-41-302ec140e632) - Flutter 3.41.2 current stable confirmed +- [CodeWithAndrea go_router tutorial](https://codewithandrea.com/articles/flutter-bottom-navigation-bar-nested-routes-gorouter/) - StatefulShellRoute.indexedStack pattern +- [Drift migrations docs](https://drift.simonbinder.eu/migrations/) - make-migrations workflow, stepByStep pattern +- [DynamicSchemeVariant API](https://api.flutter.dev/flutter/material/DynamicSchemeVariant.html) - tonalSpot vs fidelity behavior + +### Tertiary (LOW confidence) +- Exact surface color hex values for warm stone/charcoal -- these are illustrative starting points, need visual iteration +- Riverpod 3.3 compatibility with Flutter stable -- most reports confirm it works, but a few mention beta channel needed + +## Metadata + +**Confidence breakdown:** +- Standard stack: HIGH - all packages verified on pub.dev with current versions, official docs consulted +- Architecture: HIGH - patterns drawn from official go_router and Riverpod documentation, well-established in community +- Pitfalls: HIGH - documented issues verified across multiple sources (GitHub issues, official docs, community reports) +- Color palette: MEDIUM - hex values are starting points; M3 algorithm behavior verified but exact visual output requires iteration +- riverpod_lint setup: MEDIUM - new analysis_server_plugin approach is documented but IDE integration may have quirks + +**Research date:** 2026-03-15 +**Valid until:** 2026-04-15 (30 days -- stable ecosystem, no major releases expected before Flutter 3.44 in May)