import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:fluwx/fluwx.dart'; import 'app/theme/app_theme.dart'; import 'app/main_shell.dart'; import 'app/i18n/app_localizations.dart'; import 'app/i18n/locale_manager.dart'; import 'core/services/auth_service.dart'; import 'core/updater/update_service.dart'; import 'core/telemetry/telemetry_service.dart'; import 'core/telemetry/telemetry_route_observer.dart'; import 'core/updater/models/update_config.dart'; import 'core/push/push_service.dart'; import 'core/providers/notification_badge_manager.dart'; import 'features/auth/presentation/pages/login_page.dart'; import 'features/auth/presentation/pages/welcome_page.dart'; import 'features/auth/presentation/pages/register_page.dart'; import 'features/auth/presentation/pages/forgot_password_page.dart'; import 'features/auth/presentation/providers/auth_provider.dart'; import 'features/coupons/presentation/pages/coupon_detail_page.dart'; import 'features/coupons/presentation/pages/order_confirm_page.dart'; import 'features/coupons/presentation/pages/payment_page.dart'; import 'features/coupons/presentation/pages/payment_success_page.dart'; import 'features/coupons/presentation/pages/search_page.dart'; import 'features/coupons/presentation/pages/my_coupon_detail_page.dart'; import 'features/coupons/presentation/pages/redeem_qr_page.dart'; import 'features/trading/presentation/pages/trading_page.dart'; import 'features/trading/presentation/pages/transfer_page.dart'; import 'features/trading/presentation/pages/sell_order_page.dart'; import 'features/wallet/presentation/pages/wallet_page.dart'; import 'features/wallet/presentation/pages/deposit_page.dart'; import 'features/wallet/presentation/pages/withdraw_page.dart'; import 'features/wallet/presentation/pages/transaction_records_page.dart'; import 'features/profile/presentation/pages/kyc_page.dart'; import 'features/profile/presentation/pages/settings_page.dart'; import 'features/profile/presentation/pages/payment_management_page.dart'; import 'features/profile/presentation/pages/pro_mode_page.dart'; import 'features/ai_agent/presentation/pages/agent_chat_page.dart'; import 'features/message/presentation/pages/message_detail_page.dart'; import 'features/issuer/presentation/pages/issuer_main_page.dart'; import 'features/merchant/presentation/pages/merchant_home_page.dart'; import 'features/trading/presentation/pages/trading_detail_page.dart'; import 'features/coupons/presentation/pages/wallet_coupons_page.dart'; import 'features/profile/presentation/pages/share_page.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); // 全局 Flutter 框架错误上报(如 Widget build 异常) // 仅在 release 模式下上报,debug 模式保留默认的 red-screen FlutterError.onError = (FlutterErrorDetails details) { FlutterError.dumpErrorToConsole(details); TelemetryService().logError( details.exceptionAsString(), error: details.exception, stackTrace: details.stack, extra: { 'source': 'flutter_framework', 'library': details.library ?? 'unknown', }, ); }; // ── 微信 SDK 初始化 (fluwx 5.x) ────────────────────────────────────── const wechatAppId = String.fromEnvironment('WECHAT_APP_ID', defaultValue: ''); if (wechatAppId.isNotEmpty) { await Fluwx().registerApi( appId: wechatAppId, universalLink: 'https://www.gogenex.com/wechat/', ); } // 初始化升级服务(走 Nginx 反向代理 → Kong 网关) UpdateService().initialize(UpdateConfig.selfHosted( apiBaseUrl: 'https://api.gogenex.com', enabled: true, )); // 初始化推送服务(无 Firebase 配置时静默失败) await PushService().initialize(); // 恢复用户语言偏好(无选择时跟随系统语言) await LocaleManager.init(); // 从安全存储恢复上次登录的 Token // ProviderScope 外提前恢复,确保 initialRoute 能正确判断 final isLoggedIn = await AuthService.instance.restoreSession(); runApp( ProviderScope( child: GenexConsumerApp(initiallyLoggedIn: isLoggedIn), ), ); } /// Genex Mobile - 券的生命周期管理平台 class GenexConsumerApp extends ConsumerStatefulWidget { final bool initiallyLoggedIn; const GenexConsumerApp({super.key, required this.initiallyLoggedIn}); @override ConsumerState createState() => _GenexConsumerAppState(); } class _GenexConsumerAppState extends ConsumerState { final _navigatorKey = GlobalKey(); @override void initState() { super.initState(); LocaleManager.userLocale.addListener(_onLocaleChanged); // 监听 AuthService ValueNotifier → 同步到 Riverpod authProvider // ApiClient 的 Token 刷新/过期回调仍走 AuthService,此处桥接到 Riverpod AuthService.instance.authState.addListener(_onLegacyAuthChanged); // 启动时已登录,同步初始状态到 Riverpod authProvider if (widget.initiallyLoggedIn) { WidgetsBinding.instance.addPostFrameCallback((_) { ref.read(authProvider.notifier).restoreSession(); }); } // 遥测服务 + 通知徽章管理器:首帧渲染后初始化,避免冷启动 DNS 竞争 WidgetsBinding.instance.addPostFrameCallback((_) async { final auth = AuthService.instance.authState.value; await TelemetryService().initialize( apiBaseUrl: 'https://api.gogenex.com', context: context, userId: auth?.user['id'] as String?, ); if (auth != null) { TelemetryService().setAccessToken(auth.accessToken); } NotificationBadgeManager().initialize(); }); } @override void dispose() { LocaleManager.userLocale.removeListener(_onLocaleChanged); AuthService.instance.authState.removeListener(_onLegacyAuthChanged); super.dispose(); } void _onLocaleChanged() => setState(() {}); /// AuthService ValueNotifier 变化时同步到 Riverpod authProvider void _onLegacyAuthChanged() { if (!mounted) return; final legacyResult = AuthService.instance.authState.value; final riverpodState = ref.read(authProvider); if (legacyResult == null && riverpodState.isAuthenticated) { // Token 双重过期:AuthService 已清空,通知 Riverpod 同步 ref.read(authProvider.notifier).onSessionExpired(); _navigatorKey.currentState?.pushNamedAndRemoveUntil('/', (_) => false); } else if (legacyResult != null) { // 登录成功后立即刷新通知徽章 NotificationBadgeManager().refresh(); } } @override Widget build(BuildContext context) { final isAuthenticated = ref.watch(isAuthenticatedProvider); return MaterialApp( title: 'Genex', theme: AppTheme.light, debugShowCheckedModeBanner: false, navigatorKey: _navigatorKey, // 路由观察器:自动上报所有页面的 page_view 事件,支持 DAU/页面漏斗分析 navigatorObservers: [TelemetryRouteObserver.instance], // i18n locale: LocaleManager.userLocale.value, supportedLocales: LocaleManager.supportedLocales, localizationsDelegates: const [ AppLocalizationsDelegate(), GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], localeResolutionCallback: (systemLocale, supportedLocales) { if (LocaleManager.userLocale.value == null) { return LocaleManager.resolve( systemLocale != null ? [systemLocale] : null, supportedLocales, ); } return LocaleManager.userLocale.value; }, // 启动路由:已有保存的 Token → 直接进主界面;否则欢迎页 initialRoute: (widget.initiallyLoggedIn || isAuthenticated) ? '/main' : '/', onGenerateRoute: _generateRoute, ); } Route _generateRoute(RouteSettings settings) { switch (settings.name) { case '/': return MaterialPageRoute(builder: (_) => const WelcomePage()); case '/login': return MaterialPageRoute(builder: (_) => const LoginPage()); case '/register': final regArgs = settings.arguments as Map?; return MaterialPageRoute( builder: (_) => RegisterPage(isEmail: regArgs?['isEmail'] == true), ); case '/forgot-password': return MaterialPageRoute(builder: (_) => const ForgotPasswordPage()); case '/main': return MaterialPageRoute(builder: (_) => const MainShell()); case '/coupon/detail': return MaterialPageRoute(builder: (_) => const CouponDetailPage()); case '/order/confirm': return MaterialPageRoute(builder: (_) => const OrderConfirmPage()); case '/payment': return MaterialPageRoute(builder: (_) => const PaymentPage()); case '/payment/success': return MaterialPageRoute(builder: (_) => const PaymentSuccessPage()); case '/search': return MaterialPageRoute(builder: (_) => const SearchPage()); case '/coupon/mine/detail': return MaterialPageRoute(builder: (_) => const MyCouponDetailPage()); case '/redeem': return MaterialPageRoute(builder: (_) => const RedeemQrPage()); case '/trading': return MaterialPageRoute(builder: (_) => const TradingPage()); case '/transfer': return MaterialPageRoute(builder: (_) => const TransferPage()); case '/sell': return MaterialPageRoute(builder: (_) => const SellOrderPage()); case '/wallet': return MaterialPageRoute(builder: (_) => const WalletPage()); case '/wallet/deposit': return MaterialPageRoute(builder: (_) => const DepositPage()); case '/wallet/withdraw': return MaterialPageRoute(builder: (_) => const WithdrawPage()); case '/wallet/records': return MaterialPageRoute(builder: (_) => const TransactionRecordsPage()); case '/kyc': return MaterialPageRoute(builder: (_) => const KycPage()); case '/settings': return MaterialPageRoute(builder: (_) => const SettingsPage()); case '/payment/manage': return MaterialPageRoute(builder: (_) => const PaymentManagementPage()); case '/pro-mode': return MaterialPageRoute(builder: (_) => const ProModePage()); case '/ai-chat': return MaterialPageRoute(builder: (_) => const AgentChatPage()); case '/message/detail': return MaterialPageRoute(builder: (_) => const MessageDetailPage()); case '/issuer': return MaterialPageRoute(builder: (_) => const IssuerMainPage()); case '/merchant': return MaterialPageRoute(builder: (_) => const MerchantHomePage()); case '/trading/detail': return MaterialPageRoute(builder: (_) => const TradingDetailPage()); case '/wallet/coupons': return MaterialPageRoute(builder: (_) => const WalletCouponsPage()); case '/share': return MaterialPageRoute(builder: (_) => const SharePage()); default: return MaterialPageRoute( builder: (_) => Scaffold( body: Center(child: Text('Route not found: ${settings.name}')), ), ); } } }