HarmonyOS7 创意实战:深色/浅色主题切换的丝滑实现
文章目录
前言
深色模式已经从"可选"变成了"标配"。用户在晚上刷手机的时候,一个白得刺眼的页面能直接把好感度打到零。iOS 和 Android 都原生支持深色模式,HarmonyOS 也不例外。但很多场景下,我们需要在应用内提供手动切换的能力,让用户自己选。
这篇文章实现一个深色/浅色主题的实时切换效果,带过渡动画,所有元素的颜色同步变化。
灵感来源
很多优秀应用都有主题切换功能——微信的"深色模式"、知乎的"夜间模式"、GitHub 的 Dark Theme。它们的共同特点是:一键切换、全局生效、过渡平滑。这个案例就模仿这种体验。
设计思路

主题切换的核心方案有两种:
| 方案 | 原理 | 优缺点 |
|---|---|---|
| 系统跟随 | 使用 $r() 资源引用,跟随系统配置 |
简单但不能手动切换 |
| 状态驱动 | @State isDarkMode 控制所有颜色值 |
灵活,可手动切换 |
| 配置驱动 | 定义主题对象,整体切换 | 扩展性好但复杂度高 |
这个案例用状态驱动方案——一个 @State isDarkMode: boolean 控制所有 UI 元素的颜色。简单直接,适合中小规模项目。
实现过程
主题色彩定义

