Flutter

Splash Screen

Implement a native splash screen with the flutter_native_splash package, create an animated custom splash with AnimationController, and navigate to the home screen after a delay.

1. flutter_native_splash Package Setup

The flutter_native_splash package generates native launch screens for Android and iOS from a single config in pubspec.yaml. Run the generate command after configuring.

yaml
# pubspec.yaml – add the dependency
dependencies:
  flutter_native_splash: ^2.4.1

# Splash screen configuration (add at root level of pubspec.yaml)
flutter_native_splash:
  color: "#1E1E2E"                   # Background color
  image: assets/images/logo.png      # Center image
  color_dark: "#0D0D1A"              # Dark mode background
  image_dark: assets/images/logo_dark.png

  android_12:
    image: assets/images/logo.png    # Android 12+ icon
    color: "#1E1E2E"
    icon_background_color: "#1E1E2E"

  web: false                         # Disable web splash
  ios: true
  android: true
shell
# Install package
flutter pub get

# Generate native splash screens
dart run flutter_native_splash:create

# Remove generated splash (revert to default)
dart run flutter_native_splash:remove
2. Preserve Native Splash Until App Is Ready

Call FlutterNativeSplash.preserve() in main() to keep the native splash visible while your app initializes (e.g. loading prefs, checking auth). Remove it when ready.

dart
import "package:flutter/material.dart";
import "package:flutter_native_splash/flutter_native_splash.dart";

void main() {
  // Preserve native splash until we are ready
  WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
  FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const SplashScreen(),
    );
  }
}

class SplashScreen extends StatefulWidget {
  const SplashScreen({super.key});

  @override
  State<SplashScreen> createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen> {
  @override
  void initState() {
    super.initState();
    _initialize();
  }

  Future<void> _initialize() async {
    // Simulate loading: check auth, load prefs, etc.
    await Future.delayed(const Duration(seconds: 2));

    // Remove native splash now that we are ready
    FlutterNativeSplash.remove();

    if (mounted) {
      Navigator.pushReplacementNamed(context, "/home");
    }
  }

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      backgroundColor: Color(0xFF1E1E2E),
      body: Center(child: CircularProgressIndicator()),
    );
  }
}
3. Custom Animated Splash Screen

Build a fully custom splash with AnimationController and FadeTransition + ScaleTransition. This gives you full control over the animation and navigation timing.

dart
import "package:flutter/material.dart";

class AnimatedSplashScreen extends StatefulWidget {
  const AnimatedSplashScreen({super.key});

  @override
  State<AnimatedSplashScreen> createState() => _AnimatedSplashScreenState();
}

class _AnimatedSplashScreenState extends State<AnimatedSplashScreen>
    with SingleTickerProviderStateMixin {

  late AnimationController _controller;
  late Animation<double> _fadeAnimation;
  late Animation<double> _scaleAnimation;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 1200),
    );

    _fadeAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeIn),
    );

    _scaleAnimation = Tween<double>(begin: 0.7, end: 1.0).animate(
      CurvedAnimation(parent: _controller, curve: Curves.elasticOut),
    );

    // Start animation, then navigate
    _controller.forward().then((_) async {
      await Future.delayed(const Duration(milliseconds: 800));
      if (mounted) {
        Navigator.pushReplacementNamed(context, "/home");
      }
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFF1E1E2E),
      body: Center(
        child: FadeTransition(
          opacity: _fadeAnimation,
          child: ScaleTransition(
            scale: _scaleAnimation,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Image.asset("assets/images/logo.png", width: 120),
                const SizedBox(height: 24),
                const Text(
                  "MyApp",
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 32,
                    fontWeight: FontWeight.bold,
                    letterSpacing: 2,
                  ),
                ),
                const SizedBox(height: 8),
                const Text(
                  "Your tagline here",
                  style: TextStyle(color: Colors.white54, fontSize: 14),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
4. Simple Timed Navigation (No Animation)

The simplest splash: show a static screen for a fixed duration using Future.delayed, then navigate with Navigator.pushReplacement.

dart
class SimpleSplashScreen extends StatefulWidget {
  const SimpleSplashScreen({super.key});

  @override
  State<SimpleSplashScreen> createState() => _SimpleSplashScreenState();
}

class _SimpleSplashScreenState extends State<SimpleSplashScreen> {
  @override
  void initState() {
    super.initState();
    // Navigate after 3 seconds
    Future.delayed(const Duration(seconds: 3), () {
      if (mounted) {
        Navigator.pushReplacement(
          context,
          MaterialPageRoute(builder: (_) => const HomeScreen()),
        );
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      backgroundColor: Color(0xFF1E1E2E),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(Icons.bolt, size: 80, color: Colors.amber),
            SizedBox(height: 16),
            Text(
              "NeoApp",
              style: TextStyle(
                color: Colors.white,
                fontSize: 28,
                fontWeight: FontWeight.bold,
              ),
            ),
            SizedBox(height: 32),
            CircularProgressIndicator(color: Colors.amber),
          ],
        ),
      ),
    );
  }
}