173 lines
5.6 KiB
Dart
173 lines
5.6 KiB
Dart
import 'dart:async';
|
||
import 'dart:io';
|
||
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||
import 'package:go_router/go_router.dart';
|
||
import 'package:maibu_satabot_v2/core/app/app_user_cubit.dart';
|
||
import 'package:maibu_satabot_v2/core/infrastructure/logging/app_bloc_observer.dart';
|
||
import 'package:maibu_satabot_v2/core/logging/i_logger_service.dart';
|
||
import 'package:maibu_satabot_v2/core/theme/AppTheme.dart';
|
||
import 'package:maibu_satabot_v2/features/auth/presentation/bloc/auth_cubit.dart';
|
||
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_cubit.dart';
|
||
|
||
import 'core/di/injection.dart';
|
||
import 'features/auth/presentation/bloc/auth_state.dart';
|
||
import 'features/auth/presentation/bloc/login_cubit.dart';
|
||
import 'features/devices/presentation/bloc/device_status_bloc.dart';
|
||
|
||
void main() async {
|
||
// 1. 使用 runZonedGuarded 捕获所有未处理的异步错误
|
||
runZonedGuarded(
|
||
() async {
|
||
WidgetsFlutterBinding.ensureInitialized();
|
||
|
||
// 2. 初始化依赖注入 (这里面应包含 ILoggerService 的注册)
|
||
await init();
|
||
|
||
// 3. 从 sl 中获取 logger 实例并初始化 Sentry
|
||
final logger = sl<ILoggerService>();
|
||
await logger.init();
|
||
|
||
// 4. 设置全局 Bloc 观察者,自动上报 Bloc 错误
|
||
Bloc.observer = AppBlocObserver(logger);
|
||
|
||
runApp(const MyApp());
|
||
},
|
||
(error, stack) {
|
||
// 5. 捕获顶级错误并上报
|
||
sl<ILoggerService>().captureException(error, stackTrace: stack);
|
||
},
|
||
);
|
||
}
|
||
|
||
class MyApp extends StatelessWidget {
|
||
const MyApp({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
// 在 runApp 之前调用 appStarted,确保 GoRouter 初始化时能获取到正确的初始状态
|
||
sl<AuthCubit>().appStarted();
|
||
final deviceStatusBloc = sl<DeviceStatusBloc>();
|
||
return MultiBlocProvider(
|
||
providers: [
|
||
// 核心修正:在这里提供 AuthCubit
|
||
BlocProvider<AuthCubit>(create: (_) => sl<AuthCubit>()),
|
||
|
||
BlocProvider<AppUserCubit>(create: (_) => sl<AppUserCubit>()),
|
||
BlocProvider<LoginCubit>(create: (_) => sl<LoginCubit>()),
|
||
BlocProvider<DevicesCubit>(
|
||
create: (_) {
|
||
// 增加一个健壮性检查
|
||
final user = sl<AppUserCubit>().state.user;
|
||
final devicesCubit = sl<DevicesCubit>();
|
||
if (user != null) {
|
||
devicesCubit.fetchAllDevices(user.username);
|
||
}
|
||
return devicesCubit;
|
||
},
|
||
),
|
||
BlocProvider<DeviceStatusBloc>.value(
|
||
value: deviceStatusBloc,
|
||
),
|
||
// 其他 Cubit...
|
||
],
|
||
child: MaterialApp.router(title: 'Maibu Satabot',
|
||
theme: AppTheme.lightTheme, routerConfig: sl<GoRouter>(),
|
||
builder: (context, child) {
|
||
return _LifecycleListener(child: child);
|
||
},),
|
||
|
||
|
||
);
|
||
}
|
||
}
|
||
// 🔥 新增:应用生命周期监听器
|
||
class _LifecycleListener extends StatefulWidget {
|
||
final Widget? child;
|
||
|
||
const _LifecycleListener({required this.child});
|
||
|
||
@override
|
||
State<_LifecycleListener> createState() => _LifecycleListenerState();
|
||
}
|
||
|
||
class _LifecycleListenerState extends State<_LifecycleListener> with WidgetsBindingObserver {
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
WidgetsBinding.instance.addObserver(this);
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
WidgetsBinding.instance.removeObserver(this);
|
||
super.dispose();
|
||
}
|
||
|
||
@override
|
||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||
super.didChangeAppLifecycleState(state);
|
||
|
||
switch (state) {
|
||
case AppLifecycleState.resumed:
|
||
// 🔥 从息屏或后台恢复到前台
|
||
debugPrint('📱 [生命周期] 应用恢复到前台 (resumed),检查 TCP 连接...');
|
||
_handleResume();
|
||
break;
|
||
|
||
case AppLifecycleState.inactive:
|
||
// 应用处于非活动状态(iOS 来电、Android 分屏等)
|
||
debugPrint('💤 [生命周期] 应用进入非活动状态 (inactive)');
|
||
break;
|
||
|
||
case AppLifecycleState.paused:
|
||
// 应用进入后台(息屏、按 Home 键等)
|
||
debugPrint('🌙 [生命周期] 应用进入后台 (paused)');
|
||
break;
|
||
|
||
case AppLifecycleState.detached:
|
||
// 应用仍然托管但不会显示给用户(例如销毁中的 Activity)
|
||
debugPrint('🚫 [生命周期] 应用已分离 (detached)');
|
||
break;
|
||
|
||
case AppLifecycleState.hidden:
|
||
// 应用不可见(Android 14+ 或未来版本)
|
||
debugPrint('👻 [生命周期] 应用已隐藏 (hidden)');
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 🔥 处理恢复到前台的逻辑
|
||
Future<void> _handleResume() async {
|
||
try {
|
||
final authCubit = context.read<AuthCubit>();
|
||
|
||
// 检查当前是否已登录
|
||
if (authCubit.state is! AuthAuthenticated) {
|
||
debugPrint('⚠️ [生命周期] 用户未登录,跳过 TCP 重连');
|
||
return;
|
||
}
|
||
|
||
// 检查 TCP 连接状态
|
||
// 注意:这里需要通过 authCubit 访问 tcp 实例
|
||
// 由于 tcp 是 private 字段,我们需要在 AuthCubit 中暴露一个方法
|
||
debugPrint('✅ [生命周期] 准备执行 TCP 重连检查...');
|
||
|
||
// 延迟一点执行,确保 UI 已经加载完成
|
||
await Future.delayed(const Duration(milliseconds: 500));
|
||
|
||
// 调用 AuthCubit 的重连方法
|
||
await authCubit.reconnectAfterResume();
|
||
|
||
debugPrint('✅ [生命周期] TCP 重连检查完成');
|
||
} catch (e, stack) {
|
||
debugPrint('❌ [生命周期] 重连过程发生错误:$e\n$stack');
|
||
}
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return widget.child!;
|
||
}
|
||
} |