KuiklyUI 入门实战:跨平台 Todo 应用打造记录
开源鸿蒙跨平台开发环境
引言:本文主要是记录一个新手在前两篇文章的基础下入门KuiklyUI并开发Todo应用的过程。
1.什么是KuiklyUI?
1.1核心定位与目标
KuiklyUI 旨在提供一套代码,多端运行的开发体验,同时保证原生性能、极致易用性和动态灵活性,帮助开发者大幅提升跨平台应用的研发效率。
1.2核心特点与优势
- 原生性能:生成平台原生二进制文件,渲染效率比 React Native 等框架快约6 倍,接近原生开发体验
- 统一技术栈:使用 Kotlin 单一语言开发所有平台,无需学习多套语言和框架
- 动态化能力:支持动态更新 UI 和业务逻辑,无需重新发布应用
- 高性能优化:虚拟 DOM、延迟更新、增量布局等技术保证流畅体验
- 丰富生态:与 KuiklyBase(基础库)配合使用,提供完整的跨端解决方案
2.环境准备
1.JDK17+
2.Android Studio的最新版本
3.DevEco Studio5.0+
3.项目的准备
3.1下载项目模板
前往(KuiklyTodo - AtomGit | GitCode)下载项目模板并解压至你工程常用的位置

3.2TodoItem.kt的修改
前往KuiklyTodo-main\demo\src\commonMain\kotlin\com\tencent\kuikly\demo\pages\todo
该文件夹下找到TodoItem.kt文件修改代码
我这里是选择的记事本打开

package com.tencent.kuikly.demo.pages.todo
data class TodoItem(
val id: Long,
var content: String,
var isCompleted: Boolean = false
)
3.3TodoPage.kt的修改
在同一个文件夹里找到TodoPage.kt
/**
* @ProjectName : KuiklyUI
* @Author : GuoJiaHui
* @Time : 2026年01月29日 17:30 PM
* @Description : 待办事项页面逻辑控制器
* 负责管理待办事项列表的数据状态、增删改查操作
*/
package com.tencent.kuikly.demo.pages.todo
import com.tencent.kuikly.core.annotations.Page
import com.tencent.kuikly.core.base.ViewBuilder
import com.tencent.kuikly.demo.pages.base.BasePager
import com.tencent.kuikly.core.reactive.handler.observable
import com.tencent.kuikly.core.reactive.handler.observableList
/**
* 待办事项页面类
* 使用 @Page 注解标记为页面,路由名称为 "todo_page"
*/
@Page("todo_page")
internal class TodoPage : BasePager() {
/** 待办事项列表数据,使用 observableList 实现响应式更新 */
var todoList by observableList<TodoItem>()
/** 输入框文本状态,使用 observable 实现双向绑定 */
var inputText by observable("")
/**
* 页面创建生命周期回调
* 在此初始化数据
*/
override fun created() {
super.created()
// 初始化演示数据
initData()
}
/**
* 初始化演示数据
* 添加默认的待办事项
*/
private fun initData() {
todoList.add(TodoItem(1, "学习 KuiklyUI 框架"))
todoList.add(TodoItem(2, "创建一个 Todo 应用"))
}
/**
* 构建页面 UI 结构
* @return ViewBuilder UI 构建函数
*/
override fun body(): ViewBuilder {
return buildTodoUI(this)
}
/** 生成 ID 的计数器,起始值为 100 */
private var nextId = 100L
/**
* 添加新的待办事项
* 校验输入是否为空,创建新项并添加到列表,最后清空输入框
*/
fun addTodo() {
if (inputText.isBlank()) return
val newItem = TodoItem(nextId++, inputText)
todoList.add(newItem)
inputText = ""
}
/**
* 切换待办事项的完成状态
* @param item 目标待办事项对象
*/
fun toggleTodo(item: TodoItem) {
item.isCompleted = !item.isCompleted
// 触发列表更新:通过重新赋值触发 observableList 的更新机制
val index = todoList.indexOf(item)
if (index >= 0) {
todoList[index] = item
}
}
/**
* 删除指定的待办事项
* @param item 要删除的待办事项对象
*/
fun deleteTodo(item: TodoItem) {
todoList.remove(item)
}
/**
* 清除所有已完成的待办事项
* 遍历列表并移除 isCompleted 为 true 的项
*/
fun clearCompleted() {
todoList.removeAll { it.isCompleted }
}
}
3.4TodoPageUI.kt的修改
依旧是在同一个文件夹里找到TodoPageUI.kt文件

