WithTheme 自学指南:玩转局部主题和局部深浅色
在 ArkUI 里,做主题和平时做样式是两件事: 从 API Version 12 开始,ArkUI 提供了一个专门做「局部主题」的组件: 这篇文章就是一份可以直接上手的 WithTheme 自学指南,适合发社区、做笔记或带项目里落地。 官方定义很简单: 只负责两件事: 基础信息: 不是所有组件都会响应 WithTheme,这点很关键。当前支持的系统组件包括: 选择器类: 状态展示类: 很多页面希望做到: 这时可以用 要让深浅色生效,先准备深色资源文件 使用建议: 除了深浅色,有时我们希望整块区域用一套品牌色,比如「绿色主题卡片」vs「红色活动卡片」。 这时用 下面这个例子展示了一个典型的用法: 效果: 结合上面的能力,WithTheme 很适合这些场景: 局部夜间模式 卡片级换肤 / 品牌卡片 表单区域统一风格 多主题 Demo / 设置页 子组件只能一个 不是所有组件都响应主题 内部写死颜色会覆盖部分主题 深浅色看起来没变化? 掌握它之后,你可以在 ArkUI 里轻松实现:fontColor、backgroundColor;WithTheme。
它不负责画 UI,只负责一件事:给作用域里的组件套一层主题/深浅色规则。一、WithTheme 是什么?
WithTheme 是一个主题作用域容器;Column / Row / 自定义组件);theme);colorMode)。SystemCapability.ArkUI.ArkUI.Full;二、WithTheme 能影响哪些组件?
TextInput、SearchButton、Badge、CounterSwiper、Select、MenuTextTimePicker、DatePicker、TextPickerCheckbox、CheckboxGroup、RadioSliderProgress、Toggle、PatternLock、QRCodeDivider简单记:表单控件 + 按钮 + 文本 + 分隔线,大部分能跟着 WithTheme 一起变。
三、核心接口与配置项
3.1 WithTheme 基本接口
WithTheme(options: WithThemeOptions) {
// 只能有一个子组件
// 这个子组件里面可以再写 Column/Row/自定义组件
}注意:WithTheme 不支持通用属性和通用事件,需要把布局、点击等逻辑写在内部组件上。
3.2 WithThemeOptions 结构
interface WithThemeOptions {
theme?: CustomTheme // 自定义主题配色
colorMode?: ThemeColorMode // 深浅色模式
}theme?: CustomThemeundefined,表示跟随系统 token 默认样式。colorMode?: ThemeColorModeThemeColorMode.SYSTEM(跟随系统)。3.3 CustomTheme 类型
type CustomTheme = CustomThemeCustomTheme 实际上是一个接口;CustomColors 一起使用,用来描述一整套颜色体系(比如一套绿色主题、一套红色主题)。四、局部深浅色:colorMode 实战
WithTheme 搭配 colorMode。4.1 深浅色资源准备:dark.json

dark.json,例如:{
"color": [
{
"name": "start_window_background",
"value": "#000000"
}
]
}4.2 示例:同一页面展示默认、Dark、Light 三种区域

@Entry
@Component
struct Index {
build() {
Column() {
// ① 系统默认区域
Column() {
Text('无WithTheme')
.fontSize(40)
.fontWeight(FontWeight.Bold)
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('33%')
.backgroundColor($r('app.color.start_window_background'))
// ② 局部强制深色模式
WithTheme({ colorMode: ThemeColorMode.DARK }) {
Column() {
Text('WithTheme')
.fontSize(40)
.fontWeight(FontWeight.Bold)
Text('DARK')
.fontSize(40)
.fontWeight(FontWeight.Bold)
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('33%')
.backgroundColor($r('sys.color.background_primary'))
}
// ③ 局部强制浅色模式
WithTheme({ colorMode: ThemeColorMode.LIGHT }) {
Column() {
Text('WithTheme')
.fontSize(40)
.fontWeight(FontWeight.Bold)
Text('LIGHT')
.fontSize(40)
.fontWeight(FontWeight.Bold)
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('33%')
.backgroundColor($r('sys.color.background_primary'))
}
}
.height('100%')
.expandSafeArea(
[SafeAreaType.SYSTEM],
[SafeAreaEdge.TOP, SafeAreaEdge.END, SafeAreaEdge.BOTTOM, SafeAreaEdge.START]
)
}
}WithTheme({ colorMode: ThemeColorMode.DARK });ThemeColorMode.LIGHT;五、自定义主题:CustomTheme + CustomColors 实战
CustomTheme 来定义一套颜色,然后交给 WithTheme。5.1 定义颜色集合 CustomColors
import { CustomTheme, CustomColors } from '@kit.ArkUI';
class GreenColors implements CustomColors {
fontPrimary = '#ff049404';
fontEmphasize = '#FF00541F';
fontOnPrimary = '#FFFFFFFF';
compBackgroundTertiary = '#1111FF11';
backgroundEmphasize = '#FF00541F';
compEmphasizeSecondary = '#3322FF22';
}
class RedColors implements CustomColors {
fontPrimary = '#fff32b3c';
fontEmphasize = '#FFD53032';
fontOnPrimary = '#FFFFFFFF';
compBackgroundTertiary = '#44FF2222';
backgroundEmphasize = '#FFD00000';
compEmphasizeSecondary = '#33FF1111';
}实际项目里可以按照设计给的 token 表来映射,保持命名和 UI 视觉规范一致。
5.2 封装成 CustomTheme
class PageCustomTheme implements CustomTheme {
colors?: CustomColors
constructor(colors: CustomColors) {
this.colors = colors
}
}5.3 使用 WithTheme 控制局部主题
上半部分使用系统默认按钮配色;
下半部分被 WithTheme 包裹,使用可切换的自定义主题。@Entry
@Component
struct IndexPage {
static readonly themeCount = 3;
themeNames: string[] = ['System', 'Custom (green)', 'Custom (red)'];
themeArray: (CustomTheme | undefined)[] = [
undefined, // 系统默认主题
new PageCustomTheme(new GreenColors()), // 绿色主题
new PageCustomTheme(new RedColors()) // 红色主题
]
@State themeIndex: number = 0;
build() {
Column() {
// 区域一:未使用 WithTheme,系统默认配色
Column({ space: '8vp' }) {
Text('未使用WithTheme')
// 点击切换下方 WithTheme 的配色
Button(`切换theme配色:${this.themeNames[this.themeIndex]}`)
.onClick(() => {
this.themeIndex = (this.themeIndex + 1) % IndexPage.themeCount;
})
// 系统默认按钮配色
Button('Button.style(NORMAL) with System Theme')
.buttonStyle(ButtonStyleMode.NORMAL)
Button('Button.style(EMP..ED) with System Theme')
.buttonStyle(ButtonStyleMode.EMPHASIZED)
Button('Button.style(TEXTUAL) with System Theme')
.buttonStyle(ButtonStyleMode.TEXTUAL)
}
.margin({ top: '50vp' })
// 区域二:使用 WithTheme,局部换肤
WithTheme({ theme: this.themeArray[this.themeIndex] }) {
Column({ space: '8vp' }) {
Text('使用WithTheme')
Button('Button.style(NORMAL) with Custom Theme')
.buttonStyle(ButtonStyleMode.NORMAL)
Button('Button.style(EMP..ED) with Custom Theme')
.buttonStyle(ButtonStyleMode.EMPHASIZED)
Button('Button.style(TEXTUAL) with Custom Theme')
.buttonStyle(ButtonStyleMode.TEXTUAL)
}
.width('100%')
}
}
}
}六、常见使用场景
WithTheme({ colorMode: ThemeColorMode.DARK }) {
// 播放控制区 / 评论列表
}WithTheme({ theme: new PageCustomTheme(new GreenColors()) }) {
// 活动卡片 / 会员卡片布局
}七、容易踩的点 & 调试建议
Column/Row/自定义组件包一层。backgroundColor('#FF0000');buttonStyle、fontColor + 主题,让主题主导,而不是全部手写 Hex。dark.json 等资源;Text / Button 观察效果。八、总结
WithTheme 的定位可以一句话概括:内外解耦:全局主题搞整体,WithTheme 专门做“局部换肤 + 局部深浅色”。