refactor(legacy): migrate theming to Material 3 + SfColors extension

Replace the ThemePort/ThemeCode abstraction (GetIt-registered adapter)
with a Riverpod-driven Material 3 ColorScheme, an SfColors ThemeExtension
for brand tokens, and a user-facing appearance selector for light/dark/
system modes. Persisted via SharedPreferences, reacts to system
brightness changes. Payments mode keeps the existing ThemePort API.

Highlights
- New legacy_theme package: LegacyAppTheme (light/dark), LegacyColorSchemes,
  SfColors ThemeExtension, LegacyThemePreferences, LegacyThemeNotifier,
  LegacyThemeSelector. Timeframe-based variants scaffolded but disabled.
- New /legacy/dashboard/control_panel/settings/appearance route + screen.
- MaterialApp.router picks the legacy theme only when isLegacyMode.
- ~90 ThemeCode.* usages migrated to colorScheme.* / context.sfColors.*.
- 25 widgets dropped the 'ThemePort theme' constructor param.
- ~145 hardcoded colors migrated (exact hex 1:1, grey.shade tiers,
  destructive red -> colorScheme.error, background whites -> surface).
  Content-over-color whites, transparents, and brand semantic reds/
  oranges/greens intentionally preserved.
- sf_localizations updated with appearance / appearanceDescription keys
  in all six locales.
This commit is contained in:
2026-04-19 04:47:22 +02:00
parent 2eddb99c47
commit aa3ffdb6a7
175 changed files with 1722 additions and 1015 deletions

View File

@@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:design_system/design_system.dart';
import 'package:legacy_theme/legacy_theme.dart';
import 'package:sca_treezor/sca_treezor.dart';
import 'package:sf_app_platform/config/env/environment_enum.dart';
import 'package:sf_app_platform/config/env/save_family_env_config.dart';
@@ -16,12 +17,15 @@ import 'package:sf_app_platform/save_family_app.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'package:sf_tracking/sf_tracking.dart';
import 'package:shared_preferences/shared_preferences.dart';
Future<void> initApp(EnvironmentEnum env) async {
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
await initializeDateFormatting();
final sharedPreferences = await SharedPreferences.getInstance();
navigationModule();
scaTreezorModule();
themePackages();
@@ -52,5 +56,12 @@ Future<void> initApp(EnvironmentEnum env) async {
},
);
runApp(const ProviderScope(child: SaveFamilyApp()));
runApp(
ProviderScope(
overrides: [
sharedPreferencesProvider.overrideWithValue(sharedPreferences),
],
child: const SaveFamilyApp(),
),
);
}

View File

@@ -250,6 +250,11 @@ void configureAppRouter() {
name: 'settings',
pageBuilder: SettingsBuilder().buildPage,
routes: [
GoRoute(
path: 'appearance',
name: 'appearance',
pageBuilder: AppearanceBuilder().buildPage,
),
GoRoute(
path: 'alarm',
name: 'alarm',

View File

@@ -3,6 +3,7 @@ import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:legacy_theme/legacy_theme.dart';
import 'package:sf_app_platform/core/app_version_check/app_update_gate.dart';
import 'package:sf_app_platform/core/app_version_check/app_version_check.dart';
import 'package:sf_app_platform/core/config/app_mode.dart';
@@ -130,13 +131,32 @@ class SaveFamilyAppState extends ConsumerState<SaveFamilyApp>
SizeUtils.init(context: context);
ref.watch(pushTokenRefreshListenerProvider);
// Theme wiring:
// - Legacy mode: new `legacy_theme` package (Material 3 + light/dark/system).
// - Payment mode: unchanged behaviour (seed-based ColorScheme, light only).
final ThemeData lightTheme;
final ThemeData? darkTheme;
final ThemeMode themeMode;
if (isLegacyMode) {
final legacyThemeState = ref.watch(legacyThemeNotifierProvider);
lightTheme = LegacyAppTheme.light;
darkTheme = LegacyAppTheme.dark;
themeMode = legacyThemeState.themeMode;
} else {
lightTheme = ThemeData(
fontFamily: AppFonts.stolzl,
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF329E95)),
);
darkTheme = null;
themeMode = ThemeMode.light;
}
return AppUpdateGate(
child: MaterialApp.router(
title: 'SaveFamily',
theme: ThemeData(
fontFamily: AppFonts.stolzl,
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF329E95)),
),
theme: lightTheme,
darkTheme: darkTheme,
themeMode: themeMode,
routerConfig: appRouter,
debugShowCheckedModeBanner: false,
localizationsDelegates: [