Compose Multiplatform跨Android/iOS共享Lottie资源的生产级方案及桥接实现问询
嘿,我刚好在几个生产级Compose Multiplatform项目里解决过完全一样的问题——既要把Lottie动画文件只存一份在common层,又要让Android和iOS用原生Lottie库渲染,还要配合你现有的expect/actual桥接方案。咱们一步步拆解:
一、先搞定Gradle构建配置,让两端自动复用Common层资源
你的现有目录结构已经很合理了,只需要在composeApp模块的build.gradle.kts里加几行配置,就能让Android和iOS自动读取commonMain/composeResources/lottie下的文件,完全不用复制:
Android端配置(映射到Assets目录)
在android代码块里的sourceSets中,把Common层的Lottie目录映射为Android的Assets子目录,这样Android原生Lottie库可以直接通过Assets路径访问:
android { sourceSets { main { // 把Common层的lottie目录映射到Android Assets的根目录(或子目录,按需调整) assets.srcDirs += file("src/commonMain/composeResources/lottie") } } }
配置后Android端的Assets目录会直接引用Common层的文件(无复制操作),原生Lottie可以直接通过文件名访问。
iOS端配置(打包到App Bundle)
在kotlin代码块的Native Target配置里,把Common层的Lottie目录添加为iOS的资源目录,构建时会自动把这些文件打包到iOS App的Bundle中:
kotlin { targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> { binaries.all { // 告诉Kotlin Native把Common层的lottie目录作为资源目录 freeCompilerArgs += "-Xresource-dir=${project.file("src/commonMain/composeResources/lottie").absolutePath}" } } // 或者更简洁的方式,直接给iOS Main配置资源源 sourceSets["iosMain"].resources.srcDirs += file("src/commonMain/composeResources/lottie") }
配置后iOS端的App Bundle会包含Common层的Lottie文件,SwiftUI可以直接通过文件名加载。
二、Android端Actual实现(用Lottie-Compose)
基于你定义的expect函数,Android端的actual实现直接用官方的Lottie-Compose库,因为资源已经映射到Assets,直接通过文件名加载即可:
// androidMain下的实现文件 @Composable actual fun LottieAnimView(assetName: String, modifier: Modifier) { val composition by rememberLottieComposition( LottieCompositionSpec.Asset(assetName) ) LottieAnimation( composition = composition, modifier = modifier, iterations = LottieConstants.IterateForever ) }
这里的assetName直接传"animation.json"就行,和Common层的文件名完全对应。
三、iOS端Actual实现(含SwiftUI桥接)
iOS端需要通过Kotlin/Native桥接调用SwiftUI的Lottie视图,分两步走:
1. Kotlin端的Actual实现
在iosMain下写actual函数,用UIKitView包裹我们桥接的SwiftUI Lottie视图:
// iosMain下的实现文件 @Composable actual fun LottieAnimView(assetName: String, modifier: Modifier) { UIKitView( modifier = modifier, factory = { // 桥接Swift端的Lottie宿主控制器,传入资源文件名 LottieHostingController(assetName: assetName).view } ) }
2. Swift端的桥接代码
在iosMain/swift目录下创建LottieHostingController.swift,用SwiftUI的Lottie库实现渲染(记得先通过CocoaPods添加Lottie依赖到iOS目标):
import UIKit import SwiftUI import Lottie // 包裹SwiftUI视图的UIKit控制器,用于Kotlin/Native桥接 class LottieHostingController: UIHostingController<LottieSwiftUIView> { init(assetName: String) { super.init(rootView: LottieSwiftUIView(assetName: assetName)) } @objc required dynamic init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } // SwiftUI的Lottie视图实现 struct LottieSwiftUIView: View { let assetName: String var body: some View { LottieView(animation: .named(assetName)) .looping() .scaledToFit() } }
这里的assetName同样直接传"animation.json",和Common层的文件名保持一致即可。
四、Common层调用示例
现在在CommonMain的Composable里,你可以直接调用这个跨平台的Lottie视图,完全不用关心两端的实现细节:
// commonMain下的页面Composable @Composable fun HomeScreen() { Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { LottieAnimView( assetName = "animation.json", modifier = Modifier.size(200.dp) ) } }
生产环境注意事项
- 文件名大小写一致性:iOS对文件名大小写敏感,Android不敏感,所以Common层的文件名要统一大小写,比如全小写或固定驼峰,避免两端加载失败。
- 资源验证:可以在Gradle里加自定义任务,检查Common层的Lottie文件是否存在,构建时提前报错,避免运行时问题。
- Lottie版本对齐:确保Android的
lottie-compose和iOS的LottieSwift库版本尽量接近,避免动画渲染效果不一致。
内容来源于stack exchange

