Flutter学习篇(八)—— 黑夜模式的实现

2,949 阅读3分钟

觉得有帮助的可以点个赞喔😯

代码

flutter_2020

效果

导航

前言

随着 iOS13 开始推广黑夜模式,现在各个厂商的 App 也开始着力 App 黑夜模式的改造与实现,而 Flutter 作为新一代的跨平台框架,天然支持了黑夜模式的特性。

介绍

Flutter 内嵌了一套主题常量,可以方便快速地定义整个 App 的样式,如下:

ThemeData 预设了很多 widget 的通用特性,如appBar,scaffoldBackgroundColor等, 所以,我们可以通过设置主题的颜色,字体等样式,勾勒出 App 的整体风格,而这个特性就是我们实现黑夜模式的基础。

基础

Flutter 的根 widget MaterialApp有几个关键的属性 —— themeMode, theme,darkTheme,分别对应主题的模式,白天主题,黑夜主题。
themeMode的类型如下:

  • system 随着系统设置
  • light 白天模式,使用theme主题
  • dark 黑夜模式,使用darkTheme主题

控制黑白模式的开关就是themeMode,所以如上所说,我们需要在MaterialApp定义好主题:


class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => GlobalModel()),
      ],
      child: Consumer<GlobalModel>(
          builder: (context, globalModel, _) {
            GlobalTheme.setDark(globalModel.isDarkTheme);

            return MaterialApp(
                title: 'Flutter 2020',
                debugShowCheckedModeBanner: false,
                themeMode: globalModel.isDarkTheme ? ThemeMode.dark : ThemeMode
                    .light,
                theme: GlobalTheme.lightThemeData,
                darkTheme: GlobalTheme.darkThemeData,
                home: MyHomePage()
            );
          }
      ),
    );
  }
}

theme 的定义如下:

  // 生成主题的配色
  static genThemeData(bool isDark) {
    final theme = isDark ? GlobalTheme.dark : GlobalTheme.light;
    return ThemeData(
        scaffoldBackgroundColor: theme.appBgColor,
        appBarTheme: AppBarTheme(
          // 状态栏字体图标颜色
          brightness: isDark ? Brightness.dark : Brightness.light,
          color: theme.mainBgColor,
          iconTheme: IconThemeData(color: theme.appBarFontColor, size: 12),
          textTheme: TextTheme(
              title: TextStyle(color: theme.appBarFontColor, fontSize: 17)),
        ));
  }

我这里设置了背景颜色,appBar 的主题,通过这样集成,就已经实现了一个简易版的黑白模式。

深入

  1. 眼尖的读者应该发现我上面使用了provider, 我定义了一个全局的 model,管理黑白模式的开关

// 全局数据管理
class GlobalModel with ChangeNotifier {
  bool _isDarkTheme = false;
  bool get isDarkTheme => _isDarkTheme;

  void switchTheme() {
    _isDarkTheme = !_isDarkTheme;
    notifyListeners();
  }
}

MaterialApp 通过监听这个全局 model,进而切换主题。

  1. 对于 themeData 没有支持的样式,提供了主题配色设置的能力
mixin ThemeColorConfig {

  final Map<String, ColorSelector> _colorSelectorMap = Map();

  addColor(String colorKey, {@required Color light, @required Color dark}) {
    _colorSelectorMap[colorKey] = ColorSelector(light, dark);
  }

  getColor(String colorKey) {
    assert( _colorSelectorMap[colorKey] != null, "$colorKey对应的颜色值未配置");
    return _colorSelectorMap[colorKey].color;
  }

  @protected
  // 主题颜色设置
  themeColorsConfig() => {};
}

只要基础页面混合`ThemeColorConfig`, 然后配置好颜色
```dart 
 themeColorsConfig() {
    addColor("darkFont", light: Color(0X4D000000), dark: Color(0X4DFFFFFF));
    addColor("lightFont", light: Color(0XFFFFFFFF), dark: Color(0XFFFFFFFF));
  }

就可以通过getColor("darkFont")取出当前主题的颜色。

注意点

  1. q: 状态栏沉浸式的实现?
    a: 整理方案如下:
    
    // 状态栏背景设置为透明 
    SystemChrome.setSystemUIOverlayStyle(
       SystemUiOverlayStyle(statusBarColor: Colors.transparent));
    
    //brightness属性改变状态栏字体图标颜色 
    //color属性为状态栏颜色
    AppBar(
      color: theme.mainBgColor,
      brightness: isDark ? Brightness.dark : Brightness.light)
    

觉得有帮助的可以点个赞喔😯

总结

Flutter天然支持主题的切换,但是这更依赖团队的开发风格,如果在项目前期对于颜色值等样式的使用有明确的规范和标准,能够抽离出一套主题样式,那么接入主题的切换,也就是水到渠成的事情。