| 
									
										
										
										
											2025-08-27 16:14:50 +08:00
										 |  |  |  | // main.dart
 | 
					
						
							|  |  |  |  | import 'dart:async'; | 
					
						
							| 
									
										
										
										
											2025-09-06 17:38:07 +08:00
										 |  |  |  | import 'dart:io'; | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  | import 'package:flutter/material.dart'; | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  | import 'package:flutter_localizations/flutter_localizations.dart'; | 
					
						
							| 
									
										
										
										
											2025-08-21 16:44:24 +08:00
										 |  |  |  | import 'package:qhd_prevention/services/auth_service.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-09-06 17:38:07 +08:00
										 |  |  |  | import 'package:flutter_baidu_mapapi_base/flutter_baidu_mapapi_base.dart' | 
					
						
							|  |  |  |  |     show BMFMapSDK, BMF_COORD_TYPE; | 
					
						
							|  |  |  |  | import 'package:flutter_baidu_mapapi_map/flutter_baidu_mapapi_map.dart'; | 
					
						
							| 
									
										
										
										
											2025-07-18 17:13:38 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 20:08:10 +08:00
										 |  |  |  | import 'pages/mine/mine_set_pwd_page.dart'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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:在弹窗前取消焦点并尽量隐藏键盘,避免弹窗后键盘自动弹起
 | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  | Future<T?> showDialogAfterUnfocus<T>( | 
					
						
							| 
									
										
										
										
											2025-09-05 09:16:54 +08:00
										 |  |  |  |     BuildContext context, | 
					
						
							|  |  |  |  |     Widget dialog, | 
					
						
							|  |  |  |  |     ) async { | 
					
						
							| 
									
										
										
										
											2025-08-27 16:14:50 +08:00
										 |  |  |  |   // 取消焦点并尝试隐藏键盘
 | 
					
						
							|  |  |  |  |   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-09-09 20:08:10 +08:00
										 |  |  |  | void main( ) async { | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  |   WidgetsFlutterBinding.ensureInitialized(); | 
					
						
							| 
									
										
										
										
											2025-09-06 17:38:07 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 1) 同意 SDK 隐私(百度 SDK 要求)
 | 
					
						
							|  |  |  |  |   BMFMapSDK.setAgreePrivacy(true); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   // 2) 初始化鉴权 / 坐标类型
 | 
					
						
							|  |  |  |  |   if (Platform.isIOS) { | 
					
						
							|  |  |  |  |     // iOS 可以通过接口直接设置 AK
 | 
					
						
							|  |  |  |  |     BMFMapSDK.setApiKeyAndCoordType('g3lZyqt0KkFnZGUsjIVO7U6lTCfpjSCt', BMF_COORD_TYPE.BD09LL); | 
					
						
							|  |  |  |  |   } else if (Platform.isAndroid) { | 
					
						
							|  |  |  |  |     // Android 插件示例:Android 的 AK 通过 Manifest 配置
 | 
					
						
							|  |  |  |  |     BMFMapSDK.setApiKeyAndCoordType('43G1sKuHV6oRTrdR9VTIGPF9soej7V5a', BMF_COORD_TYPE.BD09LL); | 
					
						
							|  |  |  |  |     await BMFAndroidVersion.initAndroidVersion(); // 可选,插件示例中有
 | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |   await SystemChrome.setPreferredOrientations([ | 
					
						
							|  |  |  |  |     DeviceOrientation.portraitUp, | 
					
						
							|  |  |  |  |     DeviceOrientation.portraitDown, | 
					
						
							|  |  |  |  |   ]); | 
					
						
							| 
									
										
										
										
											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', | 
					
						
							| 
									
										
										
										
											2025-09-05 09:16:54 +08:00
										 |  |  |  |           (route) => false, | 
					
						
							| 
									
										
										
										
											2025-07-18 17:13:38 +08:00
										 |  |  |  |     ); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     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; | 
					
						
							| 
									
										
										
										
											2025-09-09 20:08:10 +08:00
										 |  |  |  |   // bool isLoggedIn = false;
 | 
					
						
							|  |  |  |  |   Map<String, dynamic> data={}; | 
					
						
							| 
									
										
										
										
											2025-08-19 11:06:16 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (savedLogin) { | 
					
						
							|  |  |  |  |     // 如果本地标记已登录,进一步验证 token 是否有效
 | 
					
						
							|  |  |  |  |     try { | 
					
						
							| 
									
										
										
										
											2025-09-09 20:08:10 +08:00
										 |  |  |  |       // isLoggedIn = await AuthService.isLoggedIn();
 | 
					
						
							|  |  |  |  |        data  =await AuthService.isLoggedIn(); | 
					
						
							|  |  |  |  |       // if (data.isEmpty||data['result']!= 'success') {
 | 
					
						
							|  |  |  |  |       //   isLoggedIn =false;
 | 
					
						
							|  |  |  |  |       // }else{
 | 
					
						
							|  |  |  |  |       //   isLoggedIn = true;
 | 
					
						
							|  |  |  |  |       // }
 | 
					
						
							| 
									
										
										
										
											2025-08-19 11:06:16 +08:00
										 |  |  |  |     } catch (e) { | 
					
						
							| 
									
										
										
										
											2025-09-09 20:08:10 +08:00
										 |  |  |  |       data={}; | 
					
						
							|  |  |  |  |       // isLoggedIn = false;
 | 
					
						
							| 
									
										
										
										
											2025-08-19 11:06:16 +08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 20:08:10 +08:00
										 |  |  |  |   // runApp(MyApp(isLoggedIn: isLoggedIn));
 | 
					
						
							|  |  |  |  |   runApp(MyApp(data: data)); | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-27 16:14:50 +08:00
										 |  |  |  | /// MyApp:恢复为 Stateless(无需监听 viewInsets)
 | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  | class MyApp extends StatelessWidget { | 
					
						
							| 
									
										
										
										
											2025-09-09 20:08:10 +08:00
										 |  |  |  |   // final bool isLoggedIn;
 | 
					
						
							|  |  |  |  |   final Map<String, dynamic> data; | 
					
						
							|  |  |  |  |   const MyApp({super.key, required this.data}); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   Widget getHomePage() { | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // isLoggedIn ? const MainPage() : const LoginPage(),
 | 
					
						
							|  |  |  |  |     if (data.isNotEmpty&&data['result'] == 'success') { | 
					
						
							|  |  |  |  |       // data['LONG_TERM_PASSWORD_NOT_CHANGED']='1';
 | 
					
						
							|  |  |  |  |       if(data['WEAK_PASSWORD']=='1'){ | 
					
						
							|  |  |  |  |         return const MineSetPwdPage("3"); | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 20:08:10 +08:00
										 |  |  |  |       }else if(data['LONG_TERM_PASSWORD_NOT_CHANGED']=='1'){ | 
					
						
							|  |  |  |  |         return const MineSetPwdPage("4"); | 
					
						
							|  |  |  |  |       }else{ | 
					
						
							|  |  |  |  |         return const MainPage(); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     }else{ | 
					
						
							|  |  |  |  |       return const LoginPage(); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   @override | 
					
						
							|  |  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |  |     return MaterialApp( | 
					
						
							|  |  |  |  |       title: '', | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |       localizationsDelegates: [ | 
					
						
							|  |  |  |  |         GlobalMaterialLocalizations.delegate, | 
					
						
							|  |  |  |  |         GlobalCupertinoLocalizations.delegate, | 
					
						
							|  |  |  |  |         GlobalWidgetsLocalizations.delegate, | 
					
						
							|  |  |  |  |         // 如果使用了其他本地化包,请添加对应的 delegate
 | 
					
						
							|  |  |  |  |       ], | 
					
						
							| 
									
										
										
										
											2025-09-05 09:16:54 +08:00
										 |  |  |  |       supportedLocales: const [ | 
					
						
							|  |  |  |  |         Locale('zh', 'CN'), // 中文
 | 
					
						
							|  |  |  |  |         Locale('en', 'US'), // 英文(备用)
 | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |       ], | 
					
						
							|  |  |  |  |       locale: const Locale('zh', 'CN'), | 
					
						
							|  |  |  |  |       // 强制使用中文
 | 
					
						
							| 
									
										
										
										
											2025-08-19 11:06:16 +08:00
										 |  |  |  |       navigatorKey: navigatorKey, | 
					
						
							| 
									
										
										
										
											2025-08-27 16:14:50 +08:00
										 |  |  |  |       // 在路由变化时统一取消焦点(防止 push/pop 时焦点回到 TextField)
 | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |       navigatorObservers: [KeyboardUnfocusNavigatorObserver(), routeObserver], | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  |       builder: (context, child) { | 
					
						
							| 
									
										
										
										
											2025-09-05 09:16:54 +08:00
										 |  |  |  |         // 使用 EasyLoading.init,同时在其内部把 textScaleFactor 固定为 1.0
 | 
					
						
							| 
									
										
										
										
											2025-08-08 10:52:15 +08:00
										 |  |  |  |         return EasyLoading.init( | 
					
						
							|  |  |  |  |           builder: (context, widget) { | 
					
						
							| 
									
										
										
										
											2025-09-05 09:16:54 +08:00
										 |  |  |  |             // 拿到当前 MediaQuery 并创建一个 textScaleFactor = 1.0 的副本
 | 
					
						
							|  |  |  |  |             final mq = MediaQuery.maybeOf(context) ?? MediaQueryData.fromWindow(WidgetsBinding.instance.window); | 
					
						
							|  |  |  |  |             final fixed = mq.copyWith(textScaleFactor: 1.0); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |             return MediaQuery( | 
					
						
							|  |  |  |  |               data: fixed, | 
					
						
							|  |  |  |  |               child: GestureDetector( | 
					
						
							|  |  |  |  |                 behavior: HitTestBehavior.translucent, | 
					
						
							|  |  |  |  |                 onTap: () { | 
					
						
							|  |  |  |  |                   // 全局点击空白处取消焦点(隐藏键盘)
 | 
					
						
							|  |  |  |  |                   try { | 
					
						
							|  |  |  |  |                     FocusManager.instance.primaryFocus?.unfocus(); | 
					
						
							|  |  |  |  |                   } catch (e) { | 
					
						
							|  |  |  |  |                     debugPrint('NavigatorObserver unfocus error: $e'); | 
					
						
							|  |  |  |  |                   } | 
					
						
							|  |  |  |  |                 }, | 
					
						
							|  |  |  |  |                 child: widget, | 
					
						
							|  |  |  |  |               ), | 
					
						
							| 
									
										
										
										
											2025-08-08 10:52:15 +08:00
										 |  |  |  |             ); | 
					
						
							| 
									
										
										
										
											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( | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +08:00
										 |  |  |  |           bodyMedium: TextStyle(fontSize: 13), // 默认字体大小
 | 
					
						
							| 
									
										
										
										
											2025-08-29 09:52:48 +08:00
										 |  |  |  |         ), | 
					
						
							| 
									
										
										
										
											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
										 |  |  |  |       ), | 
					
						
							| 
									
										
										
										
											2025-09-09 20:08:10 +08:00
										 |  |  |  |       home:getHomePage(), | 
					
						
							|  |  |  |  |         // isLoggedIn ? const MainPage() : const LoginPage(),
 | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |  |       debugShowCheckedModeBanner: false, | 
					
						
							| 
									
										
										
										
											2025-09-01 17:25:55 +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); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } |