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

Flutter应用启动时如何通过shared_preferences读取存储的主题并设置

解决方案

方案1:启动前置初始化(最推荐,适配你的现有技术栈)

你遇到的核心矛盾是SharedPreferences读写是异步,而GetMaterialApp的theme参数需要同步值,最简单的解决方式是把主题读取逻辑提前到runApp执行之前完成,步骤如下:

  1. 改造ThemeService,增加同步可访问的当前主题属性和初始化方法:
class ThemeService {
  static String key = "isDark";
  // 同步可访问的当前主题变量
  late ThemeData currentTheme;
  // 缓存SharedPreferences实例,避免重复初始化
  late SharedPreferences _prefs;

  // 启动时调用一次的初始化方法
  Future<void> init() async {
    _prefs = await SharedPreferences.getInstance();
    final isDarkMode = _prefs.getBool(key) ?? false;
    currentTheme = isDarkMode ? ThemeDark().theme : ThemeLight().theme;
  }

  /// 切换深浅主题并持久化存储选择结果
  Future<void> toggleTheme() async {
    final isDarkMode = _prefs.getBool(key) ?? false;
    if (isDarkMode) {
      currentTheme = ThemeLight().theme;
      Get.changeTheme(ThemeLight().theme);
      await _prefs.setBool(key, false);
    } else {
      currentTheme = ThemeDark().theme;
      Get.changeTheme(ThemeDark().theme);
      await _prefs.setBool(key, true);
    }
  }

  /// 保留原有getTheme方法按需使用
  Future<ThemeData> getTheme() async {
    final isDarkMode = _prefs.getBool(key) ?? false;
    return isDarkMode ? ThemeDark().theme : ThemeLight().theme;
  }
}
  1. 改造main函数,提前完成初始化再启动应用:
void main() async {
  // 必须调用这行,确保Flutter渲染绑定初始化完成后再执行异步操作
  WidgetsFlutterBinding.ensureInitialized();
  // 提前初始化ThemeService,读取持久化的主题配置
  final themeService = ThemeService();
  await themeService.init();
  // 将初始化完成的ThemeService存入Get容器,后续全局可访问
  Get.put(themeService);
  // 所有前置初始化完成后再启动App
  runApp(const App());
}
  1. 改造App类,直接使用同步的主题属性:
class App extends StatelessWidget {
  final ThemeService themeService = Get.find<ThemeService>();

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
        debugShowCheckedModeBanner: false,
        initialBinding: InitialBinding(),
        initialRoute: initialRoute(),
        // 直接使用同步的currentTheme,不再是Future类型
        theme: themeService.currentTheme,
        getPages: AppPages.routes);
  }
}

这个方案逻辑简单,没有额外的状态判断,适合绝大多数应用场景。

方案2:启动时用FutureBuilder异步等待

如果你不想卡住启动流程,或者需要在加载主题时展示启动屏,可以用FutureBuilder包裹GetMaterialApp,等异步读取主题完成后再渲染主应用:

class App extends StatelessWidget {
  final ThemeService themeService = Get.find<ThemeService>();

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<ThemeData>(
      future: themeService.getTheme(),
      builder: (context, snapshot) {
        // 异步读取完成后渲染主应用
        if (snapshot.connectionState == ConnectionState.done) {
          return GetMaterialApp(
            debugShowCheckedModeBanner: false,
            initialBinding: InitialBinding(),
            initialRoute: initialRoute(),
            theme: snapshot.data!,
            getPages: AppPages.routes,
          );
        } 
        // 读取中展示自定义启动屏/加载占位
        else {
          return const MaterialApp(
            home: Scaffold(
              body: Center(child: CircularProgressIndicator()),
            ),
          );
        }
      },
    );
  }
}

疑问解答

有没有不需要async/await就可以访问偏好存储的方法?
官方的shared_preferences库本身没有提供同步读写接口,因为它本质是读写本地磁盘文件,异步是默认的实现逻辑。如果你需要同步读写能力,可以使用和GetX生态适配良好的get_storage库,它采用内存缓存+本地持久化的实现,初始化完成后就可以同步读写持久化数据。

内容的提问来源于stack exchange,提问作者Chris

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 10:48:05