You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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主题定义是否正确;
  • 确保登录流程与应用初始化未覆盖主题设置。

问题咨询

请问是什么原因导致应用启动时出现错误光标颜色,且需热重载才能修复主题与登录表现?如何确保初始启动时一切正常?


问题原因分析

  1. 重复初始化Provider导致状态冲突:main函数中已初始化LocaleProvider、UserNotifier、ThemeNotifier,但登录状态下又重新创建这些Provider实例,导致Widget初始绑定旧实例,热重载后才切换到正确实例。
  2. 临时MaterialApp未应用自定义主题:FutureBuilder等待、错误状态及未登录时返回的MaterialApp未配置自定义主题,使用Flutter默认主题(紫色光标是默认accent颜色)。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 20:04:53