Kuikly 跨端聊天 App 开发实践:从 0 到 1 打造原生体验的 IM 应用
一、项目背景与架构设计
在跨端 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(onLoaded: (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 的开发效率,又满足了 各端对性能和体验的严苛要求,是构建跨端聊天应用的理想选择。
更多推荐

所有评论(0)