欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net

Flutter Dark Mode 在 OpenHarmony 系统中的适配实践

前言

最近在做 Flutter for OpenHarmony 项目时,深色模式适配这块花的时间比我预想中要多。

一开始我以为:

Flutter 本身已经支持 ThemeMode 了,适配暗黑模式应该不复杂。

结果真正接入 OpenHarmony 真机之后,还是遇到了一堆细节问题。

包括:

  • 系统深色模式切换后页面不更新
  • 部分组件颜色异常
  • 状态栏颜色不统一
  • 页面切换时闪白
  • 图片资源不适配暗色主题

尤其是多个页面叠加后,颜色管理会越来越乱。

后面我重新整理了一套主题管理方案,整体体验才稳定下来。

这篇文章主要记录一下我这次在 OpenHarmony 项目里的 Dark Mode 适配过程。


一、为什么现在必须适配 Dark Mode

现在大部分系统都已经默认支持深色模式。

包括:

  • Android
  • iOS
  • OpenHarmony

如果应用完全不适配:

用户体验会比较割裂。

尤其晚上使用时:

亮白页面会特别突兀。


我项目里的实际场景

当前项目包括:

  • 首页
  • 商品列表
  • IM 聊天
  • 用户中心
  • WebView 页面

很多页面一开始都是直接写死颜色:


Container(
  color: Colors.white,
)

结果系统切暗黑模式后:

页面直接“半黑半白”。


二、Flutter ThemeMode 基础接入

Flutter 本身已经提供了主题切换方案。


MaterialApp 配置


MaterialApp(

  theme: lightTheme,

  darkTheme: darkTheme,

  themeMode: ThemeMode.system,
)

这里:


ThemeMode.system

表示:

跟随系统主题。


三、浅色主题与深色主题拆分

后面我把所有主题单独拆了。


light_theme.dart


final ThemeData lightTheme = ThemeData(

  brightness: Brightness.light,

  scaffoldBackgroundColor: Colors.white,

  appBarTheme: const AppBarTheme(
    backgroundColor: Colors.white,
  ),
);

dark_theme.dart


final ThemeData darkTheme = ThemeData(

  brightness: Brightness.dark,

  scaffoldBackgroundColor: Colors.black,

  appBarTheme: const AppBarTheme(
    backgroundColor: Colors.black,
  ),
);

这样后面维护会轻松很多。


四、不要直接写死颜色

这个是我这次适配里踩得最多的坑。


之前的代码


Text(
  "用户信息",
  style: TextStyle(
    color: Colors.black,
  ),
)

暗黑模式下:

文字直接看不见。


后来的方案

统一改成:


Text(
  "用户信息",

  style: TextStyle(
    color: Theme.of(context)
        .colorScheme
        .onBackground,
  ),
)

或者:


Theme.of(context).textTheme

统一从主题读取颜色。

后面适配会轻松很多。


五、OpenHarmony 上的一个实际问题

这个问题我在鸿蒙真机上复现得挺明显。


问题现象

系统已经切成暗黑模式。

但 Flutter 页面:

没有实时更新。

需要:

  • 重启应用
  • 或重新进入页面

主题才生效。


后来的处理方式

监听页面亮度变化。


WidgetsBindingObserver

监听:


didChangePlatformBrightness()

@override
void didChangePlatformBrightness() {

  final brightness =
      WidgetsBinding.instance
          .platformDispatcher
          .platformBrightness;

  print(brightness);
}

然后主动更新 ThemeMode。

这个在 OpenHarmony 上会稳定很多。


六、状态栏颜色适配

这个问题很多人容易忽略。


问题

页面已经切暗色。

但状态栏:

还是黑字。

结果顶部直接看不清。


解决方案


SystemChrome.setSystemUIOverlayStyle(

  const SystemUiOverlayStyle(

    statusBarColor: Colors.transparent,

    statusBarIconBrightness:
        Brightness.light,
  ),
);

浅色模式:


Brightness.dark

深色模式:


Brightness.light

这个在 OpenHarmony 真机里效果挺明显。


七、图片资源适配

这个也是后面项目里很容易出问题的地方。


问题

深色模式下:

原本的黑色图标直接“消失”。

例如:

  • 返回按钮
  • tab 图标
  • 功能按钮

后来的方案

分别维护:


icon_dark.png
icon_light.png

根据主题切换。


isDark
  ? "dark/icon.png"
  : "light/icon.png"

虽然麻烦一点。

但实际效果会好很多。


八、页面切换闪白问题

这个问题在 OpenHarmony 上我复现概率挺高。


问题现象

暗黑模式下:

页面 push/pop 时:

偶发白色闪屏。


原因

部分页面:


Scaffold

背景色没配置。

Flutter 默认会使用浅色背景。


解决方式

统一配置:


scaffoldBackgroundColor

同时:

页面不要直接:


Container(color: Colors.white)

这个优化后:

闪白问题减少很多。


九、Provider 管理主题状态

后面我把主题统一放到了 Provider。


ThemeProvider


class ThemeProvider
    extends ChangeNotifier {

  ThemeMode themeMode =
      ThemeMode.system;

  void changeTheme(
      ThemeMode mode) {

    themeMode = mode;

    notifyListeners();
  }
}

页面切换:


context
    .read<ThemeProvider>()
    .changeTheme(
      ThemeMode.dark,
    );

后面:

  • 设置页
  • 系统跟随
  • 手动切换

都方便很多。


十、缓存主题状态

这个我建议一定做。


为什么

否则:

每次重新打开 App。

主题都会恢复默认。


我的方案

使用:


shared_preferences

保存:


await prefs.setString(
  "theme",
  "dark",
);

启动时恢复。

目前在 OpenHarmony 上没发现问题。


十一、实际效果

优化前:

问题 表现
深色模式适配 不完整
页面闪白 存在
状态栏颜色 异常
图标显示 混乱

优化后:

项目 效果
主题切换 更稳定
页面体验 更统一
暗黑适配 基本完整
页面切换 更自然

目前这套方案已经在项目里稳定用了挺长时间。


十二、总结

这次做 Flutter Dark Mode 在 OpenHarmony 下的适配,一个比较明显的感受是:

深色模式真正麻烦的地方:

其实不是 ThemeMode。

而是:

  • 全局颜色管理
  • 页面细节统一
  • 图标资源
  • 状态栏
  • 页面生命周期

尤其项目越来越大之后:

如果前期没有统一主题规范。

后面补 Dark Mode 会非常痛苦。

我现在项目里的原则基本是:

  • 不直接写死颜色
  • 所有颜色统一 Theme 管理
  • 图标区分明暗资源
  • 页面背景统一配置
  • 状态栏跟随主题变化

整体体验会稳定很多。

后面我还准备继续研究:

  • Material 3 动态主题
  • OpenHarmony 系统主题同步
  • Flutter 动态换肤
  • 多主题架构设计

希望这篇文章能给正在做 Flutter for OpenHarmony 开发的同学一点参考。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