import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import '../core/auth/session_store.dart'; import '../data/network/api_client.dart'; import '../data/storage/local_kv_store.dart'; import '../features/auth/data/apis/auth_api.dart'; import '../features/auth/data/repositories/auth_repository.dart'; import '../features/auth/presentation/bloc/auth_bloc.dart'; import '../features/auth/presentation/bloc/auth_state.dart'; import '../features/auth/presentation/screens/login_screen.dart'; import '../features/divination/data/apis/divination_api.dart'; import '../features/home/presentation/screens/home_screen.dart'; import '../features/settings/data/models/profile_settings.dart'; import '../l10n/app_localizations.dart'; import '../shared/widgets/app_loading_indicator.dart'; import 'app_theme.dart'; import 'di/injection.dart'; class EryaoApp extends StatefulWidget { const EryaoApp({super.key}); @override State createState() => _EryaoAppState(); } class _EryaoAppState extends State { final SessionStore _sessionStore = SessionStore(LocalKvStore()); late final AuthBloc _authBloc; late final DivinationApi _divinationApi; Locale _locale = const Locale('zh'); ProfileSettingsV1 _profileSettings = ProfileSettingsV1.defaultsForLocale( const Locale('zh'), ); int _creditsBalance = 0; bool _loadingCredits = false; String? _loadedCreditsUserEmail; @override void initState() { super.initState(); final apiClient = ApiClient( baseUrl: appDependencies.backendUrl, tokenProvider: _sessionStore.getToken, onUnauthorized: () { return _authBloc.handleUnauthorized401(); }, ); final authApi = AuthApi(apiClient: apiClient); _divinationApi = DivinationApi(apiClient: apiClient); final authRepository = AuthRepositoryImpl( authApi: authApi, sessionStore: _sessionStore, ); _authBloc = AuthBloc(repository: authRepository); _bootstrap(); } void _ensureCreditsLoaded(String userEmail) { if (_loadingCredits) { return; } if (_loadedCreditsUserEmail == userEmail) { return; } _loadingCredits = true; _divinationApi .getPointsBalance() .then((balance) { if (!mounted) { return; } setState(() { _creditsBalance = balance.availableBalance; _loadedCreditsUserEmail = userEmail; }); }) .whenComplete(() { _loadingCredits = false; }); } @override void dispose() { _authBloc.dispose(); super.dispose(); } Future _bootstrap() async { final localeCode = await _sessionStore.getLocaleCode(); final locale = localeCode == 'en' ? const Locale('en') : const Locale('zh'); if (mounted) { setState(() { _locale = locale; _profileSettings = ProfileSettingsV1.defaultsForLocale(locale); }); } await _authBloc.start(); } Future _handleInterfaceLanguageChanged(String languageTag) async { final locale = localeFromLanguageTag(languageTag); await _sessionStore.saveLocaleCode(locale.languageCode); if (!mounted) { return; } setState(() { _locale = locale; _profileSettings = _profileSettings.copyWith( preferences: _profileSettings.preferences.copyWith( interfaceLanguage: languageTag, ), ); }); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _authBloc, builder: (context, _) { return MaterialApp( debugShowCheckedModeBanner: false, onGenerateTitle: (context) => AppLocalizations.of(context)!.appTitle, locale: _locale, localizationsDelegates: const [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: AppLocalizations.supportedLocales, theme: AppTheme.light(), home: _buildHomeByAuthState(_authBloc.state), ); }, ); } Widget _buildHomeByAuthState(AuthState state) { if (state.status == AuthStatus.initial || state.status == AuthStatus.loading) { return const Scaffold( body: Center( child: AppLoadingIndicator(variant: AppLoadingVariant.surface), ), ); } if (state.status == AuthStatus.authenticated && state.user != null) { _ensureCreditsLoaded(state.user!.email); return HomeScreen( account: state.user!.email, sessionStore: _sessionStore, currentLocale: _locale, profileSettings: _profileSettings, coinBalance: _creditsBalance, onLocaleChanged: _handleInterfaceLanguageChanged, onLogout: _authBloc.logout, ); } return LoginScreen( currentLocale: _locale, onLocaleChanged: (_) {}, onRequestOtp: _authBloc.sendOtp, onLoginWithOtp: (email, otp) { return _authBloc.loginWithOtp(email: email, otp: otp); }, ); } }