Files

147 lines
5.1 KiB
Dart
Raw Permalink 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 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_web_plugins/url_strategy.dart';
import 'core/auth/auth_state.dart';
import 'core/auth/session_epoch.dart';
import 'core/config/app_info.dart';
import 'core/errors/error_reporter.dart';
import 'core/platform/single_instance.dart' as single_instance;
import 'core/router/app_router.dart';
import 'core/theme/theme_controller.dart';
import 'core/theme/themes.dart';
import 'providers/connectivity_provider.dart';
import 'widgets/ds/ds_atoms.dart';
import 'widgets/privacy_consent_gate.dart';
void main() {
usePathUrlStrategy();
FlutterError.onError = (details) {
FlutterError.presentError(details);
debugPrint('═══ FlutterError ════════════════════════════');
debugPrint(details.exceptionAsString());
debugPrint(details.stack.toString());
ErrorReporter.instance.report(
errorType: ErrorType.flutterError,
errorMsg: details.exceptionAsString(),
stackTrace: details.stack,
);
};
runZonedGuarded(
() async {
WidgetsFlutterBinding.ensureInitialized();
if (!await single_instance.acquire()) {
runApp(const _AlreadyRunningApp());
return;
}
await AppInfo.load(); // 从配置文件加载「关于我们」品牌信息
// 会话纪元变化(登录/登出)→ 换 key 重建整个 ProviderScope
// 所有业务 provider 状态(分页/筛选/notifier 内缓存)随账号切换彻底清零,
// 杜绝跨账号内存残留。auth 会话由新 scope 的 restore() 从持久化恢复。
runApp(ValueListenableBuilder<int>(
valueListenable: sessionEpoch,
builder: (_, epoch, __) => ProviderScope(
key: ValueKey(epoch),
child: const JiuApp(),
),
));
},
(error, stack) {
debugPrint('═══ Zone Error ═══════════════════════════════');
debugPrint(error.toString());
debugPrint(stack.toString());
ErrorReporter.instance.report(
errorType: ErrorType.zoneError,
errorMsg: error.toString(),
stackTrace: stack,
);
},
);
}
class _AlreadyRunningApp extends StatelessWidget {
const _AlreadyRunningApp();
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
// 必须挂应用主题:DsButton 经 context.tokens 取 AppTokens extension
// 裸 MaterialApp 没有该 extension 会空指针崩溃(弹不出提示框直接闪退)
theme: buildTheme('a'),
home: Builder(
builder: (context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (_) => AlertDialog(
title: const Text('岩美酒库已在运行'),
content: const Text('请勿重复打开,程序已在运行中。\n请在任务栏或 Dock 中找到已打开的窗口。'),
actions: [
DsButton('确定',
variant: DsBtnVariant.primary, onPressed: () => exit(0)),
],
),
);
});
return const Scaffold();
},
),
);
}
}
class JiuApp extends ConsumerStatefulWidget {
const JiuApp({super.key});
@override
ConsumerState<JiuApp> createState() => _JiuAppState();
}
class _JiuAppState extends ConsumerState<JiuApp> {
@override
void initState() {
super.initState();
// 异步初始化:恢复 auth + 连通性检测
// 路由 redirect 在 initialized=false 时返回 null(不重定向),
// 初始化完成后 authState 变化触发 router refreshredirect 重新执行
WidgetsBinding.instance.addPostFrameCallback((_) async {
await ref.read(themeControllerProvider.notifier).restore();
await ref.read(authStateProvider.notifier).restore();
await ref.read(connectivityProvider.notifier).forceCheck();
});
}
@override
Widget build(BuildContext context) {
final router = ref.watch(appRouterProvider);
final themeKey = ref.watch(themeControllerProvider);
return MaterialApp.router(
title: kDebugMode ? '岩美 [DEBUG]' : '岩美',
theme: buildTheme(themeKey),
routerConfig: router,
// 首启隐私同意闸(移动端 only):覆盖全部路由,同意前遮罩交互
builder: (context, child) =>
PrivacyConsentGate(child: child ?? const SizedBox.shrink()),
debugShowCheckedModeBanner: false,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('zh', 'CN'),
Locale('en', 'US'),
],
locale: const Locale('zh', 'CN'),
);
}
}