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 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.
<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
<?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
<?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
<?xml version="1.0" encoding="utf-8"?><resources> <color name="splash_background">#FFFFFF</color></resources>Step 4: Update AndroidManifest
<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
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:
┌────────────────────────────────────────────────────────────────┐│ 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
// You cannot define splash screen styling in ComposesetContent { 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
<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:
<style name="Theme.App" parent="android:Theme.DeviceDefault.NoActionBar"> <!-- No Material dependencies --> <!-- Compose Material3 handles all UI styling --></style>Mistake 3: Not Calling 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
<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:
| Aspect | XML Theme | Compose Theme |
|---|---|---|
| Splash screen | Required | Cannot replace |
| System bars | Optional in XML | Better in Compose |
| App UI styling | Possible | Preferred |
| Dynamic colors | Limited | Easy |
| Animation | Limited | Full power |
| State-based styling | Difficult | Natural |
The hybrid approach gives you the best of both:
- XML handles the platform layer (splash screen, window configuration)
- 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:
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 # TypographyThe 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:
- Keep your existing XML theme for now
- Add splash screen theme as a new style
- Gradually move styling to Compose as you migrate screens
- 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.SplashScreenas the parent for your splash theme - Set
postSplashScreenThemeto a minimal DeviceDefault theme, not Material - Call
installSplashScreen()beforesuper.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:
- 👨💻 Reddit Discussion: Do you still use a theme.xml for Jetpack Compose apps?
- 👨💻 Android Splash Screen API Guide
- 👨💻 SplashScreen Library Source Code
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments