Flutter主题切换——让你的APP也能一键换肤

为了让你的 App 更美观,主题切换已是一个必不可少的功能了,但若是想在传统的 Android 和 iOS 上分别适配不一样的主题至关繁琐。但这一切,在 Flutter 中都很是容易实现。今天咱们就来看看,如何在 Flutter 中给你的 App 添加换肤功能。咱们要实现的效果以下:html

添加依赖

在该案例中,我使用到了 providerflustars 两个库,简单介绍一下这两个库:微信

provider

官方推荐的状态管理库,相比其余状态管理库使用起来比较方便。app

状态管理:通俗的讲,当咱们想在多个页面(组件/Widget)之间共享状态(数据),或者一个页面(组件/Widget)中的多个子组件之间共享状态(数据),这个时候咱们就能够用 Flutter 中的状态管理来管理统一的状态(数据),实现不一样组件直接的传值和数据共享。less

flustars

号称“Flutter 全网最全经常使用工具类”,其中包括了SpUtilScreenUtilTimelineUtil等常见工具类,这里咱们要使用的是SpUtil这个部分,用于存储用户所选择的主题信息。async


以上就是关于咱们使用的两个第三方库的介绍,若是想要使用,咱们须要在pubspec.yaml文件中添加以下内容:ide

provider: ^4.0.5
flustars: ^0.2.6+1
复制代码

准备工做作好了,接下来咱们就开始编码吧。工具

添加主题样式

咱们须要先想好本身所须要切换的主题样式列表,若是以为麻烦的能够直接用下面的内容:字体

Map<String, Color> themeColorMap = {
  'gray': Colors.grey,
  'blue': Colors.blue,
  'blueAccent': Colors.blueAccent,
  'cyan': Colors.cyan,
  'deepPurple': Colors.purple,
  'deepPurpleAccent': Colors.deepPurpleAccent,
  'deepOrange': Colors.orange,
  'green': Colors.green,
  'indigo': Colors.indigo,
  'indigoAccent': Colors.indigoAccent,
  'orange': Colors.orange,
  'purple': Colors.purple,
  'pink': Colors.pink,
  'red': Colors.red,
  'teal': Colors.teal,
  'black': Colors.black,
};
复制代码

使用 Provider 进行全局状态管理

而后咱们就须要使用 Provider 来进行全局的状态管理了。首先先建立一个app_provider.dart文件,而后添加以下代码:ui

class AppInfoProvider with ChangeNotifier {
  String _themeColor = '';

  String get themeColor => _themeColor;

  setTheme(String themeColor) {
    _themeColor = themeColor;
    notifyListeners();
  }
}
复制代码

由于是全局的状态管理,接下来咱们须要在main.dart文件中配置一下刚才建立的 provider,有多个状态管理就使用 MultiProvider,单个的使用 Provider.value 就好了。(考虑到将来项目的扩展,这里我就直接使用 MultiProvider)了编码

class MyApp extends StatelessWidget {
  Color _themeColor;

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [ChangeNotifierProvider.value(value: AppInfoProvider())],
      child: Consumer<AppInfoProvider>(
        builder: (context, appInfo, _) {
          String colorKey = appInfo.themeColor;
          if (themeColorMap[colorKey] != null) {
            _themeColor = themeColorMap[colorKey];
          }

          return MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
              primaryColor: _themeColor,
              floatingActionButtonTheme:
                  FloatingActionButtonThemeData(backgroundColor: _themeColor),
            ),
            home: MyHomePage(title: 'Flutter Theme Change demo'),
          );
        },
      ),
    );
  }
}
复制代码

若是想要在某个地方改变主题,咱们只须要执行下面这行代码便可。

Provider.of<AppInfoProvider>(context).setTheme(colorKey);
复制代码

咱们先来讲说上面这段代码,重点就在于 ThemeData 的设置:

咱们看看ThemeData部分数据定义:

ThemeData({
  Brightness brightness, //深色仍是浅色
  MaterialColor primarySwatch, //主题颜色样本,见下面介绍
  Color primaryColor, //主色,决定导航栏颜色
  Color accentColor, //次级色,决定大多数Widget的颜色,如进度条、开关等。
  Color cardColor, //卡片颜色
  Color dividerColor, //分割线颜色
  ButtonThemeData buttonTheme, //按钮主题
  Color cursorColor, //输入框光标颜色
  Color dialogBackgroundColor,//对话框背景颜色
  String fontFamily, //文字字体
  TextTheme textTheme,// 字体主题,包括标题、body等文字样式
  IconThemeData iconTheme, // Icon的默认样式
  TargetPlatform platform, //指定平台,应用特定平台控件风格
  ...
})
复制代码

