yumi-flutter/lib/main.dart
2026-06-12 14:34:12 +08:00

466 lines
15 KiB
Dart
Raw Permalink 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 'dart:ui';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:fluro/fluro.dart' as fluro;
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:yumi/shared/tools/sc_keybord_util.dart';
import 'package:yumi/shared/data_sources/sources/local/data_persistence.dart';
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'app_localizations.dart';
import 'app/config/app_config.dart';
import 'app/constants/sc_global_config.dart';
import 'app/constants/sc_screen.dart';
import 'app/routes/sc_routes.dart';
import 'app/routes/sc_lk_application.dart';
import 'shared/tools/sc_deep_link_handler.dart';
import 'shared/tools/sc_deviceId_utils.dart';
import 'shared/tools/sc_gift_vap_svga_manager.dart';
import 'shared/data_sources/sources/local/user_manager.dart';
import 'modules/splash/splash_page.dart';
import 'services/general/sc_app_general_manager.dart';
import 'services/payment/apple_payment_manager.dart';
import 'services/auth/authentication_manager.dart';
import 'services/gift/gift_animation_manager.dart';
import 'services/gift/gift_system_manager.dart';
import 'services/payment/google_payment_manager.dart';
import 'services/localization/localization_manager.dart';
import 'services/music/room_music_manager.dart';
import 'services/room/rc_room_manager.dart';
import 'services/audio/rtc_manager.dart';
import 'services/audio/rtm_manager.dart';
import 'services/shop/shop_manager.dart';
import 'services/theme/theme_manager.dart';
import 'services/auth/user_profile_manager.dart';
import 'ui_kit/theme/socialchat_theme.dart';
bool _isCrashlyticsReady = false;
void main() async {
// 初始化应用配置
AppConfig.initialize();
// 2. 使用 runZonedGuarded 创建一个隔离区域来捕获所有异步错误
runZonedGuarded(
() async {
// 1. 确保 Widget 绑定初始化
WidgetsFlutterBinding.ensureInitialized();
// 性能优化:启用手势重采样
GestureBinding.instance.resamplingEnabled = true;
// 仅保留首帧前真正必要的初始化
await _prepareAppShell();
_installErrorHandlers();
// 立即运行应用,不等待非必要服务初始化
runApp(const RootAppWithProviders());
WidgetsBinding.instance.addPostFrameCallback((_) {
unawaited(_warmUpDeferredServices());
});
},
// 9. runZonedGuarded 的错误回调(这是捕获异步错误的主要方式)
(error, stackTrace) {
unawaited(_recordFatalError(error, stackTrace));
},
);
}
/// 首帧前的必要初始化
Future<void> _prepareAppShell() async {
// 配置设备方向:仅竖屏
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
// Android平台状态栏样式设置
if (Platform.isAndroid) {
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
),
);
}
// 键盘工具初始化
SCKeybordUtil.init();
// 使用重试机制初始化存储
await _initStore();
await AccountStorage().restoreDurableSessionIfNeeded();
await SCDeviceIdUtils.initDeviceinfo();
_configureImageCache();
}
void _configureImageCache() {
final imageCache = PaintingBinding.instance.imageCache;
imageCache.maximumSizeBytes = SCGlobalConfig.recommendedImageCacheBytes;
imageCache.maximumSize = SCGlobalConfig.recommendedImageCacheEntries;
}
void _releaseVisualMemoryCaches({bool clearLiveImages = false}) {
SCGiftVapSvgaManager().clearMemoryCache();
final imageCache = PaintingBinding.instance.imageCache;
imageCache.clear();
if (clearLiveImages) {
imageCache.clearLiveImages();
}
}
void _installErrorHandlers() {
FlutterError.onError = (details) {
FlutterError.presentError(details);
unawaited(_recordFlutterError(details));
};
PlatformDispatcher.instance.onError = (error, stack) {
unawaited(_recordFatalError(error, stack));
return true;
};
}
/// 首帧后的后台预热初始化
Future<void> _warmUpDeferredServices() async {
try {
if (Firebase.apps.isEmpty) {
await Firebase.initializeApp();
}
_isCrashlyticsReady = true;
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
} catch (_) {
return;
}
}
Future<void> _recordFlutterError(FlutterErrorDetails details) async {
if (!_isCrashlyticsReady) {
return;
}
try {
await FirebaseCrashlytics.instance.recordFlutterFatalError(details);
} catch (_) {
return;
}
}
Future<void> _recordFatalError(Object error, StackTrace stackTrace) async {
if (!_isCrashlyticsReady) {
return;
}
try {
await FirebaseCrashlytics.instance.recordError(
error,
stackTrace,
fatal: true,
);
} catch (_) {
return;
}
}
/// 带重试的数据存储初始化重命名自_initializeStorageWithRetry
Future<void> _initStore() async {
const maxAttempts = 3;
int attemptCount = 0;
bool success = false;
while (attemptCount < maxAttempts && !success) {
attemptCount++;
try {
await DataPersistence.initialize();
success = true;
} catch (_) {
if (attemptCount >= maxAttempts) {
// 不抛出异常,允许应用继续运行
return;
}
// 递增延迟500ms, 1000ms, 1500ms...
int delayMs = 500 * attemptCount;
await Future.delayed(Duration(milliseconds: delayMs));
}
}
}
/// 带有所有Provider的根组件
class RootAppWithProviders extends StatelessWidget {
const RootAppWithProviders({super.key});
@override
Widget build(BuildContext context) {
return MultiProvider(providers: _buildP(), child: const YumiApplication());
}
/// 构建应用Provider列表 - 重新排序以优化性能
List<SingleChildWidget> _buildP() {
return [
// 核心用户相关Provider - 按需初始化,避免阻塞首屏
ChangeNotifierProvider<SocialChatAuthenticationManager>(
lazy: true,
create: (context) => SocialChatAuthenticationManager(),
),
ChangeNotifierProvider<SocialChatUserProfileManager>(
lazy: true,
create: (context) => SocialChatUserProfileManager(),
),
// UI与主题相关Provider - 懒加载
ChangeNotifierProvider<ThemeManager>(
lazy: true,
create: (context) => ThemeManager(),
),
ChangeNotifierProvider<LocalizationManager>(
lazy: true,
create: (context) => LocalizationManager(),
),
// 实时通信Provider - 懒加载
ChangeNotifierProvider<RtcProvider>(
lazy: true,
create: (context) => RealTimeCommunicationManager(),
),
ChangeNotifierProvider<RtmProvider>(
lazy: true,
create: (context) => RealTimeMessagingManager(),
),
ChangeNotifierProvider<RoomMusicManager>(
lazy: true,
create: (context) => RoomMusicManager(),
),
// 房间与社交功能Provider - 懒加载
ChangeNotifierProvider<SocialChatRoomManager>.value(
value: socialChatRoomManager,
),
// 礼物与动画系统Provider - 懒加载
ChangeNotifierProvider<GiftProvider>(
lazy: true,
create: (context) => SocialChatGiftSystemManager(),
),
ChangeNotifierProvider<GiftAnimationManager>(
lazy: true,
create: (context) => GiftAnimationManager(),
),
// 支付与商店Provider - 懒加载
ChangeNotifierProvider<AndroidPaymentProcessor>(
lazy: true,
create: (context) => AndroidPaymentProcessor(),
),
ChangeNotifierProvider<IOSPaymentProcessor>(
lazy: true,
create: (context) => IOSPaymentProcessor(),
),
ChangeNotifierProvider<ShopManager>(
lazy: true,
create: (context) => ShopManager(),
),
// 通用应用管理Provider - 懒加载
ChangeNotifierProvider<SCAppGeneralManager>(
lazy: true,
create: (context) => SCAppGeneralManager(),
),
];
}
}
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
final RouteObserver<ModalRoute<dynamic>> modalRouteObserver =
RouteObserver<ModalRoute<dynamic>>();
class YumiApplication extends StatefulWidget {
const YumiApplication({super.key});
@override
State<YumiApplication> createState() => _YumiApplicationState();
}
class _YumiApplicationState extends State<YumiApplication>
with WidgetsBindingObserver {
late fluro.FluroRouter _router;
final SCDeepLinkHandler _deepLinkHandler = SCDeepLinkHandler();
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
// 将非首帧必需的初始化统一延后到首帧后
WidgetsBinding.instance.addPostFrameCallback((_) {
_initRouter();
unawaited(_initLink());
unawaited(_reconcilePendingGooglePurchases(force: true));
});
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_deepLinkHandler.dispose();
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.paused ||
state == AppLifecycleState.inactive ||
state == AppLifecycleState.hidden) {
SCGiftVapSvgaManager().stopPlayback();
_releaseVisualMemoryCaches(
clearLiveImages: state == AppLifecycleState.paused,
);
}
if (state == AppLifecycleState.detached) {
unawaited(
context.read<RtcProvider>().releaseRtcEngineForAppTermination(),
);
}
if (state == AppLifecycleState.resumed) {
unawaited(_reconcilePendingGooglePurchases());
}
}
Future<void> _reconcilePendingGooglePurchases({bool force = false}) async {
if (!Platform.isAndroid) {
return;
}
try {
await context
.read<AndroidPaymentProcessor>()
.reconcilePendingGooglePurchases(context: context, force: force);
} catch (error) {
if (kDebugMode) {
debugPrint('reconcile pending google purchases failed: $error');
}
}
}
@override
void didHaveMemoryPressure() {
super.didHaveMemoryPressure();
SCGiftVapSvgaManager().stopPlayback();
_releaseVisualMemoryCaches(clearLiveImages: true);
}
Future<void> _initLink() async {
// 初始化,并传递一个回调函数用于处理链接
await _deepLinkHandler.initDeepLinks(
onLinkReceived: (Uri uri) {
// 当收到链接时,无论应用在哪个页面,都可以进行路由
_handleLink(uri);
},
);
}
void _handleLink(Uri uri) {
// 这是处理链接的核心路由逻辑
}
void _initRouter() {
_router = fluro.FluroRouter();
SCRoutes.configureRoutes(_router);
SCLkApplication.router = _router;
}
@override
Widget build(BuildContext context) {
return _buildUI();
}
// 构建下拉刷新配置的页脚部件
Widget Function() _buildFooter() {
return () => CustomFooter(
builder: (BuildContext context, LoadStatus? mode) {
Widget body;
if (mode == LoadStatus.idle) {
body = Text(SCAppLocalizations.of(context)!.pullToLoadMore);
} else if (mode == LoadStatus.loading) {
body = CupertinoActivityIndicator(color: Colors.white24);
} else if (mode == LoadStatus.failed) {
body = Text(
SCAppLocalizations.of(context)!.loadingFailedClickToRetry,
);
} else if (mode == LoadStatus.canLoading) {
body = Text(SCAppLocalizations.of(context)!.releaseToLoadMore);
} else {
body = Text(
"",
style: TextStyle(fontSize: 12.sp, color: Color(0xff999999)),
);
}
return SizedBox(
height: kTextTabBarHeight + ScreenUtil().statusBarHeight,
child: SizedBox.expand(child: Center(child: body)),
);
},
);
}
Widget _buildUI() {
return Consumer<LocalizationManager>(
builder: (context, localeProvider, child) {
// 只有在LocaleProvider可用时才设置语言
SCGlobalConfig.lang = localeProvider.locale?.languageCode ?? "en";
return Consumer<ThemeManager>(
builder: (context, themeManager, child) {
return ScreenUtilInit(
designSize: Size(SCScreen.designWidth, SCScreen.designHeight),
splitScreenMode: false,
minTextAdapt: true,
builder: (context, child) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.dark,
child: RefreshConfiguration(
headerBuilder:
() => MaterialClassicHeader(
color: SocialChatTheme.primaryColor,
),
footerBuilder: _buildFooter(),
child: MaterialApp(
title: 'Yumi',
locale: localeProvider.locale,
localizationsDelegates: const [
SCAppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: SCAppLocalizations.supportedLocales,
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
onGenerateRoute: SCLkApplication.router.generator,
theme: themeManager.currentTheme,
home: const SplashPage(),
builder: FlutterSmartDialog.init(
builder: (context, child) {
return Stack(
fit: StackFit.expand,
children: [child ?? const SizedBox.shrink()],
);
},
),
navigatorObservers: [routeObserver, modalRouteObserver],
),
),
);
},
);
},
);
},
);
}
}