flutter_integrated_whb/lib/main.dart

213 lines
6.5 KiB
Dart
Raw Normal View History

2025-08-27 16:14:50 +08:00
// main.dart
import 'dart:async';
2025-07-11 11:03:21 +08:00
import 'package:flutter/material.dart';
2025-08-07 17:33:16 +08:00
import 'package:qhd_prevention/pages/badge_manager.dart';
import 'package:qhd_prevention/services/auth_service.dart';
import 'package:qhd_prevention/tools/tools.dart';
2025-07-11 11:03:21 +08:00
import 'package:shared_preferences/shared_preferences.dart';
2025-07-18 17:13:38 +08:00
import './pages/login_page.dart';
import './pages/main_tab.dart';
2025-07-11 11:03:21 +08:00
import 'package:intl/date_symbol_data_local.dart';
2025-07-18 17:13:38 +08:00
import 'http/HttpManager.dart';
2025-08-08 10:52:15 +08:00
import 'package:flutter_easyloading/flutter_easyloading.dart';
2025-08-27 16:14:50 +08:00
import 'package:flutter/services.dart'; // for TextInput.hide
2025-07-18 17:13:38 +08:00
// 全局导航键
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
2025-08-29 09:52:48 +08:00
// 全局路由
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
2025-07-18 17:13:38 +08:00
// 全局消息控制器
class GlobalMessage {
static void showError(String message) {
final context = navigatorKey.currentContext;
if (context != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.red,
duration: const Duration(seconds: 3),
),
);
}
}
}
2025-07-11 11:03:21 +08:00
2025-08-27 16:14:50 +08:00
/// 全局 helper在弹窗前取消焦点并尽量隐藏键盘避免弹窗后键盘自动弹起
Future<T?> showDialogAfterUnfocus<T>(BuildContext context, Widget dialog) async {
// 取消焦点并尝试隐藏键盘
FocusScope.of(context).unfocus();
try {
await SystemChannels.textInput.invokeMethod('TextInput.hide');
} catch (_) {}
// 给系统一点时间避免竞态100-200ms 足够)
await Future.delayed(const Duration(milliseconds: 150));
return showDialog<T>(context: context, builder: (_) => dialog);
}
/// 同理:在展示底部模态前确保键盘隐藏
Future<T?> showModalBottomSheetAfterUnfocus<T>({
required BuildContext context,
required WidgetBuilder builder,
bool isScrollControlled = false,
}) async {
FocusScope.of(context).unfocus();
try {
await SystemChannels.textInput.invokeMethod('TextInput.hide');
} catch (_) {}
await Future.delayed(const Duration(milliseconds: 150));
return showModalBottomSheet<T>(
context: context,
isScrollControlled: isScrollControlled,
builder: builder,
);
}
2025-07-11 11:03:21 +08:00
void main() async {
WidgetsFlutterBinding.ensureInitialized();
2025-08-08 10:52:15 +08:00
// 初始化 EasyLoading
EasyLoading.instance
2025-08-11 17:40:03 +08:00
..displayDuration = const Duration(seconds: 20)
2025-08-19 11:06:16 +08:00
..indicatorType = EasyLoadingIndicatorType.ring
..loadingStyle = EasyLoadingStyle.custom
..indicatorSize = 36.0
..radius = 0
..progressColor = Colors.blue
2025-08-12 10:57:07 +08:00
..backgroundColor = Colors.grey.shade300
2025-08-19 11:06:16 +08:00
..indicatorColor = Colors.blue
..textColor = Colors.black
..userInteractions = false
2025-08-08 10:52:15 +08:00
..dismissOnTap = false;
2025-07-11 11:03:21 +08:00
await initializeDateFormatting('zh_CN', null);
2025-08-19 11:06:16 +08:00
// 初始化HTTP管理器未授权回调
2025-07-18 17:13:38 +08:00
HttpManager.onUnauthorized = () async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('isLoggedIn', false);
2025-08-19 11:06:16 +08:00
await prefs.remove('token');
2025-07-18 17:13:38 +08:00
navigatorKey.currentState?.pushNamedAndRemoveUntil(
'/login',
(route) => false,
);
Future.delayed(const Duration(milliseconds: 100), () {
2025-08-29 09:52:48 +08:00
GlobalMessage.showError('您的账号已在其他设备登录,已自动下线,请使用单一设备进行学习。');
2025-07-18 17:13:38 +08:00
});
};
2025-08-19 11:06:16 +08:00
// 自动登录逻辑
final prefs = await SharedPreferences.getInstance();
final savedLogin = prefs.getBool('isLoggedIn') ?? false;
bool isLoggedIn = false;
if (savedLogin) {
// 如果本地标记已登录,进一步验证 token 是否有效
try {
isLoggedIn = await AuthService.isLoggedIn();
} catch (e) {
isLoggedIn = false;
}
}
2025-07-11 11:03:21 +08:00
runApp(MyApp(isLoggedIn: isLoggedIn));
}
2025-08-27 16:14:50 +08:00
/// MyApp恢复为 Stateless无需监听 viewInsets
2025-07-11 11:03:21 +08:00
class MyApp extends StatelessWidget {
final bool isLoggedIn;
2025-08-19 11:06:16 +08:00
const MyApp({super.key, required this.isLoggedIn});
2025-07-11 11:03:21 +08:00
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '',
2025-08-19 11:06:16 +08:00
navigatorKey: navigatorKey,
2025-08-27 16:14:50 +08:00
// 在路由变化时统一取消焦点(防止 push/pop 时焦点回到 TextField
2025-08-29 09:52:48 +08:00
navigatorObservers: [KeyboardUnfocusNavigatorObserver(),routeObserver],
2025-07-11 11:03:21 +08:00
builder: (context, child) {
2025-08-08 10:52:15 +08:00
return EasyLoading.init(
builder: (context, widget) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
2025-08-27 16:14:50 +08:00
// 全局点击空白处取消焦点(隐藏键盘)
2025-08-22 09:02:35 +08:00
FocusHelper.clearFocus(context);
2025-08-08 10:52:15 +08:00
},
child: widget,
);
2025-07-11 11:03:21 +08:00
},
2025-08-08 10:52:15 +08:00
)(context, child);
2025-07-11 11:03:21 +08:00
},
theme: ThemeData(
2025-08-29 09:52:48 +08:00
textTheme: const TextTheme(
bodyMedium: TextStyle(fontSize: 14), // 默认字体大小
),
2025-07-11 11:03:21 +08:00
dividerTheme: const DividerThemeData(
2025-07-22 13:34:34 +08:00
color: Colors.black12,
2025-08-19 11:06:16 +08:00
thickness: .5,
indent: 0,
endIndent: 0,
2025-07-11 11:03:21 +08:00
),
primarySwatch: Colors.blue,
2025-08-19 11:06:16 +08:00
scaffoldBackgroundColor: const Color(0xFFF1F1F1),
2025-07-11 11:03:21 +08:00
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
inputDecorationTheme: const InputDecorationTheme(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 8),
),
2025-07-18 17:13:38 +08:00
snackBarTheme: const SnackBarThemeData(
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8)),
),
),
2025-08-08 10:52:15 +08:00
progressIndicatorTheme: const ProgressIndicatorThemeData(
2025-08-19 11:06:16 +08:00
color: Colors.blue,
2025-07-22 13:34:34 +08:00
),
2025-07-11 11:03:21 +08:00
),
home: isLoggedIn ? const MainPage() : const LoginPage(),
debugShowCheckedModeBanner: false,
2025-07-18 17:13:38 +08:00
routes: {
'/login': (_) => const LoginPage(),
},
2025-07-11 11:03:21 +08:00
);
}
2025-08-19 11:06:16 +08:00
}
2025-08-27 16:14:50 +08:00
/// NavigatorObserver在 push/pop/remove/replace 等路由变化时统一取消焦点
class KeyboardUnfocusNavigatorObserver extends NavigatorObserver {
void _unfocus() {
try {
FocusManager.instance.primaryFocus?.unfocus();
} catch (e) {
debugPrint('NavigatorObserver unfocus error: $e');
}
}
@override
void didPush(Route route, Route? previousRoute) {
_unfocus();
super.didPush(route, previousRoute);
}
@override
void didPop(Route route, Route? previousRoute) {
_unfocus();
super.didPop(route, previousRoute);
}
@override
void didRemove(Route route, Route? previousRoute) {
_unfocus();
super.didRemove(route, previousRoute);
}
@override
void didReplace({Route? newRoute, Route? oldRoute}) {
_unfocus();
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
}
}