import 'dart:async'; import 'package:flutter/foundation.dart' show defaultTargetPlatform, TargetPlatform; import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:forui/forui.dart'; import 'features/update/update_dialog.dart'; import 'providers/appearance_provider.dart'; import 'providers/di_providers.dart'; import 'providers/server_sync_provider.dart'; import 'providers/sm_account_provider.dart'; import 'providers/update_provider.dart'; import 'router/app_router.dart' show appRouterProvider, rootNavigatorKey; import 'shared/theme/app_scroll_behavior.dart'; import 'shared/theme/app_theme.dart'; import 'shared/utils/app_logger.dart'; class SmPlayerApp extends ConsumerStatefulWidget { const SmPlayerApp({super.key}); @override ConsumerState createState() => _SmPlayerAppState(); } class _SmPlayerAppState extends ConsumerState { bool _updateDialogShown = false; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) async { try { final queue = await ref.read(traktSyncQueueProvider.future); await queue.drain(); } catch (e) { AppLogger.debug('Trakt', 'startup drain skipped', e); } unawaited(ref.read(updateProvider.notifier).silentCheck()); unawaited(_autoSyncPull()); }); } Future _autoSyncPull() async { try { final account = await ref.read(smAccountProvider.future); if (!account.hasVip) return; final outcome = await ref.read(serverSyncProvider.notifier).pull(); if (outcome.isError) { AppLogger.debug('CloudSync', 'auto-pull failed: ${outcome.message}'); } } catch (e) { AppLogger.debug('CloudSync', 'auto-pull skipped', e); } } @override Widget build(BuildContext context) { final router = ref.watch(appRouterProvider); final appearance = ref.watch(appearanceProvider); final flutterMode = appearance.maybeWhen( data: (d) => d.themeMode, orElse: () => ThemeMode.system, ); return MaterialApp.router( title: 'smPlayer', debugShowCheckedModeBanner: false, locale: const Locale('zh', 'CN'), supportedLocales: const [Locale('zh', 'CN'), Locale('en')], localizationsDelegates: const [ GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], theme: AppTheme.light(), darkTheme: AppTheme.dark(), themeMode: flutterMode, scrollBehavior: const AppScrollBehavior(), routerConfig: router, builder: (context, child) { final zinc = Theme.of(context).brightness == Brightness.dark ? FThemes.zinc.dark : FThemes.zinc.light; return FTheme( data: _isTouchPlatform ? zinc.touch : zinc.desktop, child: FToaster( child: Consumer( builder: (context, ref, _) { ref.listen(updateProvider, (previous, next) { if (_updateDialogShown || next.status != UpdateStatus.available || !next.silent) { return; } _updateDialogShown = true; WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; final navContext = rootNavigatorKey.currentContext; if (navContext == null) return; showFDialog( context: navContext, builder: (_, _, _) => const UpdateDialog(), ); }); }); return child ?? const SizedBox(); }, child: child, ), ), ); }, ); } } bool get _isTouchPlatform => switch (defaultTargetPlatform) { TargetPlatform.android || TargetPlatform.iOS || TargetPlatform.fuchsia => true, _ => false, };