/**
* @ProjectName : KuiklyUI
* @Author : GuoJiaHui
* @Time : 2026年01月29日 17:30 PM
* @Description : 待办事项页面UI构建逻辑
* 包含整体布局、样式定义及事件绑定
*/
package com.tencent.kuikly.demo.pages.todo
import com.tencent.kuikly.core.base.Color
import com.tencent.kuikly.core.base.ViewBuilder
import com.tencent.kuikly.core.pager.Pager
import com.tencent.kuikly.core.views.*
import com.tencent.kuikly.core.directives.vfor
import com.tencent.kuikly.core.directives.vif
import com.tencent.kuikly.core.base.Border
import com.tencent.kuikly.core.base.BorderStyle
import com.tencent.kuikly.core.base.BoxShadow
/**
* 构建 Todo 页面的 UI 结构
* @param page 页面上下文对象,需转换为 TodoPage 类型以访问数据
* @return ViewBuilder UI 构建闭包
*/
fun buildTodoUI(page: Pager): ViewBuilder {
val ctx = page as TodoPage
return {
View {
attr {
// 设置页面主容器充满全屏
flex(1f)
backgroundColor(Color.WHITE)
// 处理安全区域内边距,适配刘海屏/动态岛
padding(
top = 16f + ctx.pageData.safeAreaInsets.top,
left = 16f + ctx.pageData.safeAreaInsets.left,
right = 16f + ctx.pageData.safeAreaInsets.right,
bottom = 16f + ctx.pageData.safeAreaInsets.bottom
)
}
// 标题栏组件
Text {
attr {
text("待办事项清单")
fontSize(24f)
fontWeightBold()
marginBottom(20f)
color(Color.BLACK)
}
}
// 顶部输入区域容器
View {
attr {
flexDirectionRow() // 水平布局
marginBottom(20f)
height(50f)
alignItemsCenter() // 垂直居中
}
// 文本输入框组件
Input {
attr {
flex(1f) // 占据剩余宽度
text(ctx.inputText) // 绑定输入文本
placeholder("需要做什么?")
fontSize(16f)
backgroundColor(Color(0xFFF5F5F5))
borderRadius(8f)
height(44f)
}
event {
// 监听文本变化事件,更新数据模型
textDidChange { params ->
ctx.inputText = params.text
}
}
}
// 添加按钮组件
View {
attr {
width(80f)
height(44f)
marginLeft(10f)
backgroundColor(Color(0xFF007AFF))
borderRadius(8f)
justifyContentCenter() // 内容居中
alignItemsCenter()
// 增加按钮投影效果
boxShadow(BoxShadow(0f, 2f, 4f, Color(0x40007AFF)))
}
Text {
attr {
text("添加")
color(Color.WHITE)
fontWeightBold()
fontSize(16f)
}
}
event {
// 绑定点击事件,调用添加逻辑
click { ctx.addTodo() }
}
}
}
// 列表显示区域
View {
attr {
flex(1f) // 占据剩余空间
}
// 空状态提示:当列表为空时显示
vif({ ctx.todoList.isEmpty() }) {
View {
attr {
flex(1f)
justifyContentCenter()
alignItemsCenter()
}
Text {
attr {
text("暂无待办事项")
fontSize(18f)
color(Color(0xFF999999))
marginBottom(8f)
}
}
Text {
attr {
text("快去添加一个吧!")
fontSize(14f)
color(Color(0xFFCCCCCC))
}
}
}
}
// 列表渲染循环:遍历 todoList 生成列表项
vfor({ ctx.todoList }) { item ->
View {
attr {
flexDirectionColumn()
}
// 列表项内容行
View {
attr {
flexDirectionRow()
alignItemsCenter()
padding(top = 12f, bottom = 12f)
}
// 复选框组件(自定义 View 模拟)
View {
attr {
width(24f)
height(24f)
borderRadius(12f)
// 根据完成状态设置边框和背景色
border(Border(2f, BorderStyle.SOLID, if (item.isCompleted) Color(0xFF007AFF) else Color(0xFFCCCCCC)))
backgroundColor(if (item.isCompleted) Color(0xFF007AFF) else Color.TRANSPARENT)
marginRight(12f)
justifyContentCenter()
alignItemsCenter()
}
event {
// 点击复选框切换状态
click { ctx.toggleTodo(item) }
}
// 勾选图标
Text {
attr {
text("✓")
color(Color.WHITE)
fontSize(14f)
fontWeightBold()
// 控制勾选标记的透明度
opacity(if (item.isCompleted) 1f else 0f)
}
}
}
// 待办事项文本内容
Text {
attr {
text(item.content)
fontSize(16f)
// 完成状态文字颜色变浅
color(if (item.isCompleted) Color(0xFF999999) else Color(0xFF333333))
flex(1f)
// 完成状态添加删除线
if (item.isCompleted) textDecorationLineThrough() else "textDecoration" with "none"
}
event {
// 点击文字也可切换状态
click { ctx.toggleTodo(item) }
}
}
// 删除按钮
View {
attr {
padding(8f) // 增加点击热区
}
event {
click { ctx.deleteTodo(item) }
}
Text {
attr {
text("✕")
color(Color(0xFFFF3B30)) // 红色警示色
fontSize(18f)
}
}
}
}
// 分割线
View {
attr {
height(1f)
backgroundColor(Color(0xFFEEEEEE))
marginLeft(36f) // 缩进以对齐文本
}
}
}
}
}
// 底部状态栏与操作区(仅当列表不为空时显示)
vif({ ctx.todoList.isNotEmpty() }) {
View {
attr {
flexDirectionColumn()
}
// 顶部分割线
View {
attr {
height(1f)
backgroundColor(Color(0xFFEEEEEE))
}
}
// 底部内容容器
View {
attr {
flexDirectionRow()
justifyContentSpaceBetween() // 两端对齐
alignItemsCenter()
padding(top = 16f)
}
// 剩余待办计数
Text {
attr {
text("${ctx.todoList.count { !it.isCompleted }} 项待办")
fontSize(14f)
color(Color(0xFF666666))
}
}
// 清除已完成按钮
View {
attr {
padding(8f)
}
event {
click { ctx.clearCompleted() }
}
Text {
attr {
text("清除已完成")
fontSize(14f)
color(Color(0xFF007AFF))
}
}
}
}
}
}
}
}
}
到这里项目的准备工作已经完成了,接下来进行调试。
4.Android端调试
Android 是最简单的运行平台,所以我们选择先在Android端进行调试,后续在鸿蒙端进行调试。
打开Android Studio,打开项目,等待构建

同时拿出安卓手机打开开发者模式,或者打开安卓模拟器,我这里是用的模拟器
运行项目即可


5.华为云真机测试
在Android端运行成功后我们接着在华为云真机上调试
通过证书与签名文件准备,DevEco Studio 签名配置和构建HAP包之后,申请测试云真机




结语:在KuiklyUI 入门实战,打造跨平台 Todo 应用中,重要的是代码的修改,一套代码可以在 Android 和 HarmonyOS 上运行原生的 UI,要理解如何用 Kotlin 代码构建复杂的 Flexbox 布局。希望本文能帮助Windows平台的开发者顺利进入Kuikly OpenHarmony开发领域。
更多推荐




所有评论(0)