Kotlin Multiplatform Compose简单教程
更新: 2/25/2026 字数: 0 字 时长: 0 分钟
Kotlin Multiplatform Compose是Jetbrain基于现有的Google Jetpack Compose进行二次开发的用于Kotlin Multiplatform的UI框架,其希望可以使用原本Jetpack Compose的开发方式来对KMP的所有端进行UI绘制,底层基于Skia框架进行UI层面的渲染。
声明式 VS 命令式
早年间Android开发是基于XML,这种命令式的开发在现代编程中显得过于落后,目前主流的前端开发框架(Vue/React)均采用的声明式的开发模式,因此Google也希望自己的系统应用也能使用这种高效的开发方式
基于这种情况,Google开发了Jetpack Compose,希望取代原本的XML作为Android端开发的主流方式
所谓的声明式,即变量与UI深度绑定
Text(text)在上述文本中,变量text如果变化,Text组件也会随之改变,这就是所谓的声明式编程方式,即将变量声明后,组件的内容就会和声明的变量进行绑定
基于原本的Kotlin体系下,Compose使用的是mutableStateOf函数来实现的声明式
val text=mutableStateOf("Hello Compoes")
Text(text.value)在这种情况下,Text的值会完全跟着text的变化而变化
状态
上文中的stateOf的变量就是Compose中的状态,我们通过mutableStateOf方法很简单的创建出来了一个状态
当状态变化时,Compose就会发生页面的刷新,而刷新则分为:组合(Composition),布局,绘制,三个过程
- Composition:执行Compose代码(@Composable)的过程,这一过程完成了界面实际内容的拼凑
- 布局:针对用户界面进行测量,将拼凑出的Compose对象进行放置
- 绘制:将布局好的Compose对象绘制出用户可以看到的样子
这里需要特别说明的是,Composition过程中执行的动作被称为Compose,Compose的具体操作就是使用我们的Composable函数生成出一些对象,我们后续的布局和绘制用的都是这些对象,所以我们也可以理解为Composable函数并不是实际的界面元素,而是用来生成实际的界面元素的,这也解释了为什么Compose中是依赖函数来完成代码编写的
当状态被Compose组件绑定后,状态的变化会直接导致界面的重组
var show by remember { mutableStateOf(false) }
var size by remember { mutableStateOf(1) }
Column {
if (show){
Text("You Can Look Me")
}
Button(
modifier = Modifier
.height((size*50).dp)
.width((size*150).dp),
onClick = {
show = true
size++
}) {
Text("This is a Button")
}
LaunchedEffect(Unit){
delay(3000)
show=false
}
}以上述代码为例,我们的状态有show和size两个变量,这两个状态可以直接影响到界面的变化,因为他们与Compose函数发生了绑定关系,因此这些状态的值发生变化时,相关的组件都会发生重组
对于创建一个状态,我们使用的是
var state by mutableStateOf(1)
var state = mutableStateOf(1)其中使用by的就是利用了Kotlin独特的代理机制,基于Kotlin为我们提供的原生代理机制,我么可以实现不用手动取value的方式来获取状态中的值
渲染过程
使用Room连接本地数据库
Room是Android开发中常用的连接本地数据库的包,其具体是对于原本的SqlLite进行在封装,在2.7.0之后,Room对kMP进行了支持,使得KMP可以使用Android开发的方式来进行跨端开发
首先引入依赖
plugins{
alias(libs.plugins.ksp)
alias(libs.plugins.room)
}
//room
commonMain.dependencies{
implementation(libs.androidx.room.runtime)
implementation(libs.sqlite.bundled)
}
dependencies {
debugImplementation(compose.uiTooling)
add("kspAndroid", libs.androidx.room.compiler)
add("kspCommonMainMetadata", libs.androidx.room.compiler)
add("kspDesktop",libs.androidx.room.compiler)
}
room{
schemaDirectory("$projectDir/schemas")
}然后在Common下创建两个用于使用ksp生成代码的类
//AppDatabase.kt
@Database(entities = [Todo::class], version = 1, exportSchema = true)
@ConstructedBy(AppDatabaseConstructor::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun todoDao(): TodoDao
}
// The Room compiler generates the `actual` implementations.
@Suppress("NO_ACTUAL_FOR_EXPECT")
expect object AppDatabaseConstructor : RoomDatabaseConstructor<AppDatabase> {
override fun initialize(): AppDatabase
}//CreateDatabase.kt
class CreateDatabase(
private val builder:RoomDatabase.Builder<AppDatabase>
){
fun getDateBase():AppDatabase{
return builder
.fallbackToDestructiveMigration(true)
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}
}再在Android包下创建具体的实现
//AndroidDatabaseBuilder.kt
fun androidDatabaseBuilder(ctx: Context): RoomDatabase.Builder<AppDatabase> {
val appContext = ctx.applicationContext
val dbFile = appContext.getDatabasePath("todo.db")
return Room.databaseBuilder<AppDatabase>(
context = appContext,
name = dbFile.absolutePath
)
}还有desktop端
//DesktopDatabaseBuilder.kt
fun desktopDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
val dbFile = File(System.getProperty("java.io.tmpdir"), "todo.db")
return Room.databaseBuilder<AppDatabase>(
name = dbFile.absolutePath,
)
}值得注意的是,目前Room仅支持Desktop,Android,ios三端的本地数据库连接,Web端支持尚且还在孵化中(据说时使用LocalStorage实现)