Flutter适配深色模式(DarkMode)

在这里插入图片描述

1.瞎叨叨

也不知道写点什么,原本想写写Flutter的集成测试。由于前一阵子给flutter_deer写了一套,不过感受也没啥内容,写不了几句话就放弃了。(其实本篇内容也很少。。。)java

那就写写最近在作的事情。没错,就是文章标题提到的适配深色模式(DarkMode),也能够说是实现夜间模式的功能。相信许多iOS的同窗最近都比较关注,毕竟iOS 13上个月推送更新了。git

说适配的缘由是由于在iOS 13 和 Android 10系统上它都属于新特性。适配的目的是为了达到应用的主题随着系统主题模式的切换而变化,给用户更好的一致性体验。与它相似的就是系统语言的设置,当系统设置某种语言时,应用内的文字也相应变化。github

好在Flutter也提供了适配的入口,使得咱们能够一次适配两个平台。我手上的小米mix2s虽然是Android 9 的,没想到也能适配。canvas

2.准备工做

下面我就说说我在适配flutter_deer中的经验,Flutter版本1.9.1。微信

首先是规范问题,标题、副标题、分割线、各类背景等颜色,以及深色模式下相对应的颜色必定要先规范起来。不然你本身不只被这些颜色搞得眼冒金星,同时应用也没有一个统一的风格。app

3.适配开始

1.全局调整

Flutter 在 MaterialApp中提供了themedarkTheme两个入口让咱们设置两种模式下的颜色及文字样式。接收的ThemeData中近乎涵盖了全部Material Widget中所使用的颜色及主题。(Cupertino系列组件官方还在适配中,因此Flutter版本1.9.1暂不支持。)ide

经过配置themedarkTheme可让咱们省去不少的判断代码,好比个人分割线在不一样模式下是两种不一样颜色,我不可能每使用一次,就在使用的地方去判断一次。经过配置全局dividerTheme,咱们就能够直接使用Divider()或者BorderSide测试

ThemeData(
      dividerTheme: DividerThemeData(
        color: isDarkMode ? Colours.dark_line : Colours.line,
        space: 0.6,
        thickness: 0.6
      )
    );
复制代码

一样咱们的页面背景色、文字样式均可以这样配置。如下就是deer中最终整理的配置。ui

ThemeData(
      errorColor: isDarkMode ? Colours.dark_red : Colours.red,
      brightness: isDarkMode ? Brightness.dark : Brightness.light,
      primaryColor: isDarkMode ? Colours.dark_app_main : Colours.app_main,
      accentColor: isDarkMode ? Colours.dark_app_main : Colours.app_main,
      // Tab指示器颜色
      indicatorColor: isDarkMode ? Colours.dark_app_main : Colours.app_main,
      // 页面背景色
      scaffoldBackgroundColor: isDarkMode ? Colours.dark_bg_color : Colors.white,
      // 主要用于Material背景色
      canvasColor: isDarkMode ? Colours.dark_material_bg : Colors.white,
      // 文字选择色(输入框复制粘贴菜单)
      textSelectionColor: Colours.app_main.withAlpha(70),
      textSelectionHandleColor: Colours.app_main,
      textTheme: TextTheme(
        // TextField输入文字颜色
        subhead: isDarkMode ? TextStyles.textDark : TextStyles.text,
        // Text默认文字样式
        body1: isDarkMode ? TextStyles.textDark : TextStyles.text,
        // 这里用于小文字样式
        subtitle: isDarkMode ? TextStyles.textDarkGray12 : TextStyles.textGray12,
      ),
      inputDecorationTheme: InputDecorationTheme(
        hintStyle: isDarkMode ? TextStyles.textHint14 : TextStyles.textDarkGray14,
      ),
      appBarTheme: AppBarTheme(
        elevation: 0.0,
        color: isDarkMode ? Colours.dark_bg_color : Colors.white,
        brightness: isDarkMode ? Brightness.dark : Brightness.light,
      ),
      dividerTheme: DividerThemeData(
        color: isDarkMode ? Colours.dark_line : Colours.line,
        space: 0.6,
        thickness: 0.6
      )
    );
复制代码

使用:spa

MaterialApp (
      title: 'Flutter Deer',
      theme: getTheme(),
      darkTheme: getTheme(isDarkMode: true),
      home: TestPage()
    );			
复制代码

固然有些Widget没有使用到,因此也就没有去适配。以上这些color、theme具体的使用地方须要本身去翻看源码及注释才能知道,因此这是一个比较费力的过程。

其实这里你也能够利用某些“坑位”,好比应用内的另一种功能文字在字号、颜色上都与主文字不同,使用的地方还不少,每次使用再判断也很麻烦,这样就能够设置到未使用的属性上,好比上面代码中的subtitle。这样使用时就能够经过调用Theme.of(context).textTheme.subtitle来实现。

