Files
observer/flutter_app/lib/app.dart
T
admin 6d7ba3b6ab refactor(app): 模型不再打包进 APK,移除内置资产回退逻辑(版本 1.0.5+6)
- 移除 pubspec 的 model.tflite/labels.txt 资产与 DetectorWorker 内置回退
- 无下载模型时仅预览;modelsLabel 改为未下载
- 未打包 release APK(77.4MB,不含模型)
2026-08-31 15:57:58 +08:00

111 lines
3.8 KiB
Dart

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 版本号不递增时,点过「立即更新」的 accepted 版本参与比较,
// 更新完成后再次启动不反复提示。
final info = await UpdateChecker().fetch();
final current = await PackageInfo.fromPlatform();
if (!mounted) return;
final session = context.read<SessionStore>();
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;
// 模型热更新:后台拉取模型目录并下载/更新各数据集模型(不阻塞启动,
// 相机页打开前若未就绪会兜底等待;未下载模型时仅预览)
ModelManager.instance.refresh();
Navigator.of(context)
.pushReplacementNamed(token == null ? '/login' : '/home');
}
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
}