
Integrates Rive animations with a unified API for Android and iOS, offering easy integration, native performance, state machine support, and flexible configuration options. Currently experimental.
A Compose Multiplatform wrapper library for integrating Rive animations, providing a unified API to use rive-android, rive-ios, and @rive-app/canvas seamlessly across Android, iOS, and Web platforms.
⚠️ EXPERIMENTAL STATUSThis library is currently in an experimental state. Features, APIs, and implementation details may change significantly or the project might be discontinued. Use at your own risk in production applications.
Current Limitations:
On iOS,Fixed in #42.UIKitViewdoes not support transparent backgrounds, resulting in opaque backgrounds for Rive animations. This is a known limitation in Compose Multiplatform. See Issue #17 for details and potential workarounds.- Not all features and properties from the native Rive libraries are supported yet
- Some advanced Rive features may not be available across all platforms
CustomRiveAnimation composable that works across Android, iOS and Web| Platform | Implementation | Dependency |
|---|---|---|
| Android | Native rive-android | app.rive.runtime.kotlin |
| iOS | Swift Package Manager |
rive-ios via spm4kmp |
| Web (JS) | NPM package | @rive-app/canvas |
Add the dependency to your build.gradle.kts:
commonMain.dependencies {
implementation("dev.muazkadan:rive-cmp:0.3.1")
}dependencies {
implementation("dev.muazkadan:rive-cmp:0.3.1")
}Add to your libs.versions.toml:
[versions]
rive-cmp = "0.3.1"
[libraries]
rive-cmp = { module = "dev.muazkadan:rive-cmp", version.ref = "rive-cmp" }Rive needs to initialize its runtime when your app starts. You can do this in one of the following ways:
Add this to your app's manifest file:
<provider android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup" android:exported="false"
tools:node="merge">
<meta-data android:name="app.rive.runtime.kotlin.RiveInitializer"
android:value="androidx.startup" />
</provider>Call the initializer in your application code:
AppInitializer.getInstance(applicationContext)
.initializeComponent(RiveInitializer::class.java)Initialize Rive yourself in your code:
Rive.init(context)If you encounter undefined symbols errors for Swift classes when building for iOS, manually add the rive-ios dependency to your Xcode project:
In Xcode, go to File > Add Package Dependencies...
Enter the package URL: https://github.com/rive-app/rive-ios.git
Select version 6.13.0 (exact match to the library's dependency).
Add the package to your project.
In the target settings, add RiveRuntime to the Frameworks, Libraries, and Embedded Content.
This resolves linking issues with the Rive runtime on iOS.
Alternatively, for advanced users, the library generates a local Swift package at
library/SPM/spmKmpPlugin/nativeIosShared. You can add this local package to your Xcode project if
you have the source cloned. See spm4kmp documentation for
details.
Note: The library uses spm4kmp to integrate rive-ios, but manual addition may be required in some setups.
import dev.muazkadan.rivecmp.CustomRiveAnimation
import dev.muazkadan.rivecmp.utils.ExperimentalRiveCmpApi
@OptIn(ExperimentalRiveCmpApi::class)
@Composable
fun MyScreen() {
CustomRiveAnimation(
modifier = Modifier.size(200.dp),
url = "https://your-rive-animation-url.riv"
)
}import dev.muazkadan.rivecmp.CustomRiveAnimation
import dev.muazkadan.rivecmp.RiveCompositionSpec
import dev.muazkadan.rivecmp.rememberRiveComposition
import dev.muazkadan.rivecmp.utils.ExperimentalRiveCmpApi
@OptIn(ExperimentalRiveCmpApi::class)
@Composable
fun MyScreen() {
// URL-based composition
val urlAnimation by rememberRiveComposition {
RiveCompositionSpec.url("https://cdn.rive.app/animations/your_animation.riv")
}
// Resource-based composition
val resourceAnimation by rememberRiveComposition {
RiveCompositionSpec.byteArray(Res.readBytes("files/your_animation.riv"))
}
Column {
CustomRiveAnimation(
modifier = Modifier.size(200.dp),
composition = urlAnimation
)
CustomRiveAnimation(
modifier = Modifier.size(200.dp),
composition = resourceAnimation
)
}
}@ExperimentalRiveCmpApi
@Composable
fun CustomRiveAnimation(
modifier: Modifier = Modifier,
url: String,
alignment: RiveAlignment = RiveAlignment.CENTER,
autoPlay: Boolean = true,
artboardName: String? = null,
fit: RiveFit = RiveFit.CONTAIN,
stateMachineName: String? = null,
)@ExperimentalRiveCmpApi
@Composable
fun CustomRiveAnimation(
modifier: Modifier = Modifier,
byteArray: ByteArray,
alignment: RiveAlignment = RiveAlignment.CENTER,
autoPlay: Boolean = true,
artboardName: String? = null,
fit: RiveFit = RiveFit.CONTAIN,
stateMachineName: String? = null,
)@ExperimentalRiveCmpApi
@Composable
fun CustomRiveAnimation(
modifier: Modifier = Modifier,
composition: RiveComposition?,
alignment: RiveAlignment = RiveAlignment.CENTER,
autoPlay: Boolean = true,
artboardName: String? = null,
fit: RiveFit = RiveFit.CONTAIN,
stateMachineName: String? = null,
)// Create URL-based composition spec
RiveCompositionSpec.url(url: String): RiveCompositionSpec
// Create ByteArray-based composition spec
RiveCompositionSpec.byteArray(byteArray: ByteArray): RiveCompositionSpec@Composable
fun rememberRiveComposition(
vararg keys: Any?,
spec: suspend () -> RiveCompositionSpec,
): State<RiveComposition?>modifier: Compose modifier for styling and layouturl: URL to the Rive animation file (direct loading)byteArray: ByteArray containing the Rive animation data (direct loading)composition: Pre-loaded RiveComposition from rememberRiveComposition (recommended)alignment: How the animation should be aligned within its container (default:
RiveAlignment.CENTER)autoPlay: Whether the animation should start playing automatically (default: true)artboardName: Optional name of the specific artboard to usefit: How the animation should fit within its container (default: RiveFit.CONTAIN)stateMachineName: Optional name of the state machine to useThis library uses Kotlin Multiplatform with the following plugins:
kotlinMultiplatformandroidLibrarycomposeMultiplatformcomposeCompilerspmForKmp (for iOS Swift Package Manager integration)# Build all targets
./gradlew build
# Build Android AAR
./gradlew :library:assembleRelease
# Build iOS Framework
./gradlew :library:linkReleaseFrameworkIosArm64git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Copyright 2025 Muaz KADAN
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Muaz KADAN
A Compose Multiplatform wrapper library for integrating Rive animations, providing a unified API to use rive-android, rive-ios, and @rive-app/canvas seamlessly across Android, iOS, and Web platforms.
⚠️ EXPERIMENTAL STATUSThis library is currently in an experimental state. Features, APIs, and implementation details may change significantly or the project might be discontinued. Use at your own risk in production applications.
Current Limitations:
On iOS,Fixed in #42.UIKitViewdoes not support transparent backgrounds, resulting in opaque backgrounds for Rive animations. This is a known limitation in Compose Multiplatform. See Issue #17 for details and potential workarounds.- Not all features and properties from the native Rive libraries are supported yet
- Some advanced Rive features may not be available across all platforms
CustomRiveAnimation composable that works across Android, iOS and Web| Platform | Implementation | Dependency |
|---|---|---|
| Android | Native rive-android | app.rive.runtime.kotlin |
| iOS | Swift Package Manager |
rive-ios via spm4kmp |
| Web (JS) | NPM package | @rive-app/canvas |
Add the dependency to your build.gradle.kts:
commonMain.dependencies {
implementation("dev.muazkadan:rive-cmp:0.3.1")
}dependencies {
implementation("dev.muazkadan:rive-cmp:0.3.1")
}Add to your libs.versions.toml:
[versions]
rive-cmp = "0.3.1"
[libraries]
rive-cmp = { module = "dev.muazkadan:rive-cmp", version.ref = "rive-cmp" }Rive needs to initialize its runtime when your app starts. You can do this in one of the following ways:
Add this to your app's manifest file:
<provider android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup" android:exported="false"
tools:node="merge">
<meta-data android:name="app.rive.runtime.kotlin.RiveInitializer"
android:value="androidx.startup" />
</provider>Call the initializer in your application code:
AppInitializer.getInstance(applicationContext)
.initializeComponent(RiveInitializer::class.java)Initialize Rive yourself in your code:
Rive.init(context)If you encounter undefined symbols errors for Swift classes when building for iOS, manually add the rive-ios dependency to your Xcode project:
In Xcode, go to File > Add Package Dependencies...
Enter the package URL: https://github.com/rive-app/rive-ios.git
Select version 6.13.0 (exact match to the library's dependency).
Add the package to your project.
In the target settings, add RiveRuntime to the Frameworks, Libraries, and Embedded Content.
This resolves linking issues with the Rive runtime on iOS.
Alternatively, for advanced users, the library generates a local Swift package at
library/SPM/spmKmpPlugin/nativeIosShared. You can add this local package to your Xcode project if
you have the source cloned. See spm4kmp documentation for
details.
Note: The library uses spm4kmp to integrate rive-ios, but manual addition may be required in some setups.
import dev.muazkadan.rivecmp.CustomRiveAnimation
import dev.muazkadan.rivecmp.utils.ExperimentalRiveCmpApi
@OptIn(ExperimentalRiveCmpApi::class)
@Composable
fun MyScreen() {
CustomRiveAnimation(
modifier = Modifier.size(200.dp),
url = "https://your-rive-animation-url.riv"
)
}import dev.muazkadan.rivecmp.CustomRiveAnimation
import dev.muazkadan.rivecmp.RiveCompositionSpec
import dev.muazkadan.rivecmp.rememberRiveComposition
import dev.muazkadan.rivecmp.utils.ExperimentalRiveCmpApi
@OptIn(ExperimentalRiveCmpApi::class)
@Composable
fun MyScreen() {
// URL-based composition
val urlAnimation by rememberRiveComposition {
RiveCompositionSpec.url("https://cdn.rive.app/animations/your_animation.riv")
}
// Resource-based composition
val resourceAnimation by rememberRiveComposition {
RiveCompositionSpec.byteArray(Res.readBytes("files/your_animation.riv"))
}
Column {
CustomRiveAnimation(
modifier = Modifier.size(200.dp),
composition = urlAnimation
)
CustomRiveAnimation(
modifier = Modifier.size(200.dp),
composition = resourceAnimation
)
}
}@ExperimentalRiveCmpApi
@Composable
fun CustomRiveAnimation(
modifier: Modifier = Modifier,
url: String,
alignment: RiveAlignment = RiveAlignment.CENTER,
autoPlay: Boolean = true,
artboardName: String? = null,
fit: RiveFit = RiveFit.CONTAIN,
stateMachineName: String? = null,
)@ExperimentalRiveCmpApi
@Composable
fun CustomRiveAnimation(
modifier: Modifier = Modifier,
byteArray: ByteArray,
alignment: RiveAlignment = RiveAlignment.CENTER,
autoPlay: Boolean = true,
artboardName: String? = null,
fit: RiveFit = RiveFit.CONTAIN,
stateMachineName: String? = null,
)@ExperimentalRiveCmpApi
@Composable
fun CustomRiveAnimation(
modifier: Modifier = Modifier,
composition: RiveComposition?,
alignment: RiveAlignment = RiveAlignment.CENTER,
autoPlay: Boolean = true,
artboardName: String? = null,
fit: RiveFit = RiveFit.CONTAIN,
stateMachineName: String? = null,
)// Create URL-based composition spec
RiveCompositionSpec.url(url: String): RiveCompositionSpec
// Create ByteArray-based composition spec
RiveCompositionSpec.byteArray(byteArray: ByteArray): RiveCompositionSpec@Composable
fun rememberRiveComposition(
vararg keys: Any?,
spec: suspend () -> RiveCompositionSpec,
): State<RiveComposition?>modifier: Compose modifier for styling and layouturl: URL to the Rive animation file (direct loading)byteArray: ByteArray containing the Rive animation data (direct loading)composition: Pre-loaded RiveComposition from rememberRiveComposition (recommended)alignment: How the animation should be aligned within its container (default:
RiveAlignment.CENTER)autoPlay: Whether the animation should start playing automatically (default: true)artboardName: Optional name of the specific artboard to usefit: How the animation should fit within its container (default: RiveFit.CONTAIN)stateMachineName: Optional name of the state machine to useThis library uses Kotlin Multiplatform with the following plugins:
kotlinMultiplatformandroidLibrarycomposeMultiplatformcomposeCompilerspmForKmp (for iOS Swift Package Manager integration)# Build all targets
./gradlew build
# Build Android AAR
./gradlew :library:assembleRelease
# Build iOS Framework
./gradlew :library:linkReleaseFrameworkIosArm64git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Copyright 2025 Muaz KADAN
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Muaz KADAN