docs(01-foundation): research phase domain

This commit is contained in:
2026-03-15 19:33:29 +01:00
parent 3be468a647
commit ef7e7baf19

View File

@@ -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>
## 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
</user_constraints>
<phase_requirements>
## 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 |
</phase_requirements>
## 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<String, String> 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<ThemeMode>(
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<T>` 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)