import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:bot_toast/bot_toast.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:provider/provider.dart'; import 'package:provider/single_child_widget.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'core/network/dio_client.dart'; import 'core/network/domain_navigator.dart'; import 'core/storage/local_storage.dart'; import 'core/constants/api_endpoints.dart'; import 'core/theme/app_theme.dart'; import 'core/event/app_event_bus.dart'; import 'data/services/user_service.dart'; import 'data/services/market_service.dart'; import 'data/services/trade_service.dart'; import 'data/services/asset_service.dart'; import 'data/services/fund_service.dart'; import 'data/services/bonus_service.dart'; import 'data/services/config_service.dart'; import 'providers/auth_provider.dart'; import 'providers/market_provider.dart'; import 'providers/asset_provider.dart'; import 'providers/trade_provider.dart'; import 'providers/theme_provider.dart'; import 'ui/pages/auth/login_page.dart'; import 'ui/pages/main/main_page.dart'; import 'ui/pages/onboarding/onboarding_page.dart'; /// 全局導航 Key,用於從任意位置跳轉到登錄頁 final GlobalKey navigatorKey = GlobalKey(); void main() async { // 確保 Flutter 綁定初始化 WidgetsFlutterBinding.ensureInitialized(); // 防截图:Android 端通过 FLAG_SECURE(在 MainActivity 中已设置) // Web 端通过 index.html 中的 JS/CSS 防护 // iOS 端暂无原生防截图能力(Web 模式下同样由 JS 保护) // 全局錯誤處理 - Flutter 框架錯誤 FlutterError.onError = (FlutterErrorDetails details) { FlutterError.presentError(details); }; // 全局錯誤處理 - 異步未捕獲錯誤 PlatformDispatcher.instance.onError = (error, stack) { return true; }; Provider.debugCheckInvalidValueType = null; try { await SharedPreferences.getInstance(); await LocalStorage.init(); // 域名導航:Debug 用 localhost,Release 競速 CF Workers final resolvedUrl = await DomainNavigator.init(); ApiEndpoints.init(resolvedUrl); } catch (e, stack) { // 域名解析失敗時使用兜底地址 ApiEndpoints.init(DomainNavigator.activeUrl); } runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MultiProvider( providers: _buildProviders(), child: Consumer( builder: (context, themeProvider, _) { return _buildMaterialApp(context, themeProvider.themeMode); }, ), ); } List _buildProviders() { final dioClient = DioClient(); return [ // Theme Provider (必須放在最前面) ChangeNotifierProvider( create: (_) => ThemeProvider()..init(), ), // Services Provider.value(value: dioClient), Provider(create: (_) => AppEventBus()), Provider(create: (_) => UserService(dioClient)), Provider(create: (_) => MarketService(dioClient)), Provider(create: (_) => TradeService(dioClient)), Provider(create: (_) => AssetService(dioClient)), Provider(create: (_) => FundService(dioClient)), Provider(create: (_) => BonusService(dioClient)), Provider(create: (_) => ConfigService(dioClient)), // State Management ChangeNotifierProvider( create: (ctx) { final authProvider = AuthProvider(ctx.read()); // token 過期時,DioClient 回調 AuthProvider 強制登出 dioClient.onUnauthorized = authProvider.forceLogout; return authProvider; }, ), ChangeNotifierProvider( create: (ctx) => MarketProvider(ctx.read()), ), ChangeNotifierProvider( create: (ctx) => AssetProvider( ctx.read(), ctx.read(), ctx.read(), ctx.read(), ), ), ChangeNotifierProvider( create: (ctx) => TradeProvider( ctx.read(), ctx.read(), ), ), ]; } Widget _buildMaterialApp(BuildContext context, ThemeMode themeMode) { return MaterialApp( navigatorKey: navigatorKey, debugShowCheckedModeBanner: false, theme: AppTheme.lightTheme, darkTheme: AppTheme.darkTheme, themeMode: themeMode, localizationsDelegates: const [ GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], builder: (context, child) { // 配置 BotToast 確保顯示在所有內容之上 final botToastBuilder = BotToastInit(); child = botToastBuilder(context, child); return child; }, navigatorObservers: [BotToastNavigatorObserver()], initialRoute: '/', routes: { '/': (context) => const RootPage(), '/login': (context) => const LoginPage(), '/main': (context) => const MainPage(), }, ); } } /// 根頁面 - 決定顯示引導頁還是主頁面 class RootPage extends StatelessWidget { const RootPage({super.key}); @override Widget build(BuildContext context) { // 檢查是否需要顯示引導頁 if (!LocalStorage.isOnboardingCompleted) { return OnboardingPage( onComplete: () { Navigator.of(context).pushReplacementNamed('/login'); }, ); } return Consumer( builder: (context, auth, _) { if (auth.isLoading) { return const Scaffold( body: Center(child: CircularProgressIndicator()), ); } return auth.isLoggedIn ? const MainPage() : const LoginPage(); }, ); } }