Files
jiu/client/lib/main.dart
T
wangjia b740cc6edf feat(client): iOS 上架合规——首启隐私同意弹窗 + 注销账号入口
个保法/审核 5.1.1:移动端首启全屏隐私同意(链接隐私政策/用户协议,
同意持久化;不同意 Android 退出、iOS 留提示);我的页 iOS 形态新增
「注销账号」(sheet 说明 + 预填 mailto 至 support@51yanmei.com)。
关于页隐私政策/服务条款链接已有,桌面/Web 零改动。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 08:56:26 +08:00

134 lines
4.4 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 '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/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(); // 从配置文件加载「关于我们」品牌信息
runApp(const ProviderScope(child: 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,
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'),
);
}
}