Files
monisuo/flutter_monisuo/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: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/storage/local_storage.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 'providers/auth_provider.dart';
import 'providers/market_provider.dart';
import 'providers/asset_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用於 token 過期時全局跳轉登錄頁
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
void main() async {
// 確保 Flutter 綁定初始化
WidgetsFlutterBinding.ensureInitialized();
// 全局錯誤處理 - Flutter 框架錯誤
FlutterError.onError = (FlutterErrorDetails details) {
FlutterError.presentError(details);
debugPrint('Flutter Error: ${details.exception}');
debugPrint('Stack trace: ${details.stack}');
};
// 全局錯誤處理 - 異步未捕獲錯誤
PlatformDispatcher.instance.onError = (error, stack) {
debugPrint('Uncaught error: $error');
debugPrint('Stack: $stack');
return true;
};
Provider.debugCheckInvalidValueType = null;
try {
await SharedPreferences.getInstance();
await LocalStorage.init();
debugPrint('App initialized successfully');
} catch (e, stack) {
debugPrint('Initialization error: $e');
debugPrint('Stack: $stack');
}
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: _buildProviders(),
child: Consumer<ThemeProvider>(
builder: (context, themeProvider, _) {
return _buildMaterialApp(context, themeProvider.themeMode);
},
),
);
}
List<SingleChildWidget> _buildProviders() {
final dioClient = DioClient(navigatorKey: navigatorKey);
return [
// Theme Provider (必須放在最前面)
ChangeNotifierProvider<ThemeProvider>(
create: (_) => ThemeProvider()..init(),
),
// Services
Provider<DioClient>.value(value: dioClient),
Provider<AppEventBus>(create: (_) => AppEventBus()),
Provider<UserService>(create: (_) => UserService(dioClient)),
Provider<MarketService>(create: (_) => MarketService(dioClient)),
Provider<TradeService>(create: (_) => TradeService(dioClient)),
Provider<AssetService>(create: (_) => AssetService(dioClient)),
Provider<FundService>(create: (_) => FundService(dioClient)),
Provider<BonusService>(create: (_) => BonusService(dioClient)),
// State Management
ChangeNotifierProvider<AuthProvider>(
create: (ctx) {
final authProvider = AuthProvider(ctx.read<UserService>());
// token 過期時DioClient 回調 AuthProvider 強制登出
dioClient.onForceLogout = authProvider.forceLogout;
return authProvider;
},
),
ChangeNotifierProvider<MarketProvider>(
create: (ctx) => MarketProvider(ctx.read<MarketService>()),
),
ChangeNotifierProvider<AssetProvider>(
create: (ctx) => AssetProvider(
ctx.read<AssetService>(),
ctx.read<FundService>(),
ctx.read<AppEventBus>(),
),
),
];
}
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<AuthProvider>(
builder: (context, auth, _) {
if (auth.isLoading) {
return const Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
return auth.isLoggedIn ? const MainPage() : const LoginPage();
},
);
}
}