import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'core/config/locale_controller.dart'; import 'core/data/remote/api_client.dart'; import 'core/data/remote/auth_api.dart'; import 'core/data/remote/catalog_api.dart'; import 'core/data/repositories/catalog_repository.dart'; import 'core/data/repositories/engagement_repository.dart'; import 'core/data/services/auth_controller.dart'; import 'core/data/services/auth_session_store.dart'; import 'core/data/services/catalog_controller.dart'; import 'core/data/services/engagement_controller.dart'; import 'core/data/services/playback_session.dart'; import 'core/data/services/video_player_port.dart'; import 'l10n/app_localizations.dart'; import 'pages/home/home_view.dart'; class LumaDramaApp extends StatefulWidget { const LumaDramaApp({ super.key, this.playerFactory = VideoPlayerPort.new, this.catalogRepository, }); final PlayerFactory playerFactory; /// 测试注入用;生产走 HTTP 仓库。 final CatalogRepository? catalogRepository; @override State createState() => _LumaDramaAppState(); } class _LumaDramaAppState extends State { late final AuthSessionStore _sessionStore = AuthSessionStore(); /// 全 App 共用一个 ApiClient:这样目录、账号、后续的互动/进度请求 /// 都自动带 `Authorization` 与 `X-Device-Id`,401 的刷新重试也只有一处实现。 late final ApiClient _apiClient = ApiClient( accessTokenReader: () => _auth.accessToken, tokenRefresher: () => _auth.refresh(), onUnauthorized: () => _auth.clear(), deviceIdReader: _sessionStore.deviceId, ); late final AuthController _auth = AuthController(AuthApi(_apiClient), _sessionStore); @override void initState() { super.initState(); // 恢复本地令牌与设备号;未登录也能正常浏览,不阻塞首屏 _auth.load(); } @override Widget build(BuildContext context) { return MultiProvider( providers: [ Provider(create: (_) => PlaybackCoordinator()), Provider(create: (_) => widget.playerFactory), ChangeNotifierProvider( create: (_) => CatalogController( widget.catalogRepository ?? HttpCatalogRepository(CatalogApi(_apiClient)), )..load(), ), ChangeNotifierProvider(create: (_) => LocaleController()..load()), ChangeNotifierProvider( create: (_) => EngagementController(SharedPreferencesEngagementRepository()) ..load(), ), ChangeNotifierProvider.value(value: _auth), ], child: Consumer( builder: (context, locale, _) => MaterialApp( onGenerateTitle: (context) => AppLocalizations.of(context)!.appName, locale: locale.locale, supportedLocales: AppLocalizations.supportedLocales, localizationsDelegates: AppLocalizations.localizationsDelegates, localeResolutionCallback: (deviceLocale, supported) { final resolved = locale.locale ?? deviceLocale; if (resolved == null) return supported.first; for (final item in supported) { if (item.languageCode == resolved.languageCode) return item; } return supported.first; }, debugShowCheckedModeBanner: false, themeMode: ThemeMode.system, theme: ThemeData( useMaterial3: true, colorScheme: ColorScheme.fromSeed( seedColor: const Color(0xFF2F7BFF), ), scaffoldBackgroundColor: const Color(0xFFF8F7FC), ), darkTheme: ThemeData( useMaterial3: true, colorScheme: ColorScheme.fromSeed( seedColor: const Color(0xFF2F7BFF), brightness: Brightness.dark, ), scaffoldBackgroundColor: const Color(0xFF101019), ), home: const HomePage(), ), ), ); } }