先定义深色和浅色两套颜色方案:
浅色模式:
- 背景色: #F5F6FA
- 卡片色: #FFFFFF
- 主文本: #1A1A1A / #333333
- 次文本: #666666
- 分隔线: #F0F0F0
深色模式:
- 背景色: #121212
- 卡片色: #1E1E1E
- 主文本: #FFFFFF / #CCCCCC
- 次文本: #999999
- 分隔线: #333333
核心切换逻辑
用一个 Toggle 开关控制 isDarkMode 状态:
Toggle({ type: ToggleType.Switch, isOn: $$this.isDarkMode })
.selectedColor('#4D96FF')
然后在每个 UI 元素上,根据 this.isDarkMode 选择对应颜色:
Text('设置项')
.fontColor(this.isDarkMode ? '#CCCCCC' : '#1A1A1A')
Column()
.backgroundColor(this.isDarkMode ? '#1E1E1E' : '#FFFFFF')
添加过渡动画
关键来了——加上 .animation() 让颜色变化有平滑过渡:
Column()
.backgroundColor(this.isDarkMode ? '#1E1E1E' : '#FFFFFF')
.animation({ duration: 300, curve: Curve.EaseInOut })
这样切换时颜色不会"啪"一下变掉,而是用 300ms 平滑过渡。
完整页面代码
@Entry
@Component
struct ThemeSwitchPage {
@State isDarkMode: boolean = false
build() {
Column() {
Column() {
Row() {
Text('主题切换')
.fontSize(18).fontWeight(FontWeight.Bold)
.fontColor(this.isDarkMode ? '#FFFFFF' : '#1A1A1A')
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: $$this.isDarkMode })
.selectedColor('#4D96FF')
}
.width('100%')
Scroll() {
Column() {
// 设置列表卡片
Column() {
Text('设置项')
.fontSize(14).fontWeight(FontWeight.Medium)
.fontColor(this.isDarkMode ? '#CCCCCC' : '#1A1A1A')
.margin({ bottom: 12 })
ForEach([
'个人资料', '账号安全', '通知设置',
'隐私权限', '通用设置', '关于应用'
], (item: string) => {
Row() {
Text(item)
.fontSize(14)
.fontColor(this.isDarkMode ? '#CCCCCC' : '#333333')
.layoutWeight(1)
Text('>')
.fontSize(14)
.fontColor(this.isDarkMode ? '#666666' : '#CCCCCC')
}
.width('100%').padding(14)
.border({
width: { bottom: 0.5 },
color: this.isDarkMode ? '#333333' : '#F0F0F0'
})
})
}
.width('100%')
.backgroundColor(this.isDarkMode ? '#1E1E1E' : '#FFFFFF')
.borderRadius(12).padding(16)
.animation({ duration: 300, curve: Curve.EaseInOut })
// 预览卡片
Column() {
Text('主题预览卡片')
.fontSize(14).fontWeight(FontWeight.Medium)
.fontColor(this.isDarkMode ? '#CCCCCC' : '#1A1A1A')
.margin({ bottom: 12 })
Column() {
Text('卡片标题')
.fontSize(16).fontWeight(FontWeight.Bold)
.fontColor(this.isDarkMode ? '#FFFFFF' : '#1A1A1A')
Text('这是卡片的描述内容,展示主题切换时文字颜色的变化')
.fontSize(13)
.fontColor(this.isDarkMode ? '#999999' : '#666666')
.margin({ top: 6 })
Button('操作按钮')
.width('100%').margin({ top: 12 })
}
.width('100%')
.backgroundColor(this.isDarkMode ? '#2A2A2A' : '#F8F9FA')
.borderRadius(12).padding(16)
}
.width('100%')
.backgroundColor(this.isDarkMode ? '#1E1E1E' : '#FFFFFF')
.borderRadius(12).padding(16).margin({ top: 10 })
.animation({ duration: 300, curve: Curve.EaseInOut })
}
.width('100%')
}
.layoutWeight(1)
}
.width('100%').height('100%')
.backgroundColor(this.isDarkMode ? '#121212' : '#F5F6FA')
.padding(16)
.animation({ duration: 300, curve: Curve.EaseInOut })
}
.width('100%').height('100%')
.backgroundColor(this.isDarkMode ? '#121212' : '#F5F6FA')
}
}
效果调优
三元表达式太多怎么办
页面里到处都是 this.isDarkMode ? 'darkColor' : 'lightColor' 这种三元表达式,看着很乱。可以封装一个主题管理器:
class ThemeManager {
static colors(isDark: boolean) {
return {
bg: isDark ? '#121212' : '#F5F6FA',
card: isDark ? '#1E1E1E' : '#FFFFFF',
primaryText: isDark ? '#FFFFFF' : '#1A1A1A',
secondaryText: isDark ? '#999999' : '#666666',
divider: isDark ? '#333333' : '#F0F0F0',
subCard: isDark ? '#2A2A2A' : '#F8F9FA',
}
}
}
// 使用
const theme = ThemeManager.colors(this.isDarkMode)
Text('标题').fontColor(theme.primaryText)
Column().backgroundColor(theme.card)
这样代码清爽很多,颜色值也集中管理了。
配合 @Provide 全局共享主题
把 isDarkMode 用 @Provide 共享,所有后代组件都能消费:
@Entry
@Component
struct AppRoot {
@Provide isDarkMode: boolean = false
// ...
}
@Component
struct AnyChildComponent {
@Consume isDarkMode: boolean
// 直接使用 this.isDarkMode
}
脑洞扩展
多主题支持
不只是深色/浅色,还可以做"护眼模式"、"高对比度"等:
enum ThemeType { Light, Dark, EyeProtection, HighContrast }
@State currentTheme: ThemeType = ThemeType.Light
记住用户选择
用 @StorageLink 持久化主题选择:
@StorageLink('app_theme') isDarkMode: boolean = false
下次打开应用自动应用用户上次的选择。
跟随系统
监听系统深色模式变化:
aboutToAppear(): void {
const config = getContext(this).config
this.isDarkMode = config.colorMode === ColorMode.COLOR_MODE_DARK
}
写在最后
主题切换是提升应用品质的一个低成本高回报功能。用一个 @State 变量驱动所有颜色值,加上 animation 做平滑过渡,就能实现丝滑的深浅色切换。如果项目规模较大,建议封装一个主题管理器,把颜色值集中定义和管理。
更多推荐



所有评论(0)