Skip to content

kmpbits/KMP-Splash

Repository files navigation

KMP Splash

Professional Splash Screens for Compose Multiplatform, configured in seconds.

Creating a seamless startup experience in Compose Multiplatform is notoriously difficult. Between the native Android SplashScreen API and iOS UILaunchScreen, developers often face a "white flash" gap between the native boot sequence and the Jetpack Compose Multiplatform runtime.

KMP Splash bridges this gap. It automates the generation of native splash assets and provides a Compose-ready transition layer, ensuring your app feels premium from the very first pixel.


Maven Central License


Why KMP Splash?

  • Single Source of Truth: Configure your background color and logo once in build.gradle.kts.
  • Native Integration: Generates real Assets.xcassets for iOS and themes.xml for Android.
  • Seamless Transitions: Provides a SplashConfig composable to prevent the flicker when shifting from native boot to Compose UI.
  • No Xcode Required: Patches .pbxproj and Info.plist automatically. No more Storyboards.
  • Dark Mode Ready: Built-in support for dark mode background colors on both Android and iOS.

Requirements

  • Kotlin 2.1.0+
  • Compose Multiplatform 1.7.0+
  • Android: androidx.core:core-splashscreen 1.2.0+
  • iOS: Xcode 14+ (uses UILaunchScreen plist key)

Installation

1. Configure Repositories

Ensure you have mavenCentral() in your settings.gradle.kts:

pluginManagement {
    repositories {
        google()
        gradlePluginPortal()
        mavenCentral()
    }
}

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
}

2. Configure the Version Catalog

In your gradle/libs.versions.toml:

[versions]
kmpSplash = "0.2.1"

[libraries]
kmpSplash-runtime = { module = "io.github.kmpbits:splash-runtime", version.ref = "kmpSplash" }

[plugins]
kmpSplash = { id = "io.github.kmpbits.splash", version.ref = "kmpSplash" }

3. Apply the plugin

In your Compose App module build.gradle.kts:

plugins {
    alias(libs.plugins.kmpSplash)
}

splashScreen {
    backgroundColor = SplashColor.hex("#FFFFFF")       // Light mode background
    backgroundColorNight = SplashColor.hex("#1A1A2E")  // Optional: dark mode background
    logo = SplashLogo.resource("logo.png")             // File in composeResources/drawable/
    logoDark = SplashLogo.resource("logo_dark.png")    // Optional: dark mode logo
    iosProjectPath = "iosApp/iosApp"                   // Optional: defaults to "iosApp/iosApp"
}

SplashColor alternatives

SplashColor.hex("#FFFFFF")        // Hex string — accepts both #RRGGBB and RRGGBB
SplashColor.rgb(255, 255, 255)    // RGB values (0–255 each)
SplashColor.white                 // Named constant
SplashColor.black                 // Named constant

SplashLogo alternatives

SplashLogo.resource("logo.png")                              // File in composeResources/drawable/
SplashLogo.path("src/commonMain/composeResources/drawable/logo.png")  // Custom path relative to module

iosProjectPath should point to the inner folder that contains Info.plist and Assets.xcassets — typically iosApp/iosApp, not the root iosApp folder.

4. Add the dependencies

In your Compose App module build.gradle.kts:

commonMain.dependencies {
    implementation(libs.kmpSplash.runtime)
}

androidMain.dependencies {
    implementation("androidx.core:core-splashscreen:1.2.0")
}

5. Run the generation task (Optional)

KMP Splash is integrated into the Gradle build process. On both Android and iOS, the splash assets are generated automatically when you build or run your app.

If you ever want to trigger the generation manually, you can run:

# Generate iOS assets (Info.plist, pbxproj, xcassets)
./gradlew generateLaunchScreen

# Generate Android assets (themes.xml, logo)
./gradlew generateAndroidSplash

Important

iOS Simulator Caching: iOS heavily caches the launch screen. If you change the background color or logo and don't see the changes in the simulator, you must restart the simulator (or sometimes even delete and reinstall the app) for the new assets to be reflected.


Usage

Android

Extend SplashActivity in your MainActivity:

class MainActivity : SplashActivity() {

    override suspend fun isReady(): Boolean {
        delay(1000) // Load data, check auth, etc.
        return true
    }

    override fun onFinished() {
        setContent {
            App()
        }
    }
}

iOS

Call SplashConfig in your MainViewController:

fun MainViewController() = ComposeUIViewController {
    var isAppReady by remember { mutableStateOf(false) }

    if (!isAppReady) {
        SplashConfig(
            isReady = {
                delay(1500) // Your initialization logic
                true
            },
            onFinished = { isAppReady = true }
        )
    } else {
        App()
    }
}

Colors and logo are picked up automatically from your Gradle configuration — no extra parameters needed.


How it Works

Platform Native (Booting) Compose (Loading)
Android Generates themes.xml and values-night. Uses installSplashScreen(). Controlled by SplashActivity.
iOS Patches Info.plist with UILaunchScreen, generates SplashBackground color asset and logo imageset in Assets.xcassets. SplashConfig uses isSystemInDarkTheme() to match the native screen exactly.

The "Gap" Problem

When a KMP app starts on iOS, the OS displays the native launch screen immediately. Once the Kotlin runtime and Compose initialize (which can take 500ms+), the screen usually flashes white or black before your first Composable is rendered.

KMP Splash ensures the SplashConfig composable is visually identical to the native launch screen, providing seamless continuity that keeps your branding on screen until the app is actually ready.


Known Limitations

App-level dark mode overrides

If your app has its own appearance setting (e.g. a dark mode toggle independent of the system setting), the native splash screen will not respect it. Both iOS UILaunchScreen and Android's SplashScreen API are rendered by the OS before any app code runs, they read the system dark mode setting directly. There is no way for any library to work around this.

The Compose layer (SplashConfig / SplashActivity) does run app code, so it can respond to your app's own preference. For the native layer, the options are:

  • Use a single background color that works in both light and dark modes
  • Accept the brief mismatch, the native splash shows the system color, and the Compose layer immediately corrects to your app's preferred color

This is a system limitation, not a bug in the library.


Contributing

Contributions are welcome! If you find a bug or have a feature request, please open an issue or a pull request.


License

Copyright 2026 KMP Bits

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

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages