Add a Splash Screen in Flutter

Add a Splash Screen in Flutter

Sufi Aurangzeb Hossain
April 1, 2026
29 min read
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.

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.png

Step 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.yaml

Manual 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:

  1. Create *launch_background.xml* in *android/app/src/main/res/drawable/*
  2. Define splash screen layout with background and logo
  3. Configure splash theme in *styles.xml*
  4. Update *AndroidManifest.xml* to use splash theme
  5. 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:

  1. Open *ios/Runner.xcworkspace* in Xcode
  2. Edit *LaunchScreen.storyboard* for splash screen layout
  3. Add splash images to *Assets.xcassets*
  4. Configure auto-layout constraints for different devices
  5. 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:

  1. Customize *web/index.html* for splash screen styling
  2. Add splash screen CSS for initial loading state
  3. Implement JavaScript for splash screen dismissal
  4. Configure PWA manifest for splash screen behavior
  5. 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:

  1. Edit *macos/Runner/Base.lproj/MainMenu.xib*
  2. Configure initial window appearance
  3. Add splash screen view controller
  4. Implement splash screen dismissal logic
  5. Set appropriate window styling and transitions

Windows Desktop Configuration

Configure Windows splash screens with proper window styling and resource management.

Windows Implementation Steps:

  1. Modify *windows/runner/main.cpp* for splash screen
  2. Create splash screen window resource
  3. Implement splash screen timing logic
  4. Configure window transparency and styling
  5. 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.