# 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)