上面只是ThemeData的一小部分属性,完整的数据定义读者能够查看 SDK。

之因此使用floatingActionButtonTheme单独设置floatingActionButton而不是使用accentTextTheme,是由于会有警告 ⚠️The support for configuring the foreground color of FloatingActionButtons using ThemeData.accentIconTheme has been deprecated. Please use ThemeData.floatingActionButtonTheme instead. See https://flutter.dev/go/remove-fab-accent-theme-dependency. This feature was deprecated after v1.13.2.意思就是这个属性将会在1.13.2中被废弃。不过并不影响咱们如今的使用。

更多关于主题的内容能够参考 👉颜色和主题

持久化选择的主题

这里就须要使用到一开始提到的flustars中的SpUtil了,咱们通常会在页面初始化加载的时候读取保存的颜色信息,因此咱们须要在初始化页面配置以下代码:

String _colorKey;

@override
void initState() {
  super.initState();
  _initAsync();
}

Future<void> _initAsync() async {
  await SpUtil.getInstance();
  _colorKey = SpUtil.getString('key_theme_color', defValue: 'blue');
  // 设置初始化主题颜色
  Provider.of<AppInfoProvider>(context, listen: false).setTheme(_colorKey);
}
复制代码

await SpUtil.getInstance();这段代码用于加载SpUtil库,经过看源码咱们知道,这个库采用了单例模式,固然,这不是这篇文章的重点。

上面这段代码用于初始化主题,咱们经过SpUtil.getString('key_theme_color', defValue: 'blue');获取保存的主题信息,而后再使用Provider.of<AppInfoProvider>(context, listen: false).setTheme(_colorKey);设置主题便可。

初始化主题弄好了,那选择的代码又如何编写呢?

很简单,只须要才合适的地方调用下面的代码就能够了。

setState(() {
  _colorKey = key;
});
SpHelper.putString('key_theme_color', key);
Provider.of<AppInfoProvider>(context).setTheme(key);
复制代码

思路和上面大同小异,无非是将getString换成了putString

切换主题控件的编写

上面的代码提供了切换主题的思路,可是对于用户来讲,他们所要作的是有一个界面可让他们直接切换主题,所以,下面咱们来编写切换主题的控件。

由于切换主题一般会在设置界面中出现,因此这里我用了一个ExpansionTile,这是一个能够展开的ListTile,代码以下:

…………
ExpansionTile(
  leading: Icon(Icons.color_lens),
  title: Text('颜色主题'),
  initiallyExpanded: false,
  children: <Widget>[
    Padding(
      padding: EdgeInsets.only(left: 10, right: 10, bottom: 10),
      child: Wrap(
        spacing: 8,
        runSpacing: 8,
        children: themeColorMap.keys.map((key) {
          Color value = themeColorMap[key];
          return InkWell(
            onTap: () {
              setState(() {
                _colorKey = key;
              });
              SpUtil.putString('key_theme_color', key);
              Provider.of<AppInfoProvider>(context, listen: false)
                  .setTheme(key);
            },
            child: Container(
              width: 40,
              height: 40,
              color: value,
              child: _colorKey == key
                  ? Icon(
                      Icons.done,
                      color: Colors.white,
                    )
                  : null,
            ),
          );
        }).toList(),
      ),
    )
  ],
),
…………
复制代码

效果以下:

上面这段代码就是将咱们最开始选定的一些主题themeColorMap展现出来,告诉用户能够切换哪些主题。其中onTap内的代码就是上一节中提到的设置颜色主题的方法,InkWell主要用于提供主题色的点击效果,换成GestureDetector也是能够的。

至此咱们的换肤功能也就完成了,想要获取完整代码的能够关注公众号「01 二进制」,后台回复「Flutter 主题切换」。

最后

以上就是关于如何在 Flutter 中切换主题的详细内容了。能够看出,相较于原生应用主题的适配,在 Flutter 中实现换肤的功能简单不少了。

最后来发布一篇预告,由于 iOS 13 和 Android 10 系统上都新增了「深色模式」,在文中我也提到了ThemeDataBrightness brightness属性用于表示深色仍是浅色。下一篇文章我就来聊一聊深色模式的适配。

若是你以为个人文章对你有所帮助,不妨给个赞 👍 或者关注支持一下。

黑白大气神秘简约微信公众号二维码
相关文章
相关标签/搜索