A splash screen in Android is that very first screen a user sees the moment they tap your app icon. Think of it as a branded welcome mat. It gives your app an immediate, professional feel and cleverly masks the initial setup time while everything boots up in the background.
Why Did We Need a Modern Splash Screen?


Before we jump into the how-to, it’s worth taking a moment to understand why Google introduced a whole new splash screen system. Its story is really about the evolution of Android itself—moving from a fragmented mess of workarounds to a slick, standardized user experience. For years, we developers were on our own, and the results were… inconsistent, to say the least.
From Clunky Workarounds to a Standardized API
In the old days, implementing a splash screen was a bit of a free-for-all. I’ve seen it all: custom Activity layouts with timers, postDelayed handlers, even full-screen Dialogs trying to display a logo while the real app loaded. While clever, these methods were often buggy and created a jarring flash of content as the app transitioned from the fake splash to the main UI.
This lack of a standard approach caused a few recurring headaches for developers and users alike:
- Inconsistent Experience: Every app launch felt different. Some were fast, some were slow, and the transitions were all over the place.
- Performance Hits: A poorly coded splash screen could ironically add to the startup time instead of just hiding it.
- The Dreaded "White Screen": This was the big one. A blank white screen would often flash for a moment before our custom splash even had a chance to appear, making the app feel sluggish and unpolished right from the start.
Thankfully, Google stepped in with the official SplashScreen API in Android 12, fundamentally changing how the system handles app launches.
The Core Components of the Modern Splash Screen
The beauty of the new API is that it’s a standardized, system-managed screen that every app gets by default. It just works. Our job is no longer to build a splash screen from scratch, but simply to customize the one the system provides.
It’s built on three simple elements you can tweak:
- App Icon: A
VectorDrawablesitting right in the center. You can even animate it. - Icon Background: An optional circular background for your icon, which is great for ensuring it pops against the window background.
- Window Background: A simple, solid color that fills the screen.
Because the system controls this initial window, the launch feels perfectly integrated with the rest of the Android OS. You get a smooth, clean transition from the user tapping your icon to your app’s UI being ready for them.
The core philosophy here is a complete reversal of the old way. Instead of us developers fighting the system to show a custom screen, the system now provides a screen for us to customize. This shift elegantly solves that "white screen" problem at its source.
This idea of using a screen to hide loading is nothing new, of course. It goes way back. On my old Sinclair ZX Spectrum in the 80s, loading a game from a cassette tape to fill its 16k of RAM could take ages. Developers used splash screens back then to build a bit of brand excitement and let you know something was actually happening. It’s a concept that carried through the decades right into mobile, where Google finally standardized it for the modern era. You can read more about the long history of splash screens and their early computer origins.
Implementing the Native Android Splash Screen API
Getting started with the modern splash screen in Android is a breath of fresh air, all thanks to Google's official SplashScreen API. This is the standardized, performant approach we've needed for years, and it's surprisingly simple to set up in a native Android project.
The first order of business is to add a single dependency to your app-level build.gradle or build.gradle.kts file. This small library from Android Jetpack is what gives you access to all the new splash screen functionality.
// In your build.gradle.kts (Module :app)
dependencies {
// … other dependencies
implementation("androidx.core:core-splashscreen:1.0.1")
}
Once that's in place, the real work happens in your project's theme files, which you'll typically find under res/values/themes.xml.
Creating Your Splash Screen Theme
The strategy here is to create a brand new theme specifically for the splash screen. This theme will inherit from Theme.SplashScreen and will be applied to your main launch Activity just before your regular app theme takes over.
First, let's define the new splash theme. Making sure its parent is one of the Theme.SplashScreen variants is the most important part.
Let's quickly go over what each of these attributes does:
windowSplashScreenBackground: This is a simple, solid color that fills the screen. It’s the very first thing a user sees, so make sure it matches your brand.windowSplashScreenAnimatedIcon: This points to your app's icon. For the best results, use aVectorDrawableso it looks sharp on any screen density. Don't let the "animated" name fool you; a static drawable works perfectly fine here. We’ll get to actual animations a bit later.postSplashScreenTheme: This is a crucial instruction. It tells the system which theme to switch to after the splash screen has done its job. This should point to your main application theme.
With your new theme defined, the next step is to tell Android to actually use it for your app's launch. This is done in your AndroidManifest.xml file.
By setting the android:theme attribute directly on your launch Activity, you guarantee that your custom splash theme is used from the very first moment of a cold or warm start. This is the secret to finally banishing that jarring white screen for good. The system then handles the transition to your postSplashScreenTheme automatically.
Controlling the Splash Screen Dismissal
The final piece of the puzzle is telling the splash screen when to disappear. By default, it's gone the instant your app draws its first frame. In the real world, though, you often need to hold it on-screen a little longer while you fetch initial data or finish setting up your UI.
You handle this right inside your Activity's onCreate method. The key is to call installSplashScreen() before you call super.onCreate() and setContentView().
When the SplashScreen API rolled out with Android 12, it was a game-changer, standardizing launches across more than 3 billion devices. It kicks in on cold starts (when the app process is created from scratch) and warm starts (when the app process exists but the Activity doesn't). To keep the experience pleasant, Google strongly recommends that any animations don't exceed 1,000 milliseconds. You can dive deeper into these guidelines and the official API's behavior on developer.android.com.
Here's a great visual of how the modern splash screen flows, showing the icon animation sequence.
This flow demonstrates the "into-app motion" concept, where the icon can smoothly morph into your app's content, creating a seamless and polished first impression.
To keep the splash screen on-screen while you're loading, you can attach a condition that tells it when you're ready.
// In MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// This must be called before super.onCreate()
val splashScreen = installSplashScreen()
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var isReady = false
// Replace this with your actual data loading, e.g., from a ViewModel
viewModel.loadInitialData().observe(this) { data ->
// Once data is loaded, you're ready to show the UI
isReady = true
}
// Keep the splash screen on-screen until the 'isReady' flag is true.
splashScreen.setKeepOnScreenCondition { !isReady }
}
}
In this code, setKeepOnScreenCondition takes a simple lambda. As long as it returns true, the splash screen stays put. The moment it returns false, the splash screen dismisses and your app's UI appears. This gives you precise control, ensuring users only see your main screen when it's genuinely ready for them. If you're just getting started with native development, you might also find our guide comparing Android Studio vs IntelliJ for native development helpful for setting up your environment.
Integrating a Native Splash Screen in Flutter
Even when we're building with Flutter's single codebase, we can't afford to ignore native platform best practices. A proper splash screen in android is a hallmark of a polished app, and getting the modern Android 12+ API working in a Flutter project is surprisingly painless.
You've really got two main paths you can take. The first, and my personal recommendation for most projects, is to use a package that does the heavy lifting. The other option is to roll up your sleeves and configure the native Android files yourself, which gives you maximum control. Both methods have the same end goal: a buttery-smooth transition from the native splash to your Flutter UI, banishing that ugly white screen for good.
The Easy Way with the flutter_native_splash Package
The Flutter community has blessed us with a fantastic package called flutter_native_splash. This is my go-to because it handles the modern Android 12 API perfectly while also providing backward-compatible fallbacks for older devices. Best of all, it works its magic from a single configuration block.
To get going, just add the package to your pubspec.yaml file.
dependencies:
flutter:
sdk: flutter
flutter_native_splash: ^2.4.0
Now for the fun part. You'll add a new flutter_native_splash block to your pubspec.yaml. This is where you tell the package what you want your splash screen to look like, and it takes care of generating the native files.
flutter_native_splash:
color: "#FFFFFF"
image: assets/splash_icon.png
android_12:
color: "#FFFFFF"
image: assets/splash_icon.png
Here, we've set a white background and pointed to our logo. Take note of the android_12 section—this lets you provide specific assets for the modern API, giving you precise control over how your splash appears on the latest devices.
Once you’ve saved the file, just run one command in your terminal.
dart run flutter_native_splash:create
This command reads your config and generates all the necessary XML drawables and themes right inside your project's android/app/src/main/res directory. It's a huge time-saver and gets the job done right.
Whether you use a package or go the manual route, the core process remains the same. This flowchart breaks it down beautifully.


