Flutter Animated Splash Screen Android 12+ Lottie
Implement reliable Flutter animated splash screen with Lottie on Android 12+. Sync system launch screen, control animation timing using flutter_native_splash, installSplashScreen, and MethodChannel for smooth startup without premature playback or unresponsive taps.
Flutter animated splash screen on Android 12+ (Lottie starts too early)
I want an animated splash screen in my Flutter app, but on Android 12+ I’m running into these problems:
- Android 12+ shows a default system launch/splash screen that can’t be removed. I made it transparent, but the system splash still appears and the app takes a few seconds to open.
- My Lottie animation starts while the system splash is still visible, so the animation is half completed by the time the app UI appears.
- I want the app’s animated splash (Lottie) to play after the system launch screen is gone and be able to delay the animation until Flutter is ready.
- I tried reversing steps and using the splash_master package, but it didn’t work; the default splash still stays longer than usual and the app appears unresponsive when tapped.
How can I implement a reliable animated splash screen in Flutter (using Lottie) on Android 12+ so that:
- The Android system launch screen does not prematurely consume or hide my animation, or is shortened/hidden properly.
- The Lottie animation only starts after Flutter is initialized and the app UI is visible.
- I can control/delay the animation start time.
Please include required Android configuration (themes/styles/AndroidManifest), Flutter lifecycle tips, and working implementation examples or recommended packages to synchronize the system launch screen with a Flutter/Lottie animated splash screen on Android 12+.
To create a reliable Flutter animated splash screen on Android 12+ with Lottie, customize the native system splash using the flutter_native_splash package for a branded static screen, then trigger your Lottie animation only after Flutter initializes fully—via FlutterNativeSplash.preserve() and .remove(). On the Android side, leverage installSplashScreen() with setKeepOnScreenCondition in MainActivity.kt to hold the system splash until your app signals readiness, preventing premature Lottie playback. This syncs everything perfectly, letting you delay the animation start with WidgetsBinding.instance.addPostFrameCallback or async init checks.
Contents
- Android 12+ Splash Screen Behavior
- Setting Up flutter_native_splash
- Android Native Configuration
- Building the Lottie Splash in Flutter
- Synchronizing Native and Flutter Sides
- Controlling Lottie Timing and Delays
- Troubleshooting and Best Practices
- Sources
- Conclusion
Android 12+ Splash Screen Behavior
Android 12 flipped the script on splash screens. No more full-custom bitmaps that lag or flicker—Google mandated a crisp system API with a centered icon, optional branding below it, and a solid background. You can’t nuke it entirely; it flashes before your Flutter engine even stirs. That’s why your Lottie animation jumps the gun: it renders in Flutter’s first frame while the system splash lingers.
But here’s the fix. The Android developer docs detail installSplashScreen() in your MainActivity. Pair it with setKeepOnScreenCondition { !isFlutterReady }—a boolean you flip when Flutter yells “I’m good.” This holds the native splash (your branded version via flutter_native_splash) until everything aligns. Think of it as a gatekeeper: system splash stays put, no white flash, no half-played Lottie.
Why does this beat hacks like transparent themes? Transparency triggers the system default anyway, and tapping feels unresponsive because Flutter’s still booting. Real users notice that 2-3 second void.
Setting Up flutter_native_splash
Grab the flutter_native_splash package—it’s the gold standard for Flutter splash screen on Android 12+. Add it to pubspec.yaml:
dev_dependencies:
flutter_native_splash: ^2.4.1 # Latest as of 2025
flutter_native_splash:
color: "#your_brand_color"
image: assets/splash_logo.png # Centered icon
branding: assets/branding.png # Optional bottom logo
android_12:
image: assets/splash_logo.png
color: "#your_brand_color"
Run flutter pub get, then flutter pub run flutter_native_splash:create. Boom— it spits out styles.xml, drawables, and manifest tweaks automatically. No manual XML wrestling.
This package hides the default system launch by overriding the theme’s windowBackground and icon. But crucially, use FlutterNativeSplash.preserve() in main.dart to keep it visible until you call .remove(). That’s your hook for Lottie: start the animation right after removal.
Tried splash_master? It supports Lottie natively but skips deep Android 12 sync, leading to those unresponsive taps you saw. Stick with flutter_native_splash—it’s battle-tested.
Android Native Configuration
Dive into android/app/src/main/res/values/themes.xml (generated by the package):
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
For Android 12+, ensure android/app/src/main/AndroidManifest.xml uses android:theme="@style/LaunchTheme" on the <activity>. Add the SplashScreen compat lib in android/app/build.gradle:
dependencies {
implementation "androidx.core:core-splashscreen:1.0.1"
}
Now, the magic in android/app/src/main/kotlin/.../MainActivity.kt:
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
class MainActivity: FlutterActivity() {
private var flutterReady = false
override fun onCreate(savedInstanceState: Bundle?) {
// Hold splash until Flutter signals
SplashScreen.installSplashScreen(this).setKeepOnScreenCondition {
!flutterReady
}
super.onCreate(savedInstanceState)
}
// MethodChannel callback to set flutterReady = true
private val channel = "splash_channel"
// ... (full sync code in next section)
}
This is pulled straight from GeeksforGeeks and Android docs. It delays UI handover until flutterReady flips, shortening that “unresponsive” feel.
Building the Lottie Splash in Flutter
In pubspec.yaml, add Lottie:
dependencies:
lottie: ^3.1.2
Craft a SplashScreen widget:
class LottieSplash extends StatefulWidget {
@override
_LottieSplashState createState() => _LottieSplashState();
}
class _LottieSplashState extends State<LottieSplash>
with TickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
FlutterNativeSplash.preserve(); // Keep native splash
_controller = AnimationController(vsync: this);
// Load and play Lottie only after native splash gone
WidgetsBinding.instance.addPostFrameCallback((_) {
FlutterNativeSplash.remove(); // Now show Lottie
_controller
..duration = const Duration(seconds: 3)
..forward();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Lottie.asset('assets/splash_lottie.json',
controller: _controller, onLoaded: (composition) {
_controller.duration = composition.duration;
}),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
Set it as home in main.dart: home: LottieSplash(). The flutter animation kicks in post-native removal. Smooth.
Synchronizing Native and Flutter Sides
Basic preserve/remove works for most, but for pixel-perfect sync on Android 12 splash screen, use a MethodChannel. In Flutter main.dart:
import 'package:flutter/services.dart';
static const platform = MethodChannel('splash_channel');
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await yourAsyncInit(); // DB, API, etc.
// Signal Android: Flutter ready!
await platform.invokeMethod('flutterReady');
runApp(MyApp());
}
Back in MainActivity.kt:
private val channel = MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, "splash_channel")
channel.setMethodCallHandler { call, result ->
if (call.method == "flutterReady") {
flutterReady = true
result.success(null)
}
}
Now setKeepOnScreenCondition waits for your init. From Medium’s 2025 guide, this ties splash duration to real loading.
Controlling Lottie Timing and Delays
Want more delay? Wrap Lottie start in Future.delayed:
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Future.delayed(Duration(seconds: 1)); // Extra buffer
FlutterNativeSplash.remove();
_controller.forward();
});
Or tie to async tasks: await initPrefs(); before removal. This beats reversing animations—users hate backwards Lottie flutter. For variable timing, use setKeepOnScreenCondition with a ViewModel boolean, as in DEV Community.
Pro tip: Test on real devices. Emulators fake boot times.
Troubleshooting and Best Practices
Lottie starts too early? Forgot preserve(). Unresponsive taps? System splash blocking—add setKeepOnScreenCondition. White flash? Match colors in themes.xml and Lottie background.
From Reddit threads, Android 12 enforces icon+background—no videos natively. Pub.dev docs warn: call remove() post-frame.
Best practices:
- Keep native splash <2s: lightweight logo.
- Lottie: <5s, 60fps, under 1MB.
- Profile with Flutter DevTools: trace engine init.
- iOS parity:
flutter_native_splashhandles it.
Scale with splash_master only if you need Rive too, but debug Android first.
Sources
- flutter_native_splash on pub.dev
- Android Splash Screens Official Docs
- Splash Screen in Android - GeeksforGeeks
- Building a Splash Screen in Android (2025 Edition) - Medium
- Modern Android Splash Screen - DEV Community
- Flutter Splash Screen Setup - Codehub Journal
Conclusion
Syncing a Flutter animated splash screen with Android 12+ boils down to native customization via flutter_native_splash, installSplashScreen with conditionals, and precise Lottie triggers post-init. You’ll ditch those half-played animations and laggy starts—users get a pro feel from boot to Lottie flutter glory. Test iteratively, and your app launches like silk.