Kuikly 跨端聊天 App 开发实践:从 0 到 1 打造原生体验的 IM 应用(Kuikly安卓demo) ypxx.net

一、项目背景与架构设计

在跨端 IM 场景里,最难的往往不是“能不能跑”,而是消息列表是否跟手、键盘是否顺滑、各端体验是否一致。Kuikly 的优势在于:UI 与业务逻辑全部用 Kotlin 编写,直出各端原生视图,无 JS Bridge 开销,非常适合聊天页这种高频交互场景。

项目采用 KMM + Kuikly DSL 的分层结构,核心原则是:

UI + 业务逻辑全部收敛在 commonMain,只有极少数平台差异(时间、存储、录音等)用 expect/actual 分流。

shared/

├── commonMain/ # ✅ 核心:UI + 业务 + 数据层

├── androidMain/ # Android 平台差异实现

├── iosMain/ # iOS 平台差异实现

├── ohosArm64Main/ # 鸿蒙平台差异实现

└── jsMain/ # H5 / 小程序差异实现


二、数据层:消息模型与状态驱动

聊天应用的核心是消息流,我们用 kotlinx.serialization 定义统一的数据模型,并用 Kuikly 的响应式 API 驱动 UI 自动更新。

// commonMain — data/Message.kt

@Serializable

data class Message(

val id: String,

val text: String,

val senderId: String,

val timestamp: Long,

val status: MessageStatus = MessageStatus.SENDING

@Serializable

enum class MessageStatus { SENDING, SENT, FAILED }

在 ChatPage 中,使用 observableList 管理消息列表,新增消息时 UI 自动刷新,无需手动 notifyItemChanged:

private val currentUserId = "me"

private var messages by observableList<Message>()

private var inputText by observable("")


三、核心 UI:消息列表 + 气泡

1. 高性能消息列表

聊天页对性能要求极高。Kuikly 的 List + vfor 内部做了差分更新和视图复用,非常适合长列表:

List {

attr {

flex(1f)

paddingHorizontal(12f)

}

vfor({ messages }) { msg ->

MessageBubble(msg, msg.senderId == currentUserId)

}

}

2. 左右气泡布局

通过 senderId 判断消息归属,控制对齐方向与气泡颜色:

fun ViewContainer<*, *>.MessageBubble(msg: Message, isMine: Boolean) {

View {

attr {

flexDirectionRow()

if (isMine) justifyContentFlexEnd() else justifyContentFlexStart()

marginVertical(4f)

}

View {

attr {

maxWidth(75f)

padding(10f)

borderRadius(12f)

backgroundColor(if (isMine) Color(0xFF4A90D9) else Color.WHITE)

}

Text {

attr {

text(msg.text)

fontSize(15f)

color(if (isMine) Color.WHITE else Color(0xFF333333))

}

}

}

}

}


四、输入栏与键盘适配

1. 输入栏组件

输入栏包含 Input 和发送按钮,通过回调将事件抛给 ChatPage:

InputBar(

text = { inputText },

onTextChange = { inputText = it },

onSend = { sendMessage() }

2. 键盘弹起处理(关键体验点)

Kuikly 的 Input 在各端原生渲染,键盘自动弹起。为了让输入栏跟随键盘上移,我们监听键盘高度:

private var keyboardHeight by observable(0f)

override fun created() {

acquireModule<KRKeyboardModule>(KRKeyboardModule.MODULE_NAME)

.addKeyboardHeightListener { params ->

keyboardHeight = params.height

}

}

并在输入栏上动态设置 marginBottom:

attr { marginBottom(keyboardHeight) }

这样在 Android / iOS / 鸿蒙上都能获得原生级键盘跟随体验。


五、网络层:WebSocket + HTTP

聊天应用通常采用 WebSocket 长连接 + HTTP 兜底 的架构。

class ChatRepository(

private val onNewMessage: (Message) -> Unit

) {

fun connect() {

// 通过 KRWebSocketModule 建立长连接

isConnected = true

}

fun send(msg: Message) {

CoroutineScope(Dispatchers.Default).launch {

// KRHttpModule.post("/api/messages", json.encodeToString(msg))

}

}

fun loadHistory(ed: (List<Message>) -> Unit) {

CoroutineScope(Dispatchers.Default).launch {

// 拉取历史消息并回调

}

}

}

在 ChatPage.created() 中初始化连接和拉取历史:

repository.connect()

repository.loadHistory { history ->

messages.addAll(0, history)

}


六、平台差异处理:expect / actual

典型差异点如时间格式化,通过 expect/actual 在各端分别实现:

// commonMain

expect fun formatTimestamp(ts: Long): String

// androidMain

actual fun formatTimestamp(ts: Long): String {

return SimpleDateFormat("HH:mm", Locale.getDefault()).format(Date(ts))

}

// jsMain

actual fun formatTimestamp(ts: Long): String {

val date = kotlinx.browser.Date(ts.toDouble())

return "{date.getHours()}:{date.getMinutes()}"

}

建议优先使用 kotlinx-datetime 等纯 Kotlin 库,减少手写 expect/actual 的成本。

七、Compose DSL 对照(可选)

如果团队更熟悉 Jetpack Compose,也可以直接使用 Kuikly 的 Compose DSL,跨端能力完全一致:

@Composable

fun ChatPage() {

var messages by remember { mutableStateOf(listOf<Message>()) }

var inputText by remember { mutableStateOf("") }

Column(modifier = Modifier.fillMaxSize()) {

LazyColumn(modifier = Modifier.weight(1f)) {

items(messages) { msg ->

MessageBubble(msg, msg.senderId == "me")

}

}

Row(modifier = Modifier.padding(8.dp)) {

TextField(value = inputText, onValueChange = { inputText = it })

Button(onClick = { /* send */ }) { Text("发") }

}

}

}


八、关键实践总结


九、一句话总结

Kuikly 做聊天 App 的核心竞争力是:一套 Kotlin 代码写 UI + 业务,直出各端原生视图,无 JS Bridge 转译,消息列表滚动和键盘交互体验接近原生。

这套方案已经在多个生产级 IM 项目中落地,既保留了 Kotlin 的开发效率,又满足了 各端对性能和体验的严苛要求,是构建跨端聊天应用的理想选择。