Text(
  "文字", 
  style: Theme.of(context).textTheme.subtitle
)
复制代码

须要注意的是:毕竟是全局配置,尽可能保持通用,不要影响其余widget也是要考虑的地方。

这部分配置完成后,你须要的是"去同存异"。

  1. 好比你指定的文字样式与全局配置相同时,就须要删除它。

  2. 若是文字颜色相同,可是字号不一样。那就删除颜色配置信息,保留字号设置:

Text(
  "仅保留不一样信息",
  style: const TextStyle( fontSize: 12.0, ) ) 复制代码

由于Text的源码中就是经过merge方法来合并全局配置与局部配置。merge中其实就是调用copyWith来实现的。因此也能够这样写:

Text(
  "仅保留不一样信息",
  style: Theme.of(context).textTheme.body1.copyWith(fontSize: 12.0)
)
复制代码
  1. 颜色不一样。由于深色模式主要就是颜色变化,因此能够考虑上面的“subtitle”方案。若是仅有几处,能够封装一些方法统一判断处理。

2.局部调整

在通过全局的配置后,大多数适配问题获得了解决。但可能还有一些细节要调整,好比图标、个别的文字颜色、背景色。这时须要的就是如何判断深色模式:

bool isDarkMode(BuildContext context){
    return Theme.of(context).brightness == Brightness.dark;
  }
复制代码

这里的brightness就是上面在全局配置ThemeData中指定的brightness

Tips:

  1. 有些纯色的小图标能够直接使用Image.assetcolor来修改。

  2. ButtontextColor属性最好仍是局部处理,由于源码中“非黑即白”,我很痛苦啊!

/// The foreground color of the [button]'s text and icon.
  ///
  /// If [button] is not [MaterialButton.enabled], the value of
  /// [getDisabledTextColor] is returned. If the button is enabled and
  /// [buttonTextColor] is non-null, then [buttonTextColor] is returned.
  ///
  /// Otherwise the text color depends on the value of [getTextTheme]
  /// and [getBrightness].
  ///
  /// * [ButtonTextTheme.normal]: [Colors.white] is used if [getBrightness]
  /// resolves to [Brightness.dark]. [Colors.black87] is used if
  /// [getBrightness] resolves to [Brightness.light].
  /// * [ButtonTextTheme.accent]: [colorScheme.secondary].
  /// * [ButtonTextTheme.primary]: If [getFillColor] is dark then [Colors.white],
  /// otherwise if [button] is a [FlatButton] or an [OutlineButton] then
  /// [colorScheme.primary], otherwise [Colors.black].
  Color getTextColor(MaterialButton button) {
    if (!button.enabled)
      return getDisabledTextColor(button);

    if (button.textColor != null)
      return button.textColor;

    switch (getTextTheme(button)) {
      case ButtonTextTheme.normal:
        return getBrightness(button) == Brightness.dark ? Colors.white : Colors.black87;

      case ButtonTextTheme.accent:
        return colorScheme.secondary;

      case ButtonTextTheme.primary: {
        final Color fillColor = getFillColor(button);
        final bool fillIsDark = fillColor != null
          ? ThemeData.estimateBrightnessForColor(fillColor) == Brightness.dark
          : getBrightness(button) == Brightness.dark;
        if (fillIsDark)
          return Colors.white;
        if (button is FlatButton || button is OutlineButton)
          return colorScheme.primary;
        return Colors.black;
      }
    }

    assert(false);
    return null;
  }
复制代码

3.功能拓展

若是你适配好了深色模式,其实能够稍微拓展一下这个功能。我想到了微信中的多语言功能,在多语言这类功能中,默认选项是“跟随系统”,固然你也能够指定某种语言。

按照这个思路我在设置中添加了“夜间模式”的功能,默认也是跟随系统,固然你也能够手动的开启和关闭。

这里暂时有个问题,在iOS手机上开启深色模式,当我应用内关闭深色模式后,状态栏文字没法变为黑色

问题主要仍是Flutter 1.9.1的版本并无适配iOS 13 Status Bar增的UIStatusBarStyleDarkContent

在这里插入图片描述

这个问题Flutter的issues中也有人反馈了,期待官方的适配修复吧。

上述这些,基本就是适配深色模式主要内容了。自己没有什么复杂的,主是是个细心活。

说了这么多,最后放几张适配的效果图给你们看看:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

详细的代码以及实现细节,能够参看flutter_deer的代码。深色模式相关的设计图也已经同步更新了。最后但愿能够点赞支持一波!!!

相关文章
相关标签/搜索