Files
2026-03-16 20:12:01 +01:00

30 KiB

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

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

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:

// 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:

// 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:

// 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:

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

# 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

# Source: https://docs.flutter.dev/ui/internationalization
dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: any

flutter:
  generate: true

MaterialApp.router Setup

// 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'),
    );
  }
}
// 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

# 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

# Source: https://riverpod.dev/docs/introduction/getting_started
include: package:flutter_lints/flutter.yaml

plugins:
  riverpod_lint: ^3.1.3

AppShell with NavigationBar

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

Secondary (MEDIUM confidence)

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)