Add a Splash Screen in Flutter
Splash screens provide users with an immediate branded experience during app initialization. This guide covers both automated and manual approaches to implement professional splash screens across all Flutter-supported platforms, ensuring smooth user experience and consistent branding.
Comprehensive Guide to Implementing Splash Screens in Flutter
Splash screens provide users with an immediate branded experience during app initialization. This guide covers both automated and manual approaches to implement professional splash screens across all Flutter-supported platforms, ensuring smooth user experience and consistent branding.
Recommended Approach: Using flutter_native_splash Package
The *flutter_native_splash* package automates splash screen configuration across multiple platforms, providing consistent behavior and reducing platform-specific configuration complexity.
Step 1: Add Required Dependency
Include the *flutter_native_splash* package in your project's development dependencies.
dev_dependencies:
flutter_native_splash: ^<version_number>Step 2: Comprehensive Splash Screen Configuration
Configure splash screen properties for all supported platforms in your *pubspec.yaml* file.
flutter_native_splash:
# Basic configuration
color: "#<background_color>"
image: assets/images/splash_logo.png
# Platform enablement
android: true
ios: true
web: true
windows: true
macos: true
linux: true
# Android-specific configuration
android:
enable_full_screen: false
status_bar_color: "#<status_bar_color>"
navigation_bar_color: "#<navigation_bar_color>"
navigation_bar_style: light # or dark
# iOS-specific configuration
ios:
enable_full_screen: false
status_bar_style: dark # or light
# Web-specific configuration
web:
background_color: "#<web_background_color>"
# Image configuration
image_dark: assets/images/splash_logo_dark.png
background_image: assets/images/splash_background.png
# Branding configuration
branding: assets/images/splash_branding.png
branding_mode: bottom # or top
# Animation and timing
duration: 3000
fading_duration: 500
# Platform-specific image overrides
android_image: assets/images/splash_android.png
ios_image: assets/images/splash_ios.png
web_image: assets/images/splash_web.pngStep 3: Generate Splash Screens
Execute the package commands to generate platform-specific splash screen resources.
# Install dependencies
flutter pub get
# Generate splash screens for all platforms
flutter pub run flutter_native_splash:create
# Remove existing splash screens
flutter pub run flutter_native_splash:remove
# Create with specific configuration
flutter pub run flutter_native_splash:create --path=splash.yamlManual Setup Approach
For complete control over splash screen behavior and appearance, implement platform-specific configurations manually.
Android Manual Configuration
Configure native Android splash screens with proper theming and drawable resources.
Android Implementation Steps:
- Create *launch_background.xml* in *android/app/src/main/res/drawable/*
- Define splash screen layout with background and logo
- Configure splash theme in *styles.xml*
- Update *AndroidManifest.xml* to use splash theme
- Add appropriate image resources in density-specific directories
<!-- android/app/src/main/res/drawable/launch_background.xml -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@color/splash_background" />
<item>
<bitmap
android:gravity="center"
android:src="@mipmap/splash_logo" />
</item>
<!-- Optional: Branding at bottom -->
<item
android:bottom="48dp"
android:gravity="bottom|center_horizontal">
<bitmap
android:gravity="center"
android:src="@mipmap/splash_branding" />
</item>
</layer-list>
<!-- android/app/src/main/res/values/styles.xml -->
<style name="LaunchTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:statusBarColor">@color/splash_status_bar</item>
<item name="android:navigationBarColor">@color/splash_navigation_bar</item>
<item name="android:windowFullscreen">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
</style>
<!-- android/app/src/main/AndroidManifest.xml -->
<activity
android:name=".MainActivity"
android:theme="@style/LaunchTheme"
... >
</activity>iOS Manual Configuration
Implement native iOS splash screens using LaunchScreen.storyboard and asset catalogs.
iOS Implementation Steps:
- Open *ios/Runner.xcworkspace* in Xcode
- Edit *LaunchScreen.storyboard* for splash screen layout
- Add splash images to *Assets.xcassets*
- Configure auto-layout constraints for different devices
- Set appropriate background colors and image content modes
iOS Storyboard Configuration:
- Use *UIImageView* for splash logo
- Set content mode to *Aspect Fit*
- Configure background color view
- Add constraints for center alignment
- Support all iPhone and iPad screen sizes
- Consider safe area layouts
<!-- ios/Runner/Base.lproj/LaunchScreen.storyboard -->
<?xml version="1.0" encoding="UTF-8"?>
<document
type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB"
version="3.0"
toolsVersion="<xcode_version>"
targetRuntime="iOS.CocoaTouch"
propertyAccessControl="none"
useAutolayout="YES"
launchScreen="YES"
useTraitCollections="YES"
useSafeAreas="YES"
colorMatched="YES"
initialViewController="01J-lp-oVM">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="<plugin_version>"/>
</dependencies>
<scenes>
<!-- View Controller -->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController
id="01J-lp-oVM"
sceneMemberID="viewController">
<view
key="view"
contentMode="scaleToFill"
id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<!-- Background View -->
<view
contentMode="scaleToFill"
translatesAutoresizingMaskIntoConstraints="NO"
id="backgroundView">
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="height" constant="852" id="backgroundHeight"/>
<constraint firstAttribute="width" constant="393" id="backgroundWidth"/>
</constraints>
</view>
<!-- Logo Image View -->
<imageView
userInteractionEnabled="NO"
contentMode="scaleAspectFit"
horizontalHuggingPriority="251"
verticalHuggingPriority="251"
image="SplashLogo"
translatesAutoresizingMaskIntoConstraints="NO"
id="logoImageView">
<constraints>
<constraint firstAttribute="width" constant="200" id="logoWidth"/>
<constraint firstAttribute="height" constant="200" id="logoHeight"/>
</constraints>
</imageView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<!-- Center logo -->
<constraint firstItem="logoImageView" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="logoCenterX"/>
<constraint firstItem="logoImageView" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="logoCenterY"/>
<!-- Background constraints -->
<constraint firstItem="backgroundView" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="backgroundLeading"/>
<constraint firstItem="backgroundView" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="backgroundTop"/>
<constraint firstAttribute="trailing" secondItem="backgroundView" secondAttribute="trailing" id="backgroundTrailing"/>
<constraint firstAttribute="bottom" secondItem="backgroundView" secondAttribute="bottom" id="backgroundBottom"/>
</constraints>
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="SplashLogo" width="200" height="200"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>Web Platform Configuration
Implement web splash screens with proper CSS and HTML configuration for progressive web apps.
Web Implementation Steps:
- Customize *web/index.html* for splash screen styling
- Add splash screen CSS for initial loading state
- Implement JavaScript for splash screen dismissal
- Configure PWA manifest for splash screen behavior
- Optimize for various browser and device combinations
<!-- web/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="<app_description>">
<!-- iOS meta tags -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="<app_name>">
<!-- Splash screen styling -->
<style>
/* Splash screen styles */
.splash-screen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #<background_color>;
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
transition: opacity 0.5s ease-out;
}
.splash-screen.hidden {
opacity: 0;
pointer-events: none;
}
.splash-logo {
width: 200px;
height: 200px;
background-image: url('icons/splash_logo.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
/* Loading indicator */
.loading-indicator {
margin-top: 20px;
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #<accent_color>;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<!-- Splash Screen -->
<div id="splashScreen" class="splash-screen">
<div class="splash-content">
<div class="splash-logo"></div>
<div class="loading-indicator"></div>
</div>
</div>
<script>
// Splash screen handling
window.addEventListener('load', function() {
// Hide splash screen when Flutter is ready
setTimeout(function() {
const splashScreen = document.getElementById('splashScreen');
if (splashScreen) {
splashScreen.classList.add('hidden');
// Remove from DOM after transition
setTimeout(function() {
splashScreen.remove();
}, 500);
}
}, 3000); // Adjust timing as needed
});
// Service worker registration for PWA
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('flutter_service_worker.js');
});
}
</script>
<!-- Flutter app container -->
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>macOS Desktop Configuration
Implement native macOS splash screens with proper window management and visual design.
macOS Implementation Steps:
- Edit *macos/Runner/Base.lproj/MainMenu.xib*
- Configure initial window appearance
- Add splash screen view controller
- Implement splash screen dismissal logic
- Set appropriate window styling and transitions
Windows Desktop Configuration
Configure Windows splash screens with proper window styling and resource management.
Windows Implementation Steps:
- Modify *windows/runner/main.cpp* for splash screen
- Create splash screen window resource
- Implement splash screen timing logic
- Configure window transparency and styling
- Handle splash screen dismissal properly
Advanced Splash Screen Features
Implement enhanced splash screen functionality for better user experience.
// lib/splash/splash_controller.dart
import 'package:flutter/material.dart';
class SplashController {
static final SplashController _instance = SplashController._internal();
factory SplashController() {
return _instance;
}
SplashController._internal();
final ValueNotifier<double> _progress = ValueNotifier<double>(0.0);
final ValueNotifier<String> _status = ValueNotifier<String>('Initializing...');
ValueNotifier<double> get progress => _progress;
ValueNotifier<String> get status => _status;
void updateProgress(double value, {String? statusMessage}) {
_progress.value = value.clamp(0.0, 1.0);
if (statusMessage != null) {
_status.value = statusMessage;
}
}
void complete() {
_progress.value = 1.0;
_status.value = 'Ready!';
}
void reset() {
_progress.value = 0.0;
_status.value = 'Initializing...';
}
}
// lib/widgets/custom_splash_screen.dart
import 'package:flutter/material.dart';
import '../splash/splash_controller.dart';
class CustomSplashScreen extends StatefulWidget {
final Widget home;
final Duration duration;
final ImageProvider? backgroundImage;
final Widget? logo;
const CustomSplashScreen({
super.key,
required this.home,
this.duration = const Duration(seconds: 3),
this.backgroundImage,
this.logo,
});
@override
State<CustomSplashScreen> createState() => _CustomSplashScreenState();
}
class _CustomSplashScreenState extends State<CustomSplashScreen> {
@override
void initState() {
super.initState();
_initializeApp();
}
Future<void> _initializeApp() async {
final splashController = SplashController();
// Simulate initialization steps
await _simulateInitialization(splashController);
// Navigate to home after delay
await Future.delayed(widget.duration);
if (mounted) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => widget.home),
);
}
}
Future<void> _simulateInitialization(SplashController controller) async {
controller.updateProgress(0.2, statusMessage: 'Loading configuration...');
await Future.delayed(const Duration(milliseconds: 500));
controller.updateProgress(0.5, statusMessage: 'Initializing services...');
await Future.delayed(const Duration(milliseconds: 800));
controller.updateProgress(0.8, statusMessage: 'Preparing UI...');
await Future.delayed(const Duration(milliseconds: 500));
controller.complete();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
// Background
if (widget.backgroundImage != null)
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: widget.backgroundImage!,
fit: BoxFit.cover,
),
),
)
else
Container(
color: Theme.of(context).colorScheme.primary,
),
// Content
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Logo
widget.logo ??
FlutterLogo(
size: 120,
style: FlutterLogoStyle.horizontal,
textColor: Colors.white,
),
const SizedBox(height: 40),
// Progress indicator
ValueListenableBuilder<double>(
valueListenable: SplashController().progress,
builder: (context, progress, child) {
return SizedBox(
width: 200,
child: LinearProgressIndicator(
value: progress,
backgroundColor: Colors.white30,
valueColor: AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
);
},
),
const SizedBox(height: 16),
// Status text
ValueListenableBuilder<String>(
valueListenable: SplashController().status,
builder: (context, status, child) {
return Text(
status,
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w500,
),
);
},
),
],
),
),
],
),
);
}
}Best Practices and Performance Optimization
Follow these guidelines to ensure optimal splash screen performance and user experience.
Performance Optimization:
- Minimize splash screen duration (2-3 seconds recommended)
- Use optimized image assets
- Implement proper caching strategies
- Avoid heavy computations during splash
- Use native platform implementations when possible
User Experience Guidelines:
- Maintain consistent branding
- Provide loading indicators for longer waits
- Handle orientation changes properly
- Support dark/light mode themes
- Ensure accessibility compliance
Platform-Specific Considerations:
- Follow Android Splash Screen API guidelines
- Adhere to iOS Launch Screen requirements
- Implement proper PWA splash screens
- Consider desktop platform conventions
Testing and Validation
Thoroughly test splash screen implementation across all target platforms.
Testing Checklist:
- Verify appearance on different screen sizes
- Test orientation changes
- Validate performance on low-end devices
- Check accessibility features
- Verify proper dismissal timing
- Test cold and warm starts
- Validate platform-specific behaviors
By implementing splash screens using either the automated *flutter_native_splash* package or manual platform-specific configurations, you can create professional, branded loading experiences that enhance user perception and provide smooth transitions into your Flutter application across all supported platforms.