【开源鸿蒙跨平台开发--KuiklyUI--03】KuiklyUI 入门实战:从零打造高性能跨平台 Todo 应用
KuiklyUI是腾讯开源的跨平台UI框架,基于Kotlin Multiplatform技术。本文介绍了如何从零开始使用KuiklyUI开发一个Todo应用。内容包括:1) KuiklyUI的轻量级、高性能特性;2) 开发环境配置指南;3) 项目结构解析;4) 实战开发步骤:定义数据模型、创建ViewModel、编写声明式UI界面。重点展示了如何使用响应式编程和DSL语法构建跨平台应用,包括列表展
【开源鸿蒙跨平台开发–KuiklyUI–03】KuiklyUI 入门实战:从零打造高性能跨平台 Todo 应用
1. 什么是 KuiklyUI?
KuiklyUI 是腾讯开源的一款基于 Kotlin Multiplatform (KMP) 的高性能跨平台 UI 框架。它的核心理念是 “Write Once, Run Everywhere”,但与 Flutter 或 React Native 不同,KuiklyUI 更加强调原生性能和轻量级。
核心优势
- 极致性能:核心逻辑编译为 Native 二进制 (Android/iOS/HarmonyOS),UI 渲染直接驱动平台原生组件 (View/UIKit/ArkUI)。
- 原生体验:拒绝"自绘引擎"带来的非原生手感,KuiklyUI 的组件就是原生组件。
- 双 DSL 支持:支持自研的简洁 DSL 和 Jetpack Compose 风格 DSL。
- 全平台覆盖:Android, iOS, HarmonyOS Next, Web (H5), 小程序。
2. 环境准备
在开始之前,请确保你的开发环境满足以下要求:
- JDK: JDK 17+ (推荐 JDK 17.0.6 或更高)
- Android 开发: Android Studio Ladybug 或更高版本 (支持 KMP 插件)
- 鸿蒙开发: DevEco Studio 5.0+ (用于运行鸿蒙宿主工程)
- 构建工具: Gradle 8.0+ (项目内置,无需手动安装)
- 操作系统: Windows / macOS / Linux
2.1 鸿蒙环境特别配置
对于鸿蒙开发,你需要配置鸿蒙 SDK 的环境变量,以便 Gradle 能够找到它:
- Windows: 设置系统环境变量
DEVECO_SDK_HOME指向你的 DevEco SDK 路径(例如C:\Users\YourName\AppData\Local\Huawei\Sdk)。 - macOS: 在
~/.bash_profile或~/.zshrc中添加export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk。
3. 项目结构概览
克隆项目后,你会看到以下核心目录:
KuiklyUI/
├── core/ # 框架核心 (KMP)
├── compose/ # Compose DSL 适配层
├── demo/ # 演示代码 (我们将在这里编写业务逻辑)
│ └── src/commonMain/ # 跨平台通用代码 (Write Code Here!)
│ ├── kotlin/com/tencent/kuikly/demo/
│ │ ├── pages/ # 页面目录
│ │ ├── widgets/ # 公共组件目录
│ │ └── App.kt # 应用入口
├── androidApp/ # Android 宿主工程 (仅包含启动代码)
├── ohosApp/ # HarmonyOS 宿主工程 (仅包含启动代码)
├── iosApp/ # iOS 宿主工程
└── 2.0_ohos_demo_build.bat # 鸿蒙编译脚本 (Windows)
开发理念:我们的绝大部分代码(95%以上)都编写在 demo/src/commonMain 目录下,各个平台的 App 目录仅作为宿主壳工程,负责加载 Kuikly 引擎。
从AtomGit上下载这个项目KuiklyTodo
解压存放代码的地方查看项目代码,本项目是基于模板项目修改的
4. 实战:开发一个 Todo 应用
我们将实现一个功能完善的待办事项应用,包含:
- 待办事项列表展示
- 添加新事项
- 标记完成/未完成
- 删除事项
4.1 定义数据模型
首先,我们需要一个数据类来描述待办事项。在 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
)
4.2 创建页面逻辑类 (ViewModel)
在同目录下创建 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 }
}
}
4.3 编写声明式 UI
创建 TodoPageUI.kt,使用 Kuikly 自研 DSL 描述界面。这里的写法类似 Flutter 或 SwiftUI,但完全基于 Kotlin。
/**
* @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))
}
}
}
}
}
}
}
}
}
5. 跨端运行指南
5.1 Android 运行
Android 是最简单的运行平台。
- 打开 Android Studio。
- 等待 Gradle Sync 完成。
- 在运行配置下拉菜单中选择
androidApp。 - 点击运行按钮 (Run)。
修改完代码后要先重新构建
构建完成后点击最上面的运行
安卓运行效果

提示:如果遇到 KMP 相关的配置错误,请确保你的 Android Studio 安装了 Kotlin Multiplatform Mobile 插件。
5.2 HarmonyOS 运行
鸿蒙的运行流程涉及 Native 库的交叉编译,需要特别注意。
步骤 1:编译 Native 库
KuiklyUI 的核心逻辑需要编译成鸿蒙系统的动态链接库 (.so)。
- 找到项目根目录下的
2.0_ohos_demo_build.bat文件。 - 双击运行,或者在终端中执行:
.\2.0_ohos_demo_build.bat - 脚本会自动执行 Gradle 任务,编译生成
libshared.so并复制到ohosApp/entry/libs/arm64-v8a/目录下。注意:如果脚本报错找不到 SDK,请检查章节 2.1 中的环境变量配置。
步骤 2:配置 DevEco Studio
- 启动 DevEco Studio。
- 选择 Open Project,打开项目中的
ohosApp目录。 - 等待 Sync 完成。
步骤 3:实现 Native Bridge (可选但推荐)
如果你的应用需要跳转页面或调用原生能力,需要配置 Bridge。
打开 ohosApp/entry/src/main/ets/kuikly/modules/KRBridgeModule.ets,完善 openPage 方法:
// KRBridgeModule.ets
import router from '@ohos.router';
// ...
// TODO open new page
private openPage(params: KRAny) {
console.info('KRBridgeModule: openPage method execution started');
try {
let args: Record<string, Object>;
if (typeof params === 'string') {
args = JSON.parse(params) as Record<string, Object>;
} else {
args = params as Record<string, Object>;
}
const url = args['url'] as string;
// 使用鸿蒙原生路由跳转
router.pushUrl({
url: 'pages/Index',
params: {
pageName: url // 将目标页面名传给 Index 页面
}
});
} catch (e) {
console.error(`KRBridgeModule: openPage error: ${JSON.stringify(e)}`);
}
}
步骤 4:真机运行
由于 Kuikly 编译的是 ARM64 架构的 .so 库,不支持 x86 模拟器。
-
连接你的鸿蒙真机 (Mate 60 / Pura 70 等)。
-
或者申请 华为云真机 (参考我们另一篇云真机部署教程)。
-
点击 DevEco Studio 的运行按钮。
真机运行效果


6. 调试技巧与常见问题
6.1 如何查看日志?
- Android: 使用 Logcat,过滤
Kuikly或System.out。 - HarmonyOS: 使用 DevEco Studio 的 Log 面板,过滤
HiLog,关键字Kuikly或KRBridgeModule。- 在 Kotlin 代码中可以使用
KLog.i("Tag", "Message")打日志。
- 在 Kotlin 代码中可以使用
6.2 常见编译错误
Unresolved reference: System:- 原因: Kotlin/Native (鸿蒙/iOS) 中没有 Java 的
System类。 - 解决: 使用
Clock.System.now()(需要 kotlinx-datetime) 或者简单的自增 ID (如本教程所示)。
- 原因: Kotlin/Native (鸿蒙/iOS) 中没有 Java 的
settings.2.0.ohos.gradle.kts does not exist:- 原因: 缺少鸿蒙专用构建配置。
- 解决: 运行
.bat脚本前确保该文件存在 (脚本通常会自动处理或依赖项目预设)。
7. 总结
通过这个实战,我们不仅完成了一个 Todo 应用,还深入理解了:
- MVI 架构雏形:State (状态) -> UI (视图) -> Event (事件) 的单向数据流。
- DSL 布局:如何用 Kotlin 代码构建复杂的 Flexbox 布局。
- 多端适配:如何通过一套代码,在 Android 和 HarmonyOS 上运行原生的 UI。
KuiklyUI 让跨平台开发不再是 WebView 的妥协,而是原生性能的释放。快去尝试构建更复杂的应用吧!
8. 延伸阅读
- 【开源鸿蒙跨平台开发–KuiklyUI】 Windows平台Kuikly OpenHarmony开发环境搭建及脚本编译模板工程流程
- Kuikly官方文档
- OpenHarmony官方文档
- Kotlin Multiplatform官方文档
欢迎加入开源鸿蒙跨平台社区:开源鸿蒙跨平台开发者社区
版权声明:本文遵循 CC 4.0 BY-SA 协议,转载请注明出处。
更多推荐


所有评论(0)