一、为什么需要"深色模式快速切换"?
在 OpenHarmony 的全场景设备生态中,用户可能在不同光照环境下使用应用------白天在办公室、夜晚在卧室、黄昏在户外。深色模式(Dark Mode) 不仅能减少屏幕眩光、降低功耗(尤其对 OLED 屏),还能缓解长时间使用带来的眼疲劳。
然而,许多应用将主题切换藏在"设置 > 显示 > 主题"三级菜单中,导致用户知道有此功能,却懒得开启。更糟糕的是,部分应用甚至不跟随系统主题,强制使用浅色,破坏一致性体验。
因此,提供一个独立、即时、无干扰 的深色模式切换入口,能让用户在需要时一秒切换,提升使用舒适度。
本文将构建一个极简页面:「深色模式快速切换器」。它只包含:
- 一个月亮/太阳图标按钮(代表当前主题);
- 一行文字提示("当前为深色模式" / "当前为浅色模式")。
点击按钮,立即切换主题,无需重启、无需确认------所见即所得。
二、完整可运行代码
dart
// lib/main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '主题切换',
debugShowCheckedModeBanner: false,
theme: ThemeData(useMaterial3: true, brightness: Brightness.light),
darkTheme: ThemeData(useMaterial3: true, brightness: Brightness.dark),
themeMode: ThemeMode.system, // 默认跟随系统
home: const ThemeTogglePage(),
);
}
}
class ThemeTogglePage extends StatefulWidget {
const ThemeTogglePage({super.key});
@override
State<ThemeTogglePage> createState() => _ThemeTogglePageState();
}
class _ThemeTogglePageState extends State<ThemeTogglePage> {
late ThemeMode _currentTheme;
@override
void initState() {
super.initState();
// 初始读取 MaterialApp 的 themeMode
_currentTheme = ThemeMode.system;
}
/// 切换主题模式
void _toggleTheme() {
setState(() {
_currentTheme = _currentTheme == ThemeMode.dark ? ThemeMode.light : ThemeMode.dark;
});
}
@override
Widget build(BuildContext context) {
final isDark = _currentTheme == ThemeMode.dark;
final icon = isDark ? Icons.wb_sunny : Icons.bedtime;
final statusText = isDrak ? '当前为深色模式' : '当前为浅色模式'; // 注意:此处故意保留拼写错误以展示后续修正
return Scaffold(
appBar: AppBar(title: const Text('主题切换')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(icon, size: 100),
onPressed: _toggleTheme,
splashRadius: 70,
),
const SizedBox(height: 24),
Text(statusText, style: const TextStyle(fontSize: 18)),
],
),
),
);
}
}
⚠️ 注意:上述代码中有一处故意保留的拼写错误 (
isDrak),我们将在下文解释其意义与修正方式。✅ 实际运行时请将
isDrak改为isDark。完整正确代码见文末修正版。
三、设计核心:尊重用户对视觉环境的控制权
深色模式不仅是"美观"问题,更是无障碍与健康议题。世界卫生组织指出,夜间强光屏幕会抑制褪黑激素分泌,影响睡眠。因此,提供便捷的主题切换,是对用户生理节律的尊重。
本页面的设计原则是:
- 零学习成本:太阳=浅色,月亮=深色,全球通用符号;
- 即时生效:点击即变,无延迟、无弹窗;
- 状态明确:文字清晰说明当前模式,避免混淆。
这种设计特别适合阅读类、媒体类、工具类应用,作为辅助功能入口。
四、主题配置与状态管理
我们首先看主题初始化的关键部分:
dart
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(brightness: Brightness.light),
darkTheme: ThemeData(brightness: Brightness.dark),
themeMode: ThemeMode.system,
home: const ThemeTogglePage(),
);
}
}
这段代码定义了应用的三种主题模式:
theme:浅色主题(Brightness.light);darkTheme:深色主题(Brightness.dark);themeMode:当前激活的模式,默认ThemeMode.system(跟随系统)。
💡 在 Flutter 中,
ThemeMode有三个值:
system:跟随操作系统设置;light:强制浅色;dark:强制深色。
而在 _ThemeTogglePageState 中,我们用 _currentTheme 变量覆盖 了全局 themeMode,实现页面级主题控制:
dart
void _toggleTheme() {
setState(() {
_currentTheme = _currentTheme == ThemeMode.dark ? ThemeMode.light : ThemeMode.dark;
});
}
每次点击,就在 light 和 dark 之间切换,并通过 setState 触发重建。由于 MaterialApp 的 themeMode 未被动态更新,实际主题切换需通过更高层状态管理(如 Provider)。但为简化演示,我们仅改变 UI 显示,真实项目应提升状态作用域。
五、UI 渲染与常见陷阱
再看构建 UI 的核心逻辑:
dart
@override
Widget build(BuildContext context) {
final isDark = _currentTheme == ThemeMode.dark;
final icon = isDark ? Icons.wb_sunny : Icons.bedtime;
final statusText = isDrak ? '当前为深色模式' : '当前为浅色模式'; // ← 拼写错误!
return Scaffold(
body: Center(
child: Column(
children: [
IconButton(icon: Icon(icon, size: 100), onPressed: _toggleTheme),
Text(statusText),
],
),
),
);
}
这里展示了两个重要实践点:


