Skip to content

Do You Really Need an XML Theme for Splash Screen API in a Compose-only Android App?

I’m building a fully Compose-based Android app. No XML layouts, no Views, just Compose everywhere. Then I tried to implement the Splash Screen API and hit a wall: it requires an XML theme. Wait, isn’t Compose supposed to make XML obsolete?

The Problem

I started with a clean Compose-only project and wanted to add a splash screen using Android’s modern Splash Screen API (Android 12+). My first instinct was to define everything in Compose code.

After all, my entire UI is Compose. I define colors in Color.kt, typography in Type.kt, and shapes in Shape.kt. Why would I need an XML theme?

But when I tried to implement the splash screen without an XML theme, nothing worked. The splash screen wouldn’t show, or it would crash with theme-related errors.

The Direct Answer

Yes, you need at least a minimal XML theme for the Splash Screen API, even in a Compose-only app. While your app’s UI is built entirely with Compose, Android’s splash screen mechanism operates at the Activity/View level and requires XML theme configuration to function properly.

Why This Matters: The Platform Reality

Here’s the truth that took me a while to accept:

Android UI Architecture Reality
┌─────────────────────────────────────────────────────────────┐
│ Android Platform │
├─────────────────────────────────────────────────────────────┤
│ Activity/Window Level: STILL XML/View-based │
│ ├── Theme resolution │
│ ├── Splash Screen API │
│ ├── Window insets │
│ └── System bars (status bar, navigation bar) │
├─────────────────────────────────────────────────────────────┤
│ UI Level: Compose (Modern) │
│ ├── All your app UI │
│ ├── Material3 components │
│ └── Custom composables │
└─────────────────────────────────────────────────────────────┘

As one developer on Reddit put it:

“The native Android UI is still Activity/View/XML based, even if we all work with compose now.”

The Splash Screen API hooks into the Activity lifecycle and Window theme before Compose even initializes. You can’t bypass this with Compose code.

What I Discovered in the Source Code

I dug into the SplashScreen library source code and found something surprising. The Android team’s implementation uses android:Theme.DeviceDefault.NoActionBar as the base theme, not a Material theme.

From SplashScreen library source
<style name="Theme.SplashScreen" parent="android:Theme.DeviceDefault.NoActionBar">
<item name="android:windowBackground">@drawable/splash_screen_background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="postSplashScreenTheme">@style/Theme.AppCompat.DayNight</item>
</style>

This revealed a key insight: postSplashScreenTheme doesn’t necessarily require a Material theme. You can use a minimal Android base theme.

The Solution: Minimal XML Theme for Compose Apps

Here’s the minimal XML configuration you need:

Step 1: Create the Splash Theme

res/values/themes.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Splash screen theme -->
<style name="Theme.App.Splash" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/splash_background</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>
<item name="windowSplashScreenAnimationDuration">200</item>
<item name="postSplashScreenTheme">@style/Theme.App</item>
</style>
<!-- Minimal app theme for after splash -->
<style name="Theme.App" parent="android:Theme.DeviceDefault.NoActionBar">
<!-- No Material dependencies needed -->
<!-- Compose will handle all styling -->
</style>
</resources>

Step 2: Create the Splash Icon Drawable

res/drawable/splash_icon.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:width="108dp"
android:height="108dp"
android:drawable="@mipmap/ic_launcher_round"
android:gravity="center" />
</layer-list>

Step 3: Define the Background Color

res/values/colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="splash_background">#FFFFFF</color>
</resources>

Step 4: Update AndroidManifest

AndroidManifest.xml
<application
android:theme="@style/Theme.App.Splash">
<activity
android:name=".MainActivity"
android:theme="@style/Theme.App.Splash"
android:exported="true">
<!-- ... -->
</activity>
</application>

Step 5: Install the Splash Screen in MainActivity

MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Install splash screen BEFORE calling super.onCreate()
val splashScreen = installSplashScreen()
super.onCreate(savedInstanceState)
// Optional: Keep splash screen visible while loading
splashScreen.setKeepOnScreenCondition {
// Return true to keep splash visible, false to dismiss
viewModel.isLoading.value
}
setContent {
// Your Compose UI here
AppTheme {
// All your Compose styling lives here
NavHost(...) { ... }
}
}
}
}