It really is that simple: add your assets, set up the theme, and then manage the transition into your app. Nail these three steps, and you're golden.
Deciding which of these two approaches is right for your project depends on your specific needs and comfort level with native code. Here's a quick breakdown to help you choose.
Flutter Splash Screen Integration Methods
| Method | Pros | Cons | Best For |
|---|---|---|---|
flutter_native_splash Package | – Fast and easy setup – Handles Android 12 and older versions – Minimal native code interaction | – Less control over complex animations – Adds a package dependency | Most projects, especially for developers who want a quick, reliable solution without diving into native files. |
| Manual Native Setup | – Full control over every detail – Allows for custom exit animations – No extra dependencies | – More complex and time-consuming – Requires knowledge of Android XML themes | Projects needing highly custom splash animations, or developers who are already comfortable working in the android folder. |
Ultimately, both paths lead to a professional-looking splash screen. The package is perfect for getting a great result quickly, while the manual method offers a level of customization that some advanced projects require.
The Manual Native Implementation for Full Control
While a package is convenient, sometimes you just need to get your hands dirty. If you're implementing a slick exit animation or have to integrate with other native components, the manual approach is the way to go. You’ll be directly editing the same kinds of files the package would generate for you.
The steps are basically the same as a pure native implementation, just applied inside a Flutter project’s android folder.
- Define a Launch Theme: In
android/app/src/main/res/values/styles.xml(orthemes.xml), you'll create a new theme that inherits fromTheme.SplashScreenand set your icon and background. - Apply the Theme: Jump over to
android/app/src/main/AndroidManifest.xml, find your main<activity>, and setandroid:theme="@style/YourSplashTheme"to use the theme you just created. - Handle the Handoff: This is the most important step. You have to tell the native splash to wait until Flutter has drawn its first frame. If you skip this, you’ll get that jarring flash of a blank screen.
You'll add a snippet to your MainActivity.kt (or MainActivity.java) that uses setKeepOnScreenCondition. This is what bridges the gap between the native splash and your Flutter UI.
Pro Tip: The whole trick in a Flutter app is keeping the splash visible until the Flutter engine is ready and your first widget is on screen. The
flutter_native_splashpackage handles this by having you callFlutterNativeSplash.remove()in your Dart code when the app's UI is loaded.
No matter which path you take, the result is a clean, professional launch experience. Your choice simply boils down to your project's needs and how comfortable you are with native Android code.
To see these ideas in practice, check out our collection of splash screen samples and examples. Seeing different branding and animation approaches can give you some great inspiration for making your app's launch truly memorable.
Advanced Customization and Performance Tuning