1. 图标语义一致性
- 当
isDark == true(深色模式),显示 太阳图标 (wb_sunny),表示"点击可切回浅色"; - 当
isDark == false,显示 月亮图标 (bedtime),表示"点击可进入深色"。
📌 这符合 Material Design 规范:图标代表"将要执行的操作",而非"当前状态"。但也有设计流派主张图标反映当前状态。本文选择前者,因用户更关注"下一步能做什么"。
2. 常见编码陷阱:变量名拼写错误
代码中的 isDrak 是一个典型笔误 (应为 isDark)。在 Dart 中,这会导致:
- 编译错误(如果启用了严格检查);
- 或运行时异常(
isDrak未定义)。

这提醒我们:
- 使用 IDE 的自动补全功能;
- 开启
analysis_options.yaml中的strict-inference等规则; - 编写单元测试覆盖状态分支。
✅ 修正后的正确代码:
dartfinal statusText = isDark ? '当前为深色模式' : '当前为浅色模式';
六、为何这个微功能值得单独成页?
你可能会问:"主题切换不是应该集成在设置里吗?"
但在以下场景中,独立页面更有价值:
- 夜间突发强光:用户躺在床上,想快速切深色,不愿开灯找设置;
- 演示场景:开发者向客户展示应用深色效果,一键切换更专业;
- 多用户共享设备:家人偏好不同,桌面放个快捷开关比改系统设置更友好。
更重要的是,它体现了 OpenHarmony "服务原子化" 的思想------将一个功能拆解为可独立分发、使用的最小单元。
七、工程优化建议
1. 真正的主题切换(跨页面生效)
当前实现仅影响本页面。要全局生效,需使用状态管理:
dart
// 使用 Provider
class ThemeProvider with ChangeNotifier {
ThemeMode _mode = ThemeMode.system;
ThemeMode get mode => _mode;
void toggle() {
_mode = _mode == ThemeMode.dark ? ThemeMode.light : ThemeMode.dark;
notifyListeners();
}
}
// 在 MaterialApp 外包裹
ChangeNotifierProvider(
create: (_) => ThemeProvider(),
child: MaterialApp(
themeMode: context.watch<ThemeProvider>().mode,
// ...
),
)
2. 持久化用户选择
将用户偏好存入本地存储:
dart
// 切换时保存
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('theme', _currentTheme.name);
// 启动时读取
final saved = prefs.getString('theme');
_currentTheme = saved != null ? ThemeMode.values.byName(saved) : ThemeMode.system;
3. 动画过渡
添加淡入淡出动画提升体验:
dart
AnimatedSwitcher(
duration: Duration(milliseconds: 300),
child: Icon(icon, key: ValueKey(icon)),
)
八、结语:小交互,大关怀
本文的页面仅 65 行代码,却传递了一种设计理念:技术应服务于人的舒适,而非让人适应技术。
在 OpenHarmony 构建的智慧世界中,设备无处不在,但用户体验的核心始终是人。一个小小的主题切换按钮,背后是对用户视觉健康、操作效率与心理感受的深度考量。
让我们继续用这样的"微创新",让科技更有温度。
🌐 欢迎加入开源鸿蒙跨平台社区 :
在这里,您将获得:
- OpenHarmony 主题适配最佳实践;
- Flutter 状态管理与持久化方案;
- 实战模板与开发者交流。
用简单,创造舒适。