从零开发一个鸿蒙待办事项 App
📅 2025-05-07 · ⏱ 30 分钟 · 实战
通过一个完整的待办事项 App 实战项目,串联 ArkTS 语法、ArkUI 组件、状态管理和数据持久化等核心知识点。
一、数据模型
先定义待办事项的数据结构:
interface TodoItem {
id: number
title: string
completed: boolean
createdAt: number // 时间戳
}
二、主页面结构
import { preferences } from "@kit.ArkData"
@Entry
@Component
struct TodoPage {
@State todos: TodoItem[] = []
@State newTitle: string = ""
@State filter: "all" | "active" | "done" = "all"
private nextId: number = 1
aboutToAppear() {
this.loadTodos()
}
build() {
Column() {
// 顶部标题
Text("我的待办")
.fontSize(28)
.fontWeight(FontWeight.Bold)
.width("100%")
.padding({ left: 20, top: 20, bottom: 12 })
// 输入区域
Row() {
TextInput({ placeholder: "添加新任务...", text: this.newTitle })
.layoutWeight(1)
.height(44)
.onChange((v: string) => this.newTitle = v)
.onSubmit(() => this.addTodo())
Button("添加")
.width(60)
.height(44)
.margin({ left: 8 })
.onClick(() => this.addTodo())
}
.width("100%")
.padding({ left: 20, right: 20 })
// 筛选标签
Row({ space: 8 }) {
this.FilterTab("全部", "all")
this.FilterTab("未完成", "active")
this.FilterTab("已完成", "done")
}
.margin({ top: 16, left: 20 })
// 列表
List({ space: 8 }) {
ForEach(this.filteredTodos(), (item: TodoItem) => {
ListItem() {
this.TodoRow(item)
}
})
}
.layoutWeight(1)
.width("100%")
.padding(16)
// 统计
Text(`${this.todos.filter(t => !t.completed).length} 项待完成`)
.fontSize(13)
.fontColor("#999")
.padding({ bottom: 16 })
}
.width("100%")
.height("100%")
.backgroundColor("#f5f5f5")
}
}
三、子组件:单条待办
@Builder
TodoRow(item: TodoItem) {
Row() {
Checkbox()
.select(item.completed)
.onChange((val: boolean) => {
this.toggleTodo(item.id)
})
Text(item.title)
.fontSize(16)
.decoration({ type: item.completed ? TextDecorationType.LineThrough : TextDecorationType.None })
.fontColor(item.completed ? "#ccc" : "#333")
.layoutWeight(1)
.margin({ left: 12 })
Button("删除")
.fontSize(12)
.height(28)
.backgroundColor("#ff4d4f")
.onClick(() => this.removeTodo(item.id))
}
.width("100%")
.padding(16)
.backgroundColor(Color.White)
.borderRadius(8)
}
四、核心逻辑
// 添加待办
addTodo() {
if (!this.newTitle.trim()) return
this.todos.unshift({
id: this.nextId++,
title: this.newTitle.trim(),
completed: false,
createdAt: Date.now()
})
this.newTitle = ""
this.saveTodos()
}
// 切换完成状态
toggleTodo(id: number) {
const idx = this.todos.findIndex(t => t.id === id)
if (idx >= 0) {
this.todos[idx].completed = !this.todos[idx].completed
this.saveTodos()
}
}
// 删除
removeTodo(id: number) {
this.todos = this.todos.filter(t => t.id !== id)
this.saveTodos()
}
// 筛选
filteredTodos(): TodoItem[] {
if (this.filter === "active") return this.todos.filter(t => !t.completed)
if (this.filter === "done") return this.todos.filter(t => t.completed)
return this.todos
}
// 持久化
async saveTodos() {
const ctx = getContext(this)
const prefs = await preferences.getPreferences(ctx, "todos")
await prefs.put("data", JSON.stringify(this.todos))
await prefs.put("nextId", this.nextId.toString())
await prefs.flush()
}
async loadTodos() {
const ctx = getContext(this)
const prefs = await preferences.getPreferences(ctx, "todos")
const data = await prefs.get("data", "[]") as string
this.todos = JSON.parse(data)
this.nextId = Number(await prefs.get("nextId", "1"))
}
五、小结
通过这个实战项目,你学会了:
- ArkTS 接口定义和状态管理
- ArkUI 列表渲染和条件渲染
- 用户输入处理和事件绑定
- 使用 Preferences 实现数据持久化
- @Builder 自定义组件构建