Files
flutterApp/lib/main.dart

172 lines
5.5 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/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!;
}
}