2026-09-17 14:45:23 +08:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:flutter/services.dart';
|
2026-10-14 11:08:19 +08:00
|
|
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
2026-09-17 14:45:23 +08:00
|
|
|
import 'package:get/get.dart';
|
|
|
|
|
|
|
|
|
|
import 'app/bindings/initial_binding.dart';
|
2026-10-14 11:08:19 +08:00
|
|
|
import 'app/localization/app_language_service.dart';
|
2026-10-14 15:40:00 +08:00
|
|
|
import 'app/localization/app_notice.dart';
|
2026-10-14 11:08:19 +08:00
|
|
|
import 'app/localization/app_strings.dart';
|
2026-09-17 14:45:23 +08:00
|
|
|
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<void> main() async {
|
|
|
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
|
|
|
SystemChrome.setSystemUIOverlayStyle(_systemUiStyle);
|
|
|
|
|
await Future.wait([
|
|
|
|
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge),
|
|
|
|
|
LocalStorageService.initialize(),
|
|
|
|
|
]);
|
|
|
|
|
await AuthSessionStore.instance.restore();
|
2026-10-14 11:08:19 +08:00
|
|
|
AppLanguageService.instance = AppLanguageService(
|
|
|
|
|
LocalStorageService.instance.settings,
|
|
|
|
|
);
|
2026-09-17 14:45:23 +08:00
|
|
|
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) {
|
2026-10-14 11:08:19 +08:00
|
|
|
return ValueListenableBuilder<Locale>(
|
|
|
|
|
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<SystemUiOverlayStyle>(
|
|
|
|
|
value: _systemUiStyle,
|
2026-10-14 15:40:00 +08:00
|
|
|
child: AppNoticeHost(
|
|
|
|
|
child: _StartupPageReveal(
|
|
|
|
|
child: MediaQuery(
|
|
|
|
|
data: MediaQuery.of(
|
|
|
|
|
context,
|
|
|
|
|
).copyWith(textScaler: TextScaler.noScaling),
|
|
|
|
|
child: child ?? const SizedBox.shrink(),
|
|
|
|
|
),
|
2026-10-14 11:08:19 +08:00
|
|
|
),
|
2026-09-17 14:45:23 +08:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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<double> _opacity;
|
|
|
|
|
late final Animation<double> _scale;
|
|
|
|
|
late final Animation<Offset> _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<double>(begin: 0, end: 1).animate(curved);
|
|
|
|
|
_scale = Tween<double>(begin: .992, end: 1).animate(curved);
|
|
|
|
|
_position = Tween<Offset>(
|
|
|
|
|
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),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|