A standard, static splash screen in android gets the job done, but a truly great one feels both fast and deeply connected to your brand. This is where you can elevate your app from just another icon on the home screen to a memorable experience. By injecting some slick motion and keeping a close eye on performance, you can create a launch that actually delights users instead of just making them wait.
The best way to add that personality is by animating your splash icon. The modern SplashScreen API was practically built for AnimatedVectorDrawable (AVD). It's a lightweight, XML-based format that ensures your animations scale perfectly on any screen without losing quality. Forget about wrestling with heavy GIFs or video files—AVD is the professional's choice here.
An AVD is really just two files working together: a static VectorDrawable for your icon's shape and an XML animator file that tells it how to move.
Crafting a Custom Animated Splash Icon
So, let's say you want to make your logo fade in while doing a full rotation. It’s a classic, clean effect. You’d start by defining this animation in a dedicated XML file, which you'll place in your res/animator directory.
The key here is to keep it brief. A splash screen should be a fleeting moment. The official recommendation is to keep the entire experience, animation included, under 1,000 milliseconds. Anything longer starts to feel sluggish.
This simple XML snippet defines a rotation and fade-in that both finish in a snappy 800ms. You then link this animation to your vector drawable to create the final AVD, which you reference in your splash screen theme. The result is a smooth, branded animation that plays automatically on Android 12+.
For more complex visuals, like morphing shapes, you could even add a Flutter background animation layer once the main app UI is active, creating a seamless and impressive transition.
Perfecting the Exit with OnExitAnimationListener
But a great entrance is only half the story. The transition out of the splash screen is just as crucial for a polished feel. Instead of the icon just vanishing, you can create a custom exit animation that elegantly moves the splash screen element to its final spot in your app's layout.
This is where the SplashScreen.OnExitAnimationListener comes in. This listener gives you direct control over the splash screen’s view (SplashScreenView), allowing you to animate it away just as your main content appears.
// In your MainActivity's onCreate()
splashScreen.setOnExitAnimationListener { splashScreenView ->
// Create your custom exit animation.
val slideUp = ObjectAnimator.ofFloat(
splashScreenView,
View.TRANSLATION_Y,
0f,
-splashScreenView.height.toFloat()
)
slideUp.interpolator = AnticipateInterpolator()
slideUp.duration = 300L
// Make sure to remove the view when the animation is done.
slideUp.doOnEnd { splashScreenView.remove() }
// Kick off the animation.
slideUp.start()
}
This is an incredibly powerful technique for what’s often called "into-app motion." Imagine your splash icon shrinking down and settling perfectly into your app's toolbar. It provides a fluid, continuous journey for the user, connecting the launch directly to the first interaction.
The
OnExitAnimationListeneris your secret weapon for creating a polished handoff from the splash screen to your app. It gives you the control to choreograph the final moments of the launch, making the entire experience feel seamless and intentional.
Measuring and Optimizing Startup Performance
Of course, no amount of beautiful animation can fix a slow app. App startup time is a critical metric. While the SplashScreen API does a great job of masking load times, our real goal should always be to shrink those times in the first place.
You need to measure your Time to Initial Display (TTID). This is the stopwatch from the moment a user taps your app icon to the instant the very first frame of your UI is drawn on screen. You have a few great tools to measure this:
- Android Studio Profiler: The CPU profiler is your best friend for finding methods and processes that are clogging up the main thread during startup.
- Perfetto Traces: For a super-detailed, system-level view, capturing a Perfetto trace shows you exactly what’s happening across the entire system during your app's launch.
- Logcat: The quickest and dirtiest method. Filter Logcat for "Displayed" messages, and you'll see the TTID reported for recent launches.
Common bottlenecks I see all the time are heavy initializations in the Application class, overly complex view hierarchies, or—the classic—synchronous network calls on the main thread. Profile your app, find these expensive operations, and defer them until after your first UI is visible. Doing so will drastically improve your TTID and make your launch feel lightning-fast.
Avoiding Common Pitfalls and Launch Issues


