How to add complex animation to splash screen with flutter?

I’m not good at animation but want to add animation which is like , there are 4 alphabets letters “B”, “C” , “D” and “E” which should animate from their 4 axis towards to the center and make a logo at center then when animation get complete, social buttons should animtate from the bottom that’s all I want to implements.

I’m going to add screens which make my concept more clear, what I’m trying to say :

First view

enter image description here

second view

enter image description here

Code for simple for showing Splash image

class SplashScreen extends StatefulWidget {
  const SplashScreen({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return _SplashScreenState();
  }
}

class _SplashScreenState extends State<SplashScreen> {
  Timer? timer;

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

  void navigatePage() async {
    timer = Timer(const Duration(seconds: 2), () async {
      isLoggedIn
          ? Navigator.pushReplacementNamed(context, "home")
          : Navigator.pushReplacementNamed(context, "login");
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: const Color(0xffF7F8F8),
        body: Image.asset(
          'assets/images/splash.png',
          fit: BoxFit.cover,
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height,
        ));
  }

  @override
  void dispose() {
    timer?.cancel();
    super.dispose();
  }
}


You can play with AnimatedPositioned and AnimatedRotation:

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool animated = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            animated = !animated;
          });
        },
      ),
      body: Stack(
        children: [
          AnimatedPositioned(
            top: animated
                ? MediaQuery.of(context).size.height * .50
                : MediaQuery.of(context).size.height * .75,
            right: animated
                ? MediaQuery.of(context).size.width * .100
                : MediaQuery.of(context).size.width * .50,
            duration: const Duration(seconds: 3),
            child: AnimatedRotation(
              duration: const Duration(seconds: 3),
              turns: animated ? 0.3 : 0.45,
              child: container(color: Colors.green),
            ),
          ),
          AnimatedPositioned(
            top: animated
                ? MediaQuery.of(context).size.height * .25
                : MediaQuery.of(context).size.height * .50,
            right: animated
                ? MediaQuery.of(context).size.width * .25
                : MediaQuery.of(context).size.width * .50,
            duration: const Duration(seconds: 3),
            child: AnimatedRotation(
              duration: const Duration(seconds: 3),
              turns: animated ? 0.3 : 0.1,
              child: container(color: Colors.blue),
            ),
          ),
          AnimatedPositioned(
            top: animated
                ? MediaQuery.of(context).size.height * .75
                : MediaQuery.of(context).size.height * .15,
            right: animated
                ? MediaQuery.of(context).size.width * .50
                : MediaQuery.of(context).size.width * .25,
            duration: const Duration(seconds: 3),
            child: AnimatedRotation(
              duration: const Duration(seconds: 3),
              turns: animated ? -0.3 : 0.13,
              child: container(color: Colors.yellow),
            ),
          ),
        ],
      ),
    );
  }

  Widget container({Color color = Colors.grey}) {
    return Container(
      height: 50,
      width: 50,
      color: color,
    );
  }
}

demo

I created the animation simply using Hero, focused on the animation is BETWEEN PAGES.

Please refer to the example for not wearing fancy colours or designs.

import 'package:flutter/material.dart';

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

  @override
  State<StatefulWidget> createState() {
    return _SplashScreenState();
  }
}

class _SplashScreenState extends State<SplashScreen> {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        Navigator.push(
          context,
          PageRouteBuilder(
            transitionDuration: const Duration(seconds: 1),
            pageBuilder: (context, animation, secondaryAnimation) => const LoginPage(),
          ),
        );
      },
      child: const Scaffold(
        backgroundColor: Color(0xffF7F8F8),
        body: Stack(
          children: [
            Align(child: MainLogoItem()),
            Positioned(
              top: 150,
              child: LogoItem('B'),
            ),
            Positioned(
              bottom: 50,
              left: 30,
              child: LogoItem('C'),
            ),
            Positioned(
              right: 20,
              bottom: 130,
              child: LogoItem('D'),
            ),
            Positioned(
              top: 30,
              right: 70,
              child: LogoItem('E'),
            ),
          ],
        ),
      ),
    );
  }
}
class LoginPage extends StatelessWidget {
  const LoginPage({super.key});

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        Navigator.pop(context);
      },
      child: const Scaffold(
        backgroundColor: Color(0xffF7F8F8),
        body: Center(child: Logo()),
      ),
    );
  }
}
class Logo extends StatelessWidget {
  const Logo({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      clipBehavior: Clip.hardEdge,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(20),
        boxShadow: const [
          BoxShadow(
            color: Colors.grey,
            blurRadius: 5,
            spreadRadius: 1,
          ),
        ],
      ),
      child: const Stack(
        children: [
          Align(child: MainLogoItem()),
          Positioned(
            left: 20,
            top: 10,
            child: LogoItem('B'),
          ),
          Positioned(
            bottom: 10,
            left: 15,
            child: LogoItem('C'),
          ),
          Positioned(
            right: 10,
            bottom: 10,
            child: LogoItem('D'),
          ),
          Positioned(
            top: -10,
            right: 30,
            child: LogoItem('E'),
          ),
        ],
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return const LogoItem('A', fontSize: 50);
  }
}

class LogoItem extends StatelessWidget {
  final String alphabet;
  final double? fontSize;

  const LogoItem(
    this.alphabet, {
    super.key,
    this.fontSize,
  });

  @override
  Widget build(BuildContext context) {
    return Hero(
      tag: 'LogoItem $alphabet',
      child: Text(
        alphabet,
        style: TextStyle(
          color: Colors.black,
          decoration: TextDecoration.none,
          fontSize: fontSize ?? 30,
          fontWeight: FontWeight.bold,
          height: 0,
        ),
      ),
    );
  }
}

How to test

  1. Paste & copy the codes.
  2. Add SplashScreen to your app.
  3. Tap screen then it navigate you to LoginPage with animation.
  4. Tap again to get back to SpalshScreen.

I did my best but I am pretty sure there must be a better solution.

I hope you get some hints from my code though.

Leave a Comment