import 'package:flutter/material.dart'; import 'core/app_state.dart'; import 'core/app_theme.dart'; import 'core/courses/course_repository.dart'; import 'core/sync/sync_coordinator.dart'; import 'core/sherpa_stt_service.dart'; import 'features/onboarding/onboarding_pages.dart'; import 'features/shell/learning_shell.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); // A0 is the offline baseline; the later levels load with the app state. await CourseRepository.instance.load(levels: {'A0'}); runApp(const KouyuEnglishApp()); } class KouyuEnglishApp extends StatefulWidget { const KouyuEnglishApp({super.key}); @override State createState() => _KouyuEnglishAppState(); } class _KouyuEnglishAppState extends State { final appState = AppState(); var onboardingStep = 0; @override void initState() { super.initState(); // 异步静默预热打包在本地的 SenseVoice 离线语音模型,加速首次语音交互 SherpaSttService.instance.initialize().then((ok) { debugPrint('[Main] 离线 SenseVoice 语音引擎预热状态: $ok'); }); SyncCoordinator.instance.init().then((_) { if (mounted && appState.isLoaded) { SyncCoordinator.instance.triggerBackgroundSync(appState); } }); appState.load().then((_) { if (mounted && SyncCoordinator.instance.isInitialized) { SyncCoordinator.instance.triggerBackgroundSync(appState); } }); } @override void dispose() { appState.dispose(); super.dispose(); } static ThemeMode _themeMode(AppThemeMode mode) => switch (mode) { AppThemeMode.system => ThemeMode.system, AppThemeMode.light => ThemeMode.light, AppThemeMode.dark => ThemeMode.dark, }; @override Widget build(BuildContext context) { return AnimatedBuilder( animation: appState, builder: (context, _) => MaterialApp( title: '芽说英语 · SpeakSprout', debugShowCheckedModeBanner: false, theme: buildAppTheme(Brightness.light), darkTheme: buildAppTheme(Brightness.dark), themeMode: _themeMode(appState.themeMode), // Mirror the resolved brightness into the global palette that many // widgets read directly, so it tracks both the user's choice and any // system dark-mode change. builder: (context, child) { AppColors.applyBrightness(Theme.of(context).brightness); return child ?? const SizedBox.shrink(); }, home: Builder( builder: (context) { if (!appState.isLoaded) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } if (appState.onboardingComplete) { return LearningShell(state: appState); } if (onboardingStep == 0) { return WelcomePage( state: appState, onContinue: () => setState(() => onboardingStep = 1), ); } return PopScope( canPop: false, onPopInvokedWithResult: (didPop, _) { if (!didPop) { setState(() => onboardingStep = 0); } }, child: PlacementPage( state: appState, onBack: () => setState(() => onboardingStep = 0), onStart: () => appState.finishOnboarding(), ), ); }, ), ), ); } }