Files
observer/flutter_app/lib/app.dart
T
2026-09-03 17:50:23 +08:00

106 lines
3.5 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 'models/model_manager.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<StartupGate> createState() => _StartupGateState();
}
class _StartupGateState extends State<StartupGate> {
@override
void initState() {
super.initState();
_check();
}
Future<void> _check() async {
// 首次启动强制协议;同意后不再展示(拒绝则退出应用)
final accepted = await TermsScreen.readAccepted();
if (!mounted) return;
if (accepted != '1') {
Navigator.of(context).pushReplacementNamed('/terms');
return;
}
// 版本更新检查(公开接口,无需登录态;仅 Android):服务器版本高于已装
// 版本即强制更新,弹全屏阻塞页 → 浏览器下载 APK 手动安装
// 2026-09-03App 内安装在小米等 ROM 失败,改浏览器下载)
final info = await UpdateChecker().fetch();
final current = await PackageInfo.fromPlatform();
if (!mounted) return;
final session = context.read<SessionStore>();
if (UpdateChecker.needsUpdate(info.version, current.version)) {
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (_) => UpdateScreen(
version: info.version,
url: UpdateChecker.downloadUrl(),
notes: info.notes,
),
));
return;
}
final token = await session.readToken();
if (!mounted) return;
// 模型热更新:后台拉取模型目录并下载/更新各数据集模型(不阻塞启动,
// 相机页打开前若未就绪会兜底等待;未下载模型时仅预览)
ModelManager.instance.refresh();
Navigator.of(context)
.pushReplacementNamed(token == null ? '/login' : '/home');
}
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
}