首页 文章

如何在fluttter开头加载主题

提问于
浏览
1

我希望用户可以在我的应用中更改并保存主题颜色 . 但是,我不知道如何在应用程序开始运行时加载保存的主题颜色 . 例如,我想直接在下面的评论位置加载保存的主题颜色 . 我试过SharedPreference . 但是,SharedPreference实例需要使用await运行 . 似乎不能在这里使用 . 有没有什么办法可以直接在这里加载保存的主题,而不是使用setState或类似的东西?

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: // how to load saved theme here?
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

2 回答

  • 2

    这个答案更进一步 . 它显示了如何加载和保存主题首选项,如何构建 ThemeData ,以及如何从应用程序的页面更改主题 .


    • 使用 shared_preferences 插件保存用户首选项(选择了哪个主题) .

    • 使用整个Flutter框架中使用的"controller pattern"为您的应用程序提供当前选定的主题(及其更改) .

    • 使用 InheritedWidget 在应用程序的任何部分使用控制器 .

    以下是控制器的外观:

    import 'package:flutter/material.dart';
    import 'package:shared_preferences/shared_preferences.dart';
    
    /// provides the currently selected theme, saves changed theme preferences to disk
    class ThemeController extends ChangeNotifier {
      static const themePrefKey = 'theme';
    
      ThemeController(this._prefs) {
        // load theme from preferences on initialization
        _currentTheme = _prefs.getString(themePrefKey) ?? 'light';
      }
    
      final SharedPreferences _prefs;
      String _currentTheme;
    
      /// get the current theme
      String get currentTheme => _currentTheme;
    
      void setTheme(String theme) {
        _currentTheme = theme;
    
        // notify the app that the theme was changed
        notifyListeners();
    
        // store updated theme on disk
        _prefs.setString(themePrefKey, theme);
      }
    
      /// get the controller from any page of your app
      static ThemeController of(BuildContext context) {
        final provider = context.inheritFromWidgetOfExactType(ThemeControllerProvider) as ThemeControllerProvider;
        return provider.controller;
      }
    }
    
    /// provides the theme controller to any page of your app
    class ThemeControllerProvider extends InheritedWidget {
      const ThemeControllerProvider({Key key, this.controller, Widget child}) : super(key: key, child: child);
    
      final ThemeController controller;
    
      @override
      bool updateShouldNotify(ThemeControllerProvider old) => controller != old.controller;
    }
    

    以下是在应用程序中使用控制器和 InheritedWidget 的方法:

    void main() async {
      // load the shared preferences from disk before the app is started
      final prefs = await SharedPreferences.getInstance();
    
      // create new theme controller, which will get the currently selected from shared preferences
      final themeController = ThemeController(prefs);
    
      runApp(MyApp(themeController: themeController));
    }
    
    class MyApp extends StatelessWidget {
      final ThemeController themeController;
    
      const MyApp({Key key, this.themeController}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        // use AnimatedBuilder to listen to theme changes (listen to ChangeNotifier)
        // the app will be rebuilt when the theme changes
        return AnimatedBuilder(
          animation: themeController,
          builder: (context, _) {
            // wrap app in inherited widget to provide the ThemeController to all pages
            return ThemeControllerProvider(
              controller: themeController,
              child: MaterialApp(
                title: 'Flutter Demo',
                theme: _buildCurrentTheme(),
                home: MyHomePage(),
              ),
            );
          },
        );
      }
    
      // build the flutter theme from the saved theme string
      ThemeData _buildCurrentTheme() {
        switch (themeController.currentTheme) {
          case "dark":
            return ThemeData(
              brightness: Brightness.dark,
              primarySwatch: Colors.orange,
            );
          case "light":
          default:
            return ThemeData(
              brightness: Brightness.light,
              primarySwatch: Colors.blue,
            );
        }
      }
    }
    
    class MyHomePage extends StatefulWidget {
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: new AppBar(),
          body: Center(
            child: Column(
              children: <Widget>[
                RaisedButton(
                  onPressed: () {
                    // thanks to the inherited widget, we can access the theme controller from any page
                    ThemeController.of(context).setTheme('light');
                  },
                  child: Text('Light Theme'),
                ),
                RaisedButton(
                  onPressed: () {
                    ThemeController.of(context).setTheme('dark');
                  },
                  child: Text('Dark Theme'),
                )
              ],
            ),
          ),
        );
      }
    }
    
  • 1

    您可以选择如何加载它 . 第一个是Gunter在评论中说的 - 你把 MyApp 变成一个有状态的小部件并用 initState() 加载它,然后setState它 .

    这看起来像这样:

    class MyApp extends StatefulWidget {
      @override
      MyAppState createState() => MyAppState();
    }
    
    class MyAppState extends State<MyApp> {
    
      ThemeData theme = ThemeData.dark(); // whatever your default is
    
      @override
      void initState() {
        super.initState();
        SharedProperties.getInstance().then((prefs) {
         ThemeData theme = ThemeData.light(); // load from prefs here
         setState(() => this.theme = theme);
        });
      }
    
      ...
    }
    

    第二种选择是使用FutureBuilder .

    class MyApp extends StatelessWidget {
    
      final Future<ThemeData> loadThemeData = SharedPreferences.getInstance().then((prefs) {
         ... get theme from prefs
         return ThemeData.light();
      });
    
    
      @override
      Widget build(BuildContext context) {
        return FutureBuilder(
          future: loadThemeData,
          builder: (context, snapshot) {
            return MaterialApp(
              theme: snapshot.data,
            );        
          },
          initialData: ThemeData.dark(), // whatever you want your default theme to be
        );
      }
    }
    

    第三个选项是在实际启动应用程序之前进行加载 - 在main方法中 . 我不知道这是否真的被推荐,好像共享偏好需要一段时间它可能会延迟你的应用程序的开始,但实际上它应该非常快,你可能想要避免闪光不同的主题显示无论如何 .

    main() async {
      SharedPreferences prefs = await SharedPreferences.getInstance();
      ThemeData theme = ThemeData.dark(); // get theme from prefs
    
      runApp(MyApp(
        theme: theme,
      ));
    }
    
    class MyApp extends StatelessWidget {
      final ThemeData theme;
    
      const MyApp({Key key, @required this.theme}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          theme: theme,
          ....
        );
      }
    }
    

相关问题