The modern API for the splash screen in android is a huge improvement, but it's not a magic wand. Even with these better tools, some of the classic launch headaches can still creep in if you're not paying attention. Think of this as a field guide from someone who's seen it all—we'll walk through the common traps so you can sidestep them entirely.
Getting your app's first impression right is crucial. By learning from the common mistakes, you can fix problems before they ever make it to your users, from pixelated icons to a splash screen that overstays its welcome.
The Dreaded White Screen Flash
This is the big one. The most common and jarring issue is that brief, ugly white screen that flashes between your splash screen and your actual app UI. It instantly makes an otherwise polished app feel cheap and janky.
The problem is almost always a timing mismatch—the system dismisses the splash screen before your app has its first frame ready to draw. To fix this, you need to take control of that handoff.
- Native Android: The key is
splashScreen.setKeepOnScreenCondition { !isReady }. This simple line tells the system to hold the splash screen until yourisReadyflag (which you control) flips to true. You flip it once your data is loaded or your initial UI is prepped. - Flutter: If you're using a package like
flutter_native_splash, the principle is the same. You just need to be deliberate about callingFlutterNativeSplash.remove()in your Dart code after your app has finished its initial setup.
Coordinating this transition is what creates that seamless flow from your branded launch into the app itself.
Solving Pixelated or Blurry Icons
Okay, you've fixed the timing. The next thing you might notice is that your beautiful logo looks fuzzy or pixelated, especially on newer phones with high-resolution screens. This is a dead giveaway that you're using a raster image (like a PNG) for your windowSplashScreenAnimatedIcon.
The solution is non-negotiable: always use a VectorDrawable (XML) for your splash icon.
Vectors are just math—lines and curves, not pixels. They scale perfectly to any screen density without losing an ounce of quality. Your logo will look sharp and clean on every device, every time. If your design team hands you a PNG, it is absolutely worth the effort to convert it to a proper VectorDrawable.
The
SplashScreenAPI is built forVectorDrawable. Using a pixel-based image isn't just a quality compromise; you're also giving up the ability to useAnimatedVectorDrawablefor slick, performant animations right out of the box.
Handling Different Icon Shapes and OEMs
Ever notice how your icon looks great on a Google Pixel but gets weirdly cropped or looks tiny on a Samsung or Xiaomi device? That's not your fault—it's the result of different manufacturers (OEMs) applying their own unique masks to app icons.
To defend against this, you need to design your icon with a "safe zone." As a rule of thumb, your actual logo content should only take up the inner 66% of the drawable's total canvas. This creates enough padding around the edges to accommodate any circle, squircle, or teardrop mask an OEM launcher throws at it without butchering your brand.
Finally, here's a quick gut-check list to keep you on the right path:
- Keep Branding Minimal: The splash screen is for a quick brand hit, not a billboard. A clean logo on a solid background is all you need. Avoid taglines or complex images.
- Never Show Ads: Don't do it. Using this launch moment for an ad is one of the fastest ways to annoy a user and earn an uninstall.
- Test on Real Devices: Emulators are fantastic, but they don't have OEM-specific software quirks. Testing on a few different real-world phones is the only way to catch these issues and see how your splash performs on lower-end hardware.
- Watch the Clock: Always remember the 1000ms rule. From launch to interactive app, the entire splash experience should be over in a second or less.
By proactively tackling these common issues, you'll ensure your splash screen is a professional and positive start to your app's user experience.
Common Splash Screen Questions Answered
Even with Google's modern API, a few tricky questions always seem to surface when you're implementing a new splash screen in android. Let's walk through some of the common hangups I see developers run into.
How Do I Handle Older Android Versions?
This is a big one. For devices running Android 11 (API 30) or older, the SplashScreen compatibility library simply can't backport the fancy new animations or the OnExitAnimationListener. You're stuck with the old ways.
That means falling back to a legacy approach: a dedicated launch Activity that uses a custom theme to set a drawable as the window background. This classic screen holds the fort while your main app loads.
This is exactly why using a package like
flutter_native_splashis such a time-saver. It automatically generates both the modern Android 12+ setup and the necessary legacy implementation. You get proper behavior on all versions without having to manage two separate code paths yourself.
Why Is My Animated Splash Icon Not Working?
This one trips up a lot of developers, but the fix is usually straightforward. Nine times out of ten, it boils down to one of these common mistakes.
First, check your file type. The API is built exclusively for AnimatedVectorDrawable (AVD) files. If you're trying to use a GIF or some other format, it's just not going to work.
Also, pay close attention to your animation's duration. The system imposes a hard limit of 1,000 milliseconds. If your AVD runs for a single millisecond longer, it might not play at all. Keep it short and sweet.
Still stuck? Run through this quick checklist:
- Did you set the icon using
<item name="windowSplashScreenAnimatedIcon">in your splash theme? - Are you testing on a device running Android 12 (API 31) or newer? The animations won't work on older OS versions.
Can I Just Skip the Splash Screen Entirely?
In a word, no. Since Android 12, the system automatically adds a default splash screen to every app, whether you want one or not. This is a deliberate change to provide consistent, immediate feedback when a user launches an app from a cold or warm start.
You can't "disable" it, so you might as well own it. If you do nothing, your users will see a generic, unpolished screen with just your app icon. The best practice is to provide a simple, branded theme. That's the only way to control the launch experience and make sure it feels like a professional part of your app.
At Flutter Geek Hub, we're dedicated to helping you master every aspect of app development. For more deep dives, practical tutorials, and performance tips, visit https://fluttergeekhub.com.


















