Files
feature-tenant/lib/main.dart

197 lines
6.4 KiB
Dart
Raw Normal View History

2026-01-21 13:27:55 +08:00
import 'dart:async';
2026-01-19 18:24:24 +08:00
import 'dart:io';
2026-01-11 19:42:22 +08:00
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
2026-01-11 19:42:22 +08:00
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
2026-01-18 20:21:14 +08:00
import 'package:maibu_satabot_v2/core/app/app_user_cubit.dart';
2026-01-21 13:27:55 +08:00
import 'package:maibu_satabot_v2/core/infrastructure/logging/app_bloc_observer.dart';
import 'package:maibu_satabot_v2/core/logging/i_logger_service.dart';
2026-01-18 20:21:14 +08:00
import 'package:maibu_satabot_v2/core/theme/AppTheme.dart';
2026-01-11 19:42:22 +08:00
import 'package:maibu_satabot_v2/features/auth/presentation/bloc/auth_cubit.dart';
2026-01-18 20:21:14 +08:00
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_cubit.dart';
2026-01-11 19:42:22 +08:00
import 'core/di/injection.dart';
import 'core/localization/app_localizations.dart';
import 'core/localization/locale_cubit.dart';
import 'features/auth/presentation/bloc/auth_state.dart';
2026-01-11 19:42:22 +08:00
import 'features/auth/presentation/bloc/login_cubit.dart';
import 'features/devices/presentation/bloc/device_status_bloc.dart';
2026-01-11 19:42:22 +08:00
void main() async {
2026-01-21 13:27:55 +08:00
// 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);
},
);
2026-01-11 19:42:22 +08:00
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// 在 runApp 之前调用 appStarted,确保 GoRouter 初始化时能获取到正确的初始状态
2026-01-11 19:42:22 +08:00
sl<AuthCubit>().appStarted();
final deviceStatusBloc = sl<DeviceStatusBloc>();
final localeCubit = sl<LocaleCubit>();
2026-01-11 19:42:22 +08:00
return MultiBlocProvider(
providers: [
// 核心修正:在这里提供 AuthCubit
BlocProvider<AuthCubit>(create: (_) => sl<AuthCubit>()),
2026-01-18 20:21:14 +08:00
BlocProvider<AppUserCubit>(create: (_) => sl<AppUserCubit>()),
2026-01-11 19:42:22 +08:00
BlocProvider<LoginCubit>(create: (_) => sl<LoginCubit>()),
2026-01-18 20:21:14 +08:00
BlocProvider<DevicesCubit>(
create: (_) {
// 增加一个健壮性检查
final user = sl<AppUserCubit>().state.user;
final devicesCubit = sl<DevicesCubit>();
if (user != null) {
devicesCubit.fetchAllDevices(user.username);
}
return devicesCubit;
},
2026-01-18 20:21:14 +08:00
),
BlocProvider<DeviceStatusBloc>.value(
value: deviceStatusBloc,
),
// 🔥 语言管理 Cubit
BlocProvider<LocaleCubit>.value(value: localeCubit),
// 其他 Cubit...
2026-01-11 19:42:22 +08:00
],
child: BlocBuilder<LocaleCubit, Locale>(
bloc: localeCubit,
builder: (context, locale) {
return MaterialApp.router(
title: 'Maibu Satabot',
locale: locale,
supportedLocales: const [
Locale('zh', 'CN'),
Locale('en', 'US'),
],
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
theme: AppTheme.lightTheme,
routerConfig: sl<GoRouter>(),
builder: (context, child) {
return _LifecycleListener(child: child);
},
);
},
),
2026-01-11 19:42:22 +08:00
);
}
2026-01-21 13:27:55 +08:00
}
// 🔥 新增:应用生命周期监听器
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!;
}
}