Flutter应用初始化光标颜色异常,登录后需热重载修复
Flutter应用启动时光标颜色异常,需热重载/重启恢复
问题描述
我在Flutter应用中遇到如下问题:应用启动时光标颜色异常(比如显示紫色而非预期颜色),该问题持续到登录流程结束,只有通过热重载或重启应用才能恢复正常表现。
相关代码
main函数
void main() async { WidgetsFlutterBinding.ensureInitialized(); bool supabaseInitialized = false; bool firebaseInitialized = false; try { await Supabase.initialize( url: 'https://XXXXXXXXXXXXXXX.supabase.co', anonKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', ); supabaseInitialized = true; } catch (e) { } try { await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform); FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); } catch (e) { } updateExistingUsersExpiration(); ValueNotifier<bool> switchNotifier = ValueNotifier<bool>(false); runApp( MultiProvider( providers: [ ChangeNotifierProvider<LocaleProvider>( create: (context) { return LocaleProvider()..loadLocale(); }, ), ChangeNotifierProvider<UserNotifier>( create: (context) { final userNotifier = UserNotifier(); userNotifier.initializeUser(); return userNotifier; }, ), ChangeNotifierProvider<ThemeNotifier>( create: (context) { return ThemeNotifier(); }, ), ValueListenableProvider<bool>.value( value: switchNotifier, ), ], child: const MyApp(), ), ); }
MyApp的build方法
@override Widget build(BuildContext context) { final _ = Provider.of<LocaleProvider>(context, listen: false); final __ = Provider.of<UserNotifier>(context, listen: false); final ___ = Provider.of<ThemeNotifier>(context, listen: false); return FutureBuilder<String>( future: getCurrentUserId(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( body: Center( child: CircularProgressIndicator(), ), ), ); } else if (snapshot.hasError) { return const MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( body: Center( child: Text('Failed to get user ID'), ), ), ); } else { final userId = snapshot.data; if (userId == null || userId.isEmpty) { return const MaterialApp( debugShowCheckedModeBanner: false, home: AuthPage(), ); } else { return MultiProvider( providers: [ ChangeNotifierProvider<LocaleProvider>( create: (_) => LocaleProvider()..loadLocale(), ), ChangeNotifierProvider<UserNotifier>( create: (context) => UserNotifier(), ), ChangeNotifierProvider<ThemeNotifier>( create: (context) => ThemeNotifier(), ), ChangeNotifierProvider<BalanceNotifier>( create: (context) => BalanceNotifier(userId), ), ChangeNotifierProvider<Cart>( create: (context) => Cart(), ), ChangeNotifierProvider<RateNotifier>( create: (context) => RateNotifier(), ), ChangeNotifierProvider<ValueNotifier<bool>>( create: (context) => ValueNotifier<bool>(false), ), ], child: Consumer<ThemeNotifier>( builder: (context, themeNotifier, child) { return Consumer<LocaleProvider>( builder: (context, localeProvider, child) { return MaterialApp( debugShowCheckedModeBanner: false, theme: lightMode, darkTheme: darkMode, themeMode: themeNotifier.currentTheme, locale: localeProvider.locale, supportedLocales: const [ Locale('fr'), // Français Locale('en'), // Anglais Locale('es'), // Espagnol // Locale('ht'), // Créole haïtien ], localizationsDelegates: const [ AppLocalizations .delegate, // Utilisation de AppLocalizations GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], initialRoute: '/', routes: { '/': (context) { return const SplashScreen(); }, '/ProfilePage': (context) => const ProfilPage(), '/LanguageSelection': (context) => const LanguageSelectionPage(), }, // Routes dynamiques pour gérer les arguments onGenerateRoute: (settings) { switch (settings.name) { case '/homePage': final firebase_auth.User? user = settings.arguments as firebase_auth.User?; if (user == null) { return MaterialPageRoute( builder: (_) => const Scaffold( body: Center( child: Text('user not found')), ), ); } return MaterialPageRoute( builder: (_) => HomePage(user: user), ); case '/GalleryPage': final pinController = settings.arguments as TextEditingController?; if (pinController == null) { return MaterialPageRoute( builder: (_) => const Scaffold( body: Center( child: Text('PinController is missing')), ), ); } return MaterialPageRoute( builder: (_) => GalleryPage(pinController: pinController), ); case '/TransfertPage': final args = settings.arguments as Map<String, dynamic>?; if (args == null || !args.containsKey('total') || !args.containsKey('userId') || !args.containsKey('pinController')) { return MaterialPageRoute( builder: (_) => const Scaffold( body: Center( child: Text( 'No Arguments for TransfertPage')), ), ); } return MaterialPageRoute( builder: (_) => TransfertPage( total: args['total'], userId: args['userId'], pinController: args['pinController'], ), ); default: return MaterialPageRoute( builder: (_) => const Scaffold( body: Center(child: Text('Page not found')), ), ); } }, ); }, ); })); } } }, ); }
细节说明
- 应用启动后立即出现光标颜色错误,且该问题贯穿登录过程;
- 登录后应用表现不符合预期,直到执行热重载或重启;
themeNotifier.currentTheme可在ThemeMode.light和ThemeMode.dark间动态切换,且两种主题配置无误;- 热重载或重启后该问题不再出现。
已尝试操作
- 验证
themeNotifier.currentTheme始终返回正确的ThemeMode; - 检查
lightMode和darkMode主题定义是否正确; - 确保登录流程与应用初始化未覆盖主题设置。
问题咨询
请问是什么原因导致应用启动时出现错误光标颜色,且需热重载才能修复主题与登录表现?如何确保初始启动时一切正常?
问题原因分析
- 重复初始化Provider导致状态冲突:main函数中已初始化
LocaleProvider、UserNotifier、ThemeNotifier,但登录状态下又重新创建这些Provider实例,导致Widget初始绑定旧实例,热重载后才切换到正确实例。 - 临时MaterialApp未应用自定义主题:FutureBuilder等待、错误状态及未登录时返回的
MaterialApp未配置自定义主题,使用Flutter默认主题(紫色光标是默认accent颜色)。 - ThemeNotifier初始化未加载保存主题:
ThemeNotifier初始化时未立即加载本地存储的主题,初始使用默认值,热重载后才完成加载。
修复方案
1. 统一Provider初始化,删除重复创建
去掉登录状态下的MultiProvider,直接使用main中已初始化的Provider:
// 替换登录状态下的MultiProvider代码 return Consumer<ThemeNotifier>( builder: (context, themeNotifier, child) { return Consumer<LocaleProvider>( builder: (context, localeProvider, child) { return MaterialApp( debugShowCheckedModeBanner: false, theme: lightMode, darkTheme: darkMode, themeMode: themeNotifier.currentTheme, locale: localeProvider.locale, supportedLocales: const [ Locale('fr'), // 法语 Locale('en'), // 英语 Locale('es'), // 西班牙语 ], localizationsDelegates: const [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], initialRoute: userId!.isEmpty ? '/auth' : '/', routes: { '/': (context) => const SplashScreen(), '/auth': (context) => const AuthPage(), '/ProfilePage': (context) => const ProfilPage(), '/LanguageSelection': (context) => const LanguageSelectionPage(), }, onGenerateRoute: (settings) { // 保留原有路由逻辑 switch (settings.name) { case '/homePage': final firebase_auth.User? user = settings.arguments as firebase_auth.User?; if (user == null) { return MaterialPageRoute( builder: (_) => const Scaffold( body: Center(child: Text('未找到用户')), ), ); } return MaterialPageRoute( builder: (_) => HomePage(user: user), ); // 其他路由逻辑... default: return MaterialPageRoute( builder: (_) => const Scaffold( body: Center(child: Text('页面未找到')), ), ); } }, ); }, ); }, );
2. 为所有临时MaterialApp应用自定义主题
修改FutureBuilder中等待、错误状态的MaterialApp:
if (snapshot.connectionState == ConnectionState.waiting) { return MaterialApp( debugShowCheckedModeBanner: false, theme: lightMode, darkTheme: darkMode, themeMode: Provider.of<ThemeNotifier>(context).currentTheme, home: const Scaffold( body: Center(child: CircularProgressIndicator()), ), ); } else if (snapshot.hasError) { return MaterialApp( debugShowCheckedModeBanner: false, theme: lightMode, darkTheme: darkMode, themeMode: Provider.of<ThemeNotifier>(context).currentTheme, home: const Scaffold( body: Center(child: Text('获取用户ID失败')), ), ); }
3. 让ThemeNotifier初始化时加载保存的主题
修改ThemeNotifier构造函数:
class ThemeNotifier extends ChangeNotifier { ThemeMode _currentTheme = ThemeMode.light; ThemeNotifier() { _loadSavedTheme(); } ThemeMode get currentTheme => _currentTheme; Future<void> _loadSavedTheme() async { final prefs = await SharedPreferences.getInstance(); final themeString = prefs.getString('theme_mode'); if (themeString != null) { _currentTheme = ThemeMode.values.firstWhere((mode) => mode.name == themeString); notifyListeners(); } } // 主题切换方法... }
4. 整合用户ID获取到UserNotifier
将getCurrentUserId()逻辑移到UserNotifier,用Consumer替代FutureBuilder:
// UserNotifier中添加逻辑 class UserNotifier extends ChangeNotifier { String? _userId; Future<void> initializeUser() async { _userId = await getCurrentUserId(); notifyListeners(); } String? get userId => _userId; } // MyApp的build方法修改 @override Widget build(BuildContext context) { return Consumer<UserNotifier>( builder: (context, userNotifier, child) { if (userNotifier.userId == null) { return MaterialApp( debugShowCheckedModeBanner: false, theme: lightMode, darkTheme: darkMode, themeMode: Provider.of<ThemeNotifier>(context).currentTheme, home: const Scaffold(body: Center(child: CircularProgressIndicator())), ); } else { return Consumer<ThemeNotifier>( builder: (context, themeNotifier, child) { return Consumer<LocaleProvider>( builder: (context, localeProvider, child) { return MaterialApp( // 主MaterialApp配置... initialRoute: userNotifier.userId!.isEmpty ? '/auth' : '/', ); }, ); }, ); } }, ); }
内容的提问来源于stack exchange,提问作者Whirley Paul
相关产品推荐
相关产品推荐