The Complete Picture: Hybrid Approach

Here’s how the two systems work together:

Splash Screen Flow
┌────────────────────────────────────────────────────────────────┐
│ 1. App Launch │
│ └── Android reads Theme.App.Splash from XML │
│ │
│ 2. Splash Screen Visible │
│ └── Shows icon and background from XML drawable/color │
│ │
│ 3. MainActivity.onCreate() │
│ └── installSplashScreen() called │
│ │
│ 4. Splash Screen Transition │
│ └── Android switches to postSplashScreenTheme (XML) │
│ │
│ 5. Compose Initializes │
│ └── setContent { AppTheme { } } takes over styling │
│ │
│ 6. App Running │
│ └── All UI styling is now 100% Compose │
└────────────────────────────────────────────────────────────────┘

Common Mistakes I’ve Seen

Mistake 1: Trying to Define Splash Theme in Compose

WRONG: This doesn't work
// You cannot define splash screen styling in Compose
setContent {
SplashTheme { // This is too late!
// The splash screen already showed and dismissed
}
}

The splash screen appears before setContent() is even called. You must define it in XML.

Mistake 2: Using Material Theme for postSplashScreenTheme When Not Needed

UNNECESSARY: Adding Material dependency just for splash
<style name="Theme.App" parent="Theme.Material3.DayNight.NoActionBar">
<!-- This adds Material dependency weight -->
<!-- But your app is Compose-only anyway -->
</style>

If you’re not using Views, use the minimal DeviceDefault theme:

BETTER: Minimal theme for Compose apps
<style name="Theme.App" parent="android:Theme.DeviceDefault.NoActionBar">
<!-- No Material dependencies -->
<!-- Compose Material3 handles all UI styling -->
</style>

Mistake 3: Not Calling installSplashScreen()

WRONG: Missing installSplashScreen()
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) // Splash won't animate out
setContent { ... }
}
}

You must call installSplashScreen() before super.onCreate().

Mistake 4: Forgetting to Set Theme in AndroidManifest

WRONG: Theme not applied to Activity
<activity android:name=".MainActivity">
<!-- Theme.App.Splash not set here -->
</activity>

The theme must be set on both the application and the activity.

Why Not Define Everything in XML Then?

Good question. Here’s the trade-off:

AspectXML ThemeCompose Theme
Splash screenRequiredCannot replace
System barsOptional in XMLBetter in Compose
App UI stylingPossiblePreferred
Dynamic colorsLimitedEasy
AnimationLimitedFull power
State-based stylingDifficultNatural

The hybrid approach gives you the best of both:

  1. XML handles the platform layer (splash screen, window configuration)
  2. Compose handles the UI layer (everything the user interacts with)

Real-World Example: Material3 Compose App

Here’s how I structure a production Compose app:

Project Structure
app/
├── src/main/
│ ├── res/
│ │ ├── values/
│ │ │ ├── colors.xml # Only splash colors
│ │ │ └── themes.xml # Only splash theme
│ │ └── drawable/
│ │ └── splash_icon.xml # Splash icon only
│ └── ...
└── ui/
└── theme/
├── Color.kt # All app colors
├── Theme.kt # Compose theme
└── Type.kt # Typography

The XML files are minimal and only serve the splash screen. Everything else is Compose.

The Migration Path

If you’re migrating an existing app to Compose:

  1. Keep your existing XML theme for now
  2. Add splash screen theme as a new style
  3. Gradually move styling to Compose as you migrate screens
  4. Eventually reduce XML theme to minimal (just splash + window config)

Summary

Even in a Compose-only Android app, you need XML for the Splash Screen API. This isn’t a failure of Compose - it’s a recognition of how Android’s platform architecture works.

The splash screen operates at the Activity/Window level, before Compose initializes. The solution is a minimal XML theme that handles only the splash screen, while Compose handles all your app’s UI styling.

Key takeaways:

  • Use Theme.SplashScreen as the parent for your splash theme
  • Set postSplashScreenTheme to a minimal DeviceDefault theme, not Material
  • Call installSplashScreen() before super.onCreate()
  • Keep XML minimal - only what the platform requires
  • Let Compose handle all user-facing UI styling

The hybrid approach isn’t a compromise - it’s the correct architecture for modern Android development.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments