import 'package:flutter/material.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; import 'auth/auth_screen.dart'; import 'auth/session_store.dart'; import 'camera/camera_screen.dart'; import 'container.dart'; import 'home/home_screen.dart'; import 'legal/terms_screen.dart'; import 'payment/paywall_screen.dart'; import 'update/update_checker.dart'; import 'update/update_screen.dart'; class ObserverApp extends StatelessWidget { final AppContainer container; const ObserverApp({super.key, required this.container}); @override Widget build(BuildContext context) { return MultiProvider( providers: [ Provider.value(value: container), Provider.value(value: container.sessionStore), ChangeNotifierProvider.value(value: container.authViewModel), ChangeNotifierProvider.value(value: container.homeViewModel), ChangeNotifierProvider.value(value: container.paywallViewModel), ], child: MaterialApp( title: '视野', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.green), ), initialRoute: '/gate', routes: { '/gate': (_) => const StartupGate(), '/terms': (_) => const TermsScreen(), '/login': (_) => const AuthScreen(), '/home': (_) => const HomeScreen(), '/paywall': (_) => const PaywallScreen(), '/camera': (_) => const CameraScreen(), }, ), ); } } /// 启动门卫:无登录 token → 登录页;已登录 → 主界面(到期状态由主界面展示) class StartupGate extends StatefulWidget { const StartupGate({super.key}); @override State createState() => _StartupGateState(); } class _StartupGateState extends State { @override void initState() { super.initState(); _check(); } Future _check() async { // 首次启动强制协议;同意后不再展示(拒绝则退出应用) final accepted = await TermsScreen.readAccepted(); if (!mounted) return; if (accepted != '1') { Navigator.of(context).pushReplacementNamed('/terms'); return; } // 版本更新检查(公开接口,无需登录态;仅 Android):服务器版本高于 // 「已装版本与已确认接受版本」中的较大者即强制更新,弹全屏阻塞页。 // APK 版本号不递增时,点过「立即更新」的 accepted 版本参与比较, // 更新完成后再次启动不反复提示。 final info = await UpdateChecker().fetch(); final current = await PackageInfo.fromPlatform(); if (!mounted) return; final session = context.read(); final acceptedUpdate = await session.readAcceptedUpdateVersion(); if (!mounted) return; if (UpdateChecker.needsUpdate(info.version, current.version, acceptedUpdate)) { Navigator.of(context).pushReplacement(MaterialPageRoute( builder: (_) => UpdateScreen( version: info.version, url: UpdateChecker.downloadUrl(), notes: info.notes, onUpdateAccepted: () => session.saveAcceptedUpdateVersion(info.version), ), )); return; } final token = await session.readToken(); if (!mounted) return; Navigator.of(context) .pushReplacementNamed(token == null ? '/login' : '/home'); } @override Widget build(BuildContext context) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } }