import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:get/get.dart'; import 'app/bindings/initial_binding.dart'; import 'app/localization/app_language_service.dart'; import 'app/localization/app_notice.dart'; import 'app/localization/app_strings.dart'; import 'app/routes/app_pages.dart'; import 'app/routes/app_routes.dart'; import 'app/theme/app_theme.dart'; import 'core/network/auth_session_store.dart'; import 'core/storage/local_storage_service.dart'; import 'core/storage/scoring_storage_keys.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); SystemChrome.setSystemUIOverlayStyle(_systemUiStyle); await Future.wait([ SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge), LocalStorageService.initialize(), ]); await AuthSessionStore.instance.restore(); AppLanguageService.instance = AppLanguageService( LocalStorageService.instance.settings, ); final hasValidSession = AuthSessionStore.instance.isAuthenticated; final settings = LocalStorageService.instance.settings; final shouldResumeScoring = hasValidSession && settings.get(ScoringStorageKeys.apiResumeScoringSetting) == true && (settings.get(ScoringStorageKeys.apiActiveSheetSetting) as String?) ?.isNotEmpty == true; runApp( PatternScoringApp( initialRoute: shouldResumeScoring ? AppRoutes.scoring : hasValidSession ? AppRoutes.home : AppRoutes.login, ), ); } const _systemUiStyle = SystemUiOverlayStyle( statusBarColor: Colors.transparent, statusBarIconBrightness: Brightness.dark, statusBarBrightness: Brightness.light, systemStatusBarContrastEnforced: false, systemNavigationBarColor: Colors.transparent, systemNavigationBarIconBrightness: Brightness.dark, systemNavigationBarDividerColor: Colors.transparent, systemNavigationBarContrastEnforced: false, ); class PatternScoringApp extends StatelessWidget { const PatternScoringApp({super.key, required this.initialRoute}); final String initialRoute; @override Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: AppLanguageService.instance.locale, builder: (context, locale, _) => GetMaterialApp( locale: locale, supportedLocales: const [Locale('zh'), Locale('en')], localizationsDelegates: const [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], title: AppStrings.translate('花样跳绳打分系统', locale: locale), debugShowCheckedModeBanner: false, theme: AppTheme.light, initialBinding: InitialBinding(), initialRoute: initialRoute, getPages: AppPages.pages, defaultTransition: Transition.rightToLeft, transitionDuration: const Duration(milliseconds: 360), opaqueRoute: true, builder: (context, child) => AnnotatedRegion( value: _systemUiStyle, child: AppNoticeHost( child: _StartupPageReveal( child: MediaQuery( data: MediaQuery.of( context, ).copyWith(textScaler: TextScaler.noScaling), child: child ?? const SizedBox.shrink(), ), ), ), ), ), ); } } class _StartupPageReveal extends StatefulWidget { const _StartupPageReveal({required this.child}); final Widget child; @override State<_StartupPageReveal> createState() => _StartupPageRevealState(); } class _StartupPageRevealState extends State<_StartupPageReveal> with SingleTickerProviderStateMixin { late final AnimationController _controller; late final Animation _opacity; late final Animation _scale; late final Animation _position; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 520), ); final curved = CurvedAnimation( parent: _controller, curve: Curves.easeOutCubic, ); _opacity = Tween(begin: 0, end: 1).animate(curved); _scale = Tween(begin: .992, end: 1).animate(curved); _position = Tween( begin: const Offset(0, .012), end: Offset.zero, ).animate(curved); _controller.forward(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return ColoredBox( color: const Color(0xFFF4F8FE), child: FadeTransition( opacity: _opacity, child: ScaleTransition( scale: _scale, child: SlideTransition(position: _position, child: widget.child), ), ), ); } }