QinGang_interested/lib/main.dart

337 lines
11 KiB
Dart
Raw Normal View History

2025-12-12 09:11:30 +08:00
// main.dart
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:qhd_prevention/customWidget/custom_alert_dialog.dart';
import 'package:qhd_prevention/customWidget/toast_util.dart';
import 'package:qhd_prevention/pages/main_tab.dart';
import 'package:qhd_prevention/pages/user/login_page.dart';
import 'package:qhd_prevention/services/StorageService.dart';
import 'package:qhd_prevention/services/auth_service.dart';
import 'package:qhd_prevention/tools/tools.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'http/HttpManager.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter/services.dart'; // for TextInput.hide
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';
import 'package:qhd_prevention/common/route_observer.dart';
import 'pages/mine/mine_set_pwd_page.dart';
// 全局导航键
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
bool _isLoggingOut = false;
// 全局消息控制器
class GlobalMessage {
static void showError(String message) {
final context = navigatorKey.currentContext;
if (context != null) {
ToastUtil.showError(context, message);
}
}
}
/// 全局 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,
);
}
void main( ) async {
WidgetsFlutterBinding.ensureInitialized();
StorageService.instance.init();
// 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(); // 可选,插件示例中有
}
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
// 初始化 EasyLoading
EasyLoading.instance
..displayDuration = const Duration(seconds: 20)
..indicatorType = EasyLoadingIndicatorType.ring
..loadingStyle = EasyLoadingStyle.custom
..indicatorSize = 36.0
..radius = 0
..progressColor = Colors.blue
..backgroundColor = Colors.grey.shade300
..indicatorColor = Colors.blue
..textColor = Colors.black
..userInteractions = false
..dismissOnTap = false;
await initializeDateFormatting('zh_CN', null);
// 初始化HTTP管理器未授权回调
// 全局 flag确保不会重复弹窗
bool _isLoggingOut = false;
bool _isDialogShowing = false;
HttpManager.onUnauthorized = () async {
return;
final navState = navigatorKey.currentState;
if (navState == null) return;
// 如果已经在登录页就不弹了
final currentRouteName = ModalRoute.of(navState.context)?.settings.name;
if (currentRouteName == '/login') return;
if (_isLoggingOut) return;
_isLoggingOut = true;
try {
// 清理登录信息(提前做或在用户确认后做,根据你业务决定)
final prefs = await SharedPreferences.getInstance();
await prefs.setBool('isLoggedIn', false);
await prefs.remove('token');
// 获取最安全的 contextoverlay context 优先),保证能在任意 route 上显示 dialog
final dialogContext = navState.overlay?.context ?? navState.context;
if (!_isDialogShowing) {
_isDialogShowing = true;
// 跳回登录页
navState.pushNamedAndRemoveUntil('/login', (route) => false);
// 弹出不可通过点击外部关闭的对话框
await showDialog(
context: dialogContext,
barrierDismissible: false, //
builder: (context) {
return PopScope(
canPop: false,
// 禁止返回键关闭Android
child: AlertDialog(
title: const Text('提示'),
content: const Text('您的账号已在其他设备登录,已自动下线,请使用单一设备进行学习。'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(); // 关闭 dialog
},
child: const Text('确定', style: TextStyle(color: Colors.blue),),
),
],
),
);
},
);
_isDialogShowing = false;
}
} finally {
_isLoggingOut = false;
_isDialogShowing = false;
}
};
// 自动登录逻辑
final prefs = await SharedPreferences.getInstance();
// final savedLogin = prefs.getBool('isLoggedIn') ?? false;
final savedLogin = false;
// bool isLoggedIn = false;
Map<String, dynamic> data={};
if (savedLogin) {
// 如果本地标记已登录,进一步验证 token 是否有效
try {
// isLoggedIn = await AuthService.isLoggedIn();
data =await AuthService.isLoggedIn();
// if (data.isEmpty||data['result']!= 'success') {
// isLoggedIn =false;
// }else{
// isLoggedIn = true;
// }
} catch (e) {
data={};
// isLoggedIn = false;
}
}
// runApp(MyApp(isLoggedIn: isLoggedIn));
runApp(MyApp(data: data));
}
/// MyApp恢复为 Stateless无需监听 viewInsets
class MyApp extends StatelessWidget {
// final bool isLoggedIn;
final Map<String, dynamic> data;
const MyApp({super.key, required this.data});
Widget getHomePage() {
/**
if (data.isNotEmpty&&data['result'] == 'success') {
if(data['WEAK_PASSWORD']=='1'){
return const MineSetPwdPage("3");
}else if(data['LONG_TERM_PASSWORD_NOT_CHANGED']=='1'){
return const MineSetPwdPage("4");
}else{
return const MainPage();
}
}else{
return const LoginPage();
}
*/
return const LoginPage();
// return MainPage(isChooseFirm: false);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '',
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
// 如果使用了其他本地化包,请添加对应的 delegate
],
supportedLocales: const [
Locale('zh', 'CN'), // 中文
Locale('en', 'US'), // 英文(备用)
],
locale: const Locale('zh', 'CN'),
// 强制使用中文
navigatorKey: navigatorKey,
// 在路由变化时统一取消焦点(防止 push/pop 时焦点回到 TextField
navigatorObservers: [KeyboardUnfocusNavigatorObserver(), routeObserver],
builder: (context, child) {
// 使用 EasyLoading.init同时在其内部把 textScaleFactor 固定为 1.0
return EasyLoading.init(
builder: (context, widget) {
// 拿到当前 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,
),
);
},
)(context, child);
},
theme: ThemeData(
textTheme: const TextTheme(
bodyMedium: TextStyle(fontSize: 13), // 默认字体大小
),
dividerTheme: const DividerThemeData(
color: Colors.black12,
thickness: .5,
indent: 0,
endIndent: 0,
),
primarySwatch: Colors.blue,
scaffoldBackgroundColor: const Color(0xFFF1F1F1),
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
inputDecorationTheme: const InputDecorationTheme(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
),
snackBarTheme: const SnackBarThemeData(
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8)),
),
),
progressIndicatorTheme: const ProgressIndicatorThemeData(
color: Colors.blue,
),
),
home:getHomePage(),
// isLoggedIn ? const MainPage() : const LoginPage(),
debugShowCheckedModeBanner: false,
routes: {'/login': (_) => const LoginPage()},
);
}
}
/// 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);
}
}