Flutter如何加载多个Future 解决主题配置在界面渲染后才生效问题
问题根源
你当前的主题配置是在CustomTheme实例化后异步加载的,runApp执行时主题用的是默认的isDarkTheme = false值,等异步加载完偏好设置才会触发重绘,就会出现界面渲染完成后才切换主题的问题。FutureBuilder是Flutter组件,不能在非Widget的theme.dart逻辑文件里直接使用。
解决步骤
1. 修改theme.dart,新增可等待的初始化方法
把加载偏好设置的逻辑改成可等待的Future,方便在启动阶段提前执行:
import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; CustomTheme customTheme = CustomTheme(); class CustomTheme extends ChangeNotifier { // 移除static修饰,避免状态监听异常 bool isDarkTheme = false; ThemeData get currentTheme => isDarkTheme ? ThemeData( brightness: Brightness.dark, scaffoldBackgroundColor: Colors.grey.shade900, primaryColor: Colors.white, accentColor: Colors.blue, iconTheme: const IconThemeData(color: Colors.white, opacity: 0.8), ) : ThemeData( brightness: Brightness.light, scaffoldBackgroundColor: Colors.white, primaryColor: Colors.black, accentColor: Colors.blue, iconTheme: const IconThemeData(color: Colors.black, opacity: 0.8), dividerColor: Colors.black12, ); // 新增异步初始化方法,提前加载主题配置 Future<void> initTheme() async { await loadPrefs(); } void toggleTheme() { isDarkTheme = !isDarkTheme; savePrefs(); notifyListeners(); } void savePrefs() async { SharedPreferences _prefs = await SharedPreferences.getInstance(); _prefs.setBool('theme', isDarkTheme); } Future<void> loadPrefs() async { SharedPreferences _prefs = await SharedPreferences.getInstance(); isDarkTheme = _prefs.getBool('theme') ?? false; notifyListeners(); } }
2. 修改main.dart,合并邮箱和主题的加载逻辑
把两类配置的加载合并成一个Future,等所有配置加载完成再渲染业务界面,避免主题切换闪烁:
import 'package:chatapp/home.dart'; import 'package:chatapp/login.dart'; import 'package:chatapp/themes.dart'; import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:shared_preferences/shared_preferences.dart'; Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> with WidgetsBindingObserver { // 合并两个异步加载任务:读取邮箱配置 + 读取主题配置 Future<Map<String, dynamic>> loadAllConfigs() async { SharedPreferences prefs = await SharedPreferences.getInstance(); String email = prefs.getString('email') ?? ''; // 等待主题配置加载完成 await customTheme.initTheme(); return {'email': email}; } @override void initState() { super.initState(); customTheme.addListener(() { setState(() {}); }); } @override Widget build(BuildContext context) { return MaterialApp( theme: customTheme.currentTheme, // 基于合并后的Future做渲染判断,所有配置加载完成后再进入业务页 home: FutureBuilder<Map<String, dynamic>>( future: loadAllConfigs(), builder: (BuildContext context, AsyncSnapshot<Map<String, dynamic>> snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Scaffold( body: Center( child: CircularProgressIndicator(), ), ); } else if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasError) { return const Text('Error'); } else if (snapshot.hasData && snapshot.data!['email'].isNotEmpty) { return Home(); } else { return Login(); } } else { return Text('State: ${snapshot.connectionState}'); } }, ), ); } }
实现原理
现在邮箱、主题两类配置都会在启动加载阶段全部读取完成,等主题配置已经赋值后才会渲染首页/登录页,完全避免了界面渲染后再切换主题的问题。如果想要进一步提升启动速度,也可以把主题初始化逻辑放到main函数里await Firebase.initializeApp()的后面同步等待,效果完全一致。
内容的提问来源于stack exchange,提问作者josef
相关产品推荐
相关产品推荐

