In this flutter custom splash screen tutorial, we will learn how to create a Custom Splash Screen in Flutter with animation 2022 so, I have divided this tutorial into 3 main portions.
- Design a splash screen in flutter
- Add Animation using Stateful Widget & use Widgets mentioned below
- AnimatedPositioned Widget
- AnimatedOpacity Widget
- Separate Business Logic using GetX
Worthwhile Links
- Install Android Studio
- Install Flutter
- Create a new flutter app
- Create Folder Structure
- Setup Theme in Flutter for Light & Dark Mode
This is the tutorial of our Flutter Login App Series.
Design Beautiful Splash Screen in Flutter
To create a Custom Splash Screen in flutter we are not going to use Flutter Native Splash but at first, we have to design a beautiful splash screen in flutter.
To Design this splash screen I have divided the screen into 4 parts.
Use Stack & Positioned Widget to layout the screen widgets, therefore, to position the child widgets we have to use Positioned Widget to align them.
Design is divided into 4 parts, Image, Text, Image, and Container.
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
const Positioned(
top: 0,
left: 0,
child: Image(image: AssetImage(tSplashTopIcon)),
),
Positioned(
top: 80,
left: tDefaultSize,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
tAppName,
style: Theme.of(context).textTheme.headline3,
),
Text(
tAppTagLine,
style: Theme.of(context).textTheme.headline2,
)
],
),
),
const Positioned(
bottom: 100,
child: Image(image: AssetImage(tSplashImage)),
),
Positioned(
bottom: 60,
right: tDefaultSize,
child: Container(
width: tSplashContainerSize,
height: tSplashContainerSize,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
color: tPrimaryColor,
),
),
),
],
),
);
}
Splash Screen in Flutter with animation
To add animation in splash screen, we have to convert our StatlessWidget to StatefulWidget so that, we can add setState() to update widgets at run time.
Note: Its a bad practice to keep both design and logic in the same file. Therefore, we will use GetX State Management in the next step.
Once the design is ready, convert Positioned widget into AnimatedPositioned Widget. Inside Animated Positioned widget add AnimatedOpactiy Widget for a fade effect.
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
@override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
bool animate = false;
@override
void initState() {
super.initState();
startAnimation();
}
Future startAnimation() async {
await Future.delayed(const Duration(milliseconds: 500));
setState(() { animate = true; });
await Future.delayed(const Duration(milliseconds: 5000));
Get.to(const WelcomeScreen());
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
AnimatedPositioned(
duration: const Duration(milliseconds: 1600),
top: animate ? 0 : -30,
left: animate ? 0 : -30,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 1600),
opacity: animate ? 1 : 0,
child: const Image(image: AssetImage(tSplashTopIcon)),
),
),
AnimatedPositioned(
duration: const Duration(milliseconds: 2000),
top: 80,
left: animate ? tDefaultSize : -80,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 2000),
opacity: animate ? 1 : 0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
tAppName,
style: Theme.of(context).textTheme.headline3,
),
Text(
tAppTagLine,
style: Theme.of(context).textTheme.headline2,
)
],
),
),
),
AnimatedPositioned(
duration: const Duration(milliseconds: 2400),
bottom: animate ? 100 : 0,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 2000),
opacity: animate ? 1 : 0,
child: const Image(image: AssetImage(tSplashImage)),
),
),
AnimatedPositioned(
duration: const Duration(milliseconds: 2400),
bottom: animate ? 60 : 0,
right: tDefaultSize,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 2000),
opacity: animate ? 1 : 0,
child: Container(
width: tSplashContainerSize,
height: tSplashContainerSize,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
color: tPrimaryColor,
),
),
),
),
],
),
);
}
}
Create Custom Widget for all four parts
As our design keep on growing so, its a best practice to put all the related code into single Custom Widget and use that widget in main Screen.
Flutter Native Splash using GetX
In this step, we will move our flutter splash screen logic we created above into GetX.
The purpose of using GetX is simply to seprate business logic from design. Additionally, we can enhance the speed by avoiding the use of Stateful widgets to spare some RAM.
First, add get
flutter package as a dependency in your pubspec.yaml file.
dependencies:
get: [latest-version]
Don’t forget to run flutter pub get
.
Customize the following settings and add to your project’s pubspec.yaml
file or place in a new file in your root project folder named flutter_native_splash.yaml
.
class SplashScreen extends StatelessWidget {
SplashScreen({Key? key}) : super(key: key);final splashController = Get.put(SplashScreenController());
@override
Widget build(BuildContext context) {
SplashScreenController.find.startAnimation();return Scaffold(
body: Stack(
children: [
Obx(
() => AnimatedPositioned(
duration: const Duration(milliseconds: 1600),
top: splashController.animate.value ? 0 : -30,
left: splashController.animate.value ? 0 : -30,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 1600),
opacity: splashController.animate.value ? 1 : 0,
child: const Image(image: AssetImage(tSplashTopIcon)),
),
),
),
Obx(
() => AnimatedPositioned(
duration: const Duration(milliseconds: 2000),
top: 80,
left: splashController.animate.value ? tDefaultSize : -80,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 2000),
opacity: splashController.animate.value ? 1 : 0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(tAppName, style: Theme.of(context).textTheme.headline3),
Text(tAppTagLine, style: Theme.of(context).textTheme.headline2)
],
),
),
),
),
Obx(
() => AnimatedPositioned(
duration: const Duration(milliseconds: 2400),
bottom: splashController.animate.value ? 100 : 0,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 2000),
opacity: splashController.animate.value ? 1 : 0,
child: const Image(image: AssetImage(tSplashImage)),
),
),
),
Obx(
() => AnimatedPositioned(
duration: const Duration(milliseconds: 2400),
bottom: splashController.animate.value ? 60 : 0,
right: tDefaultSize,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 2000),
opacity: splashController.animate.value ? 1 : 0,
child: Container(
width: tSplashContainerSize,
height: tSplashContainerSize,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
color: tPrimaryColor,
),
),
),
),
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:login_flutter_app/src/features/authentication/screens/welcome/welcome_screen.dart';class SplashScreenController extends GetxController{
static SplashScreenController get find => Get.find();RxBool animate = false.obs;
Future startAnimation() async {
await Future.delayed(const Duration(milliseconds: 500));
animate.value = true;
await Future.delayed(const Duration(milliseconds: 5000));
Get.to(const WelcomeScreen());
}
}