01. 鸿蒙ArkUI实战:用状态驱动构建自适应待办面板

一、为什么用一个“小而完整”的待办面板练习鸿蒙 ArkUI
在鸿蒙 PC 场景中,页面不是把手机界面横向拉宽即可。窗口变大后,用户通常希望同时看到分类入口、当前任务和完成进度;如果还依赖多层跳转,操作成本反而会上升。
待办面板正好覆盖一个业务页面最常见的闭环:数据建模 → 页面状态 → 分类筛选 → 列表渲染 → 用户交互 → 状态回写。示例包含以下能力:
- 左侧显示“全部、进行中、已完成”三个筛选入口;
- 右侧用任务卡片展示标题与完成状态;
- 点击分类后只展示符合条件的任务;
- 勾选复选框后,任务文本颜色和删除线随状态更新;
- 采用“左导航 + 右内容”的宽屏布局,让页面层级保持清晰。
它不是要替代完整的任务管理产品,而是提供一个可以继续扩展的 ArkUI 页面骨架。后续接入本地持久化、网络数据、搜索和任务创建时,不需要推翻核心状态结构。
二、先确定数据边界:只保存源数据和筛选条件
每条任务只保留三个字段:唯一标识 id、展示用 title 和完成状态 done。页面状态中只维护原始任务数组 todos 与筛选条件 filter:
interface TodoItem {
id: number
title: string
done: boolean
}
@State todos: TodoItem[] = [
{ id: 1, title: '整理鸿蒙 PC 文章大纲', done: false },
{ id: 2, title: '运行示例并截取页面', done: false },
{ id: 3, title: '检查发布前质量分', done: true }
]
@State filter: string = '全部'
这里有一个容易被忽略的原则:不要把“全部列表”“进行中列表”“已完成列表”分别存成三份状态。 三份列表一旦同时存在,勾选任务后就必须同步修改多个位置,很容易出现计数不一致或切换分类后显示旧数据的问题。
更稳妥的做法是只保存源数据,再根据当前筛选条件推导出页面需要展示的数据:
private get visibleTodos(): TodoItem[] {
if (this.filter === '进行中') return this.todos.filter(item => !item.done)
if (this.filter === '已完成') return this.todos.filter(item => item.done)
return this.todos
}
visibleTodos 是从 todos 和 filter 计算出来的结果,不承担额外的数据写入职责。这样无论用户先切换筛选还是先勾选任务,最终界面都从同一个源数组推导,逻辑更容易验证。
三、完整页面代码:导航、列表和交互放在同一个状态闭环里
将下面代码放入 ArkTS Stage 工程首页的 Index.ets 后,可以作为页面原型继续扩展:
interface TodoItem {
id: number
title: string
done: boolean
}
@Entry
@Component
struct Index {
@State todos: TodoItem[] = [
{ id: 1, title: '整理鸿蒙 PC 文章大纲', done: false },
{ id: 2, title: '运行示例并截取页面', done: false },
{ id: 3, title: '检查发布前质量分', done: true }
]
@State filter: string = '全部'
private get visibleTodos(): TodoItem[] {
if (this.filter === '进行中') return this.todos.filter(item => !item.done)
if (this.filter === '已完成') return this.todos.filter(item => item.done)
return this.todos
}
build() {
Row() {
this.Sidebar()
this.ContentPanel()
}
.width('100%')
.height('100%')
.backgroundColor('#F6F8FA')
}
@Builder Sidebar() {
Column({ space: 12 }) {
Text('我的待办')
.fontSize(24)
.fontWeight(FontWeight.Bold)
ForEach(['全部', '进行中', '已完成'], (item: string) => {
Text(item)
.width('100%')
.padding(12)
.borderRadius(8)
.backgroundColor(this.filter === item ? '#DCEBFF' : '#FFFFFF')
.onClick(() => this.filter = item)
})
}
.width(180)
.height('100%')
.padding(20)
.backgroundColor('#FFFFFF')
}
@Builder ContentPanel() {
Column({ space: 16 }) {
Text(this.filter)
.fontSize(28)
.fontWeight(FontWeight.Bold)
Text(`共 ${this.visibleTodos.length} 条任务`)
.fontSize(14)
.fontColor('#667085')
List({ space: 10 }) {
ForEach(this.visibleTodos, (item: TodoItem) => {
ListItem() {
Row({ space: 12 }) {
Checkbox({ name: String(item.id), group: 'todos' })
.select(item.done)
.onChange((value: boolean) => this.toggleTodo(item.id, value))
Text(item.title)
.fontSize(17)
.fontColor(item.done ? '#98A2B3' : '#1D2939')
.decoration({ type: item.done ? TextDecorationType.LineThrough : TextDecorationType.None })
}
.width('100%')
.padding(18)
.borderRadius(12)
.backgroundColor('#FFFFFF')
}
}, (item: TodoItem) => item.id)
}
.layoutWeight(1)
}
.layoutWeight(1)
.height('100%')
.padding({ left: 32, right: 32, top: 28, bottom: 28 })
}
private toggleTodo(id: number, done: boolean): void {
this.todos = this.todos.map(item => item.id === id ? { ...item, done } : item)
}
}
四、三个关键实现点
1. 用 @State 管理会影响界面的数据
todos 和 filter 都会直接影响 UI:前者决定任务卡片内容和勾选状态,后者决定展示范围。因此它们适合作为页面状态。点击分类时只需要更新 filter;列表读取 visibleTodos 后就会根据新的条件展示对应任务。
状态粒度要适中。把每一个派生结果也放入状态,不会让页面更“响应式”,反而会让同步关系变复杂。保留最少、最权威的状态,能降低维护成本。
2. 更新任务时返回新数组,而不是隐式修改旧对象
勾选复选框时,示例通过 map 生成新的任务数组:
private toggleTodo(id: number, done: boolean): void {
this.todos = this.todos.map(item => item.id === id ? { ...item, done } : item)
}
这段代码有两个好处:第一,只有 id 相同的目标任务会被替换;第二,最终会把新的数组赋回 @State 字段,状态变化的入口非常明确。以后如果要记录修改时间、增加撤销操作或写入持久化层,也能在这个方法里集中处理。
3. 宽屏布局先保证信息层级,再考虑视觉细节
左侧导航使用固定宽度,右侧内容区通过 layoutWeight(1) 占据剩余空间。这个组合的目的不是追求复杂布局,而是让用户一眼看清三类信息:
- 当前位置:左侧哪个筛选项被选中;
- 当前范围:右侧标题与“共 X 条任务”;
- 可执行内容:列表中的任务和复选框。
任务卡片采用留白、圆角和弱对比背景,已完成任务降低文字对比度并加删除线。即使任务数量增加,阅读顺序也不会混乱。
五、真实运行前的验证清单
下面步骤用于得到可用于文章的真实运行截图。在未完成这些步骤前,不应把页面描述为“运行成功”。
- 在 DevEco Studio 创建或打开 ArkTS Stage 工程;
- 用本文代码替换首页
Index.ets,处理工程模板与 SDK 版本可能带来的编译提示; - 将工程运行到可用的鸿蒙 PC 模拟器或真实设备;
- 截取完整初始布局:左侧导航、右侧标题、统计文字和三张任务卡都应可见;
- 点击“进行中”,确认列表只保留未完成任务,再截取筛选状态;
- 勾选一条未完成任务,确认任务从“进行中”列表移除,或在“全部”中呈现完成样式,再截取交互后的状态;
- 对照截图与本文任务文案、数量和交互说明,确认一致后再插入 CSDN 正文。
真实运行截图

六、可以继续扩展什么
当页面原型完成真实验证后,可以按业务优先级继续增加:
- 创建任务:用输入框或弹窗生成新的
TodoItem; - 搜索任务:把搜索关键字作为另一个状态,并与
filter共同推导展示列表; - 持久化:把任务数组保存到本地数据层,在页面初始化时恢复;
- 任务优先级:在数据模型中加入优先级字段,并通过颜色或排序强化信息层级;
- 跨设备能力:在明确业务需求和数据一致性策略后,再考虑同步机制。
扩展时仍应遵守同一原则:源数据尽量单一,界面结果尽量通过源数据和当前条件推导,而不是在多个组件之间复制状态。
七、小结
这个鸿蒙 ArkUI 待办面板的重点不是功能数量,而是形成一条可复用的页面开发路径:数据建模 → 状态保存 → 派生筛选 → 列表渲染 → 交互更新 → 真实运行验证。
只要保持状态来源清晰,后续无论是增加搜索、创建任务还是接入网络数据,都可以在现有结构上继续演进。发布前最后一步也不能省略:必须用真实设备或模拟器运行项目、截取与本文描述一致的画面,并把截图嵌入文章正文。
更多推荐




所有评论(0)