Adding Magic with Flutter Animations 🧙♂️✨

Adding Magic with Flutter Animations 🧙♂️✨

Animations can transform a static app into an engaging experience—and with Flutter, adding those magical touches is easier than you think! In this tutorial, I’ll show you how to create a simple sliding animation for a login screen, perfect for impressing users and adding a touch of polish to your app.

Here's a quick look at what we'll be building:


Why Add Animations?

Users love smooth transitions. Animations make apps feel alive, guide attention, and create a memorable experience. Flutter’s animation framework is powerful yet intuitive, letting you build complex effects with minimal code.


Set Up Your Flutter Project

Create a new Flutter project in Android Studio/VS Code.

flutter create flutter_login_animation        

Code the Sliding Animation

Step 1: Setting Up the Foundation

First, we need a StatefulWidget. Animations require a state that can change over time, and a StatefulWidget is perfect for this. We'll also mix in SingleTickerProviderStateMixin, which is essential for our AnimationController to function correctly.

import 'package:flutter/material.dart';

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

  @override
  State<LoginScreen> createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen>
    with SingleTickerProviderStateMixin {
  // Animation variables will go here
  
  @override
  Widget build(BuildContext context) {
    // Our UI will be built here
  }
}        

Step 2: Initializing the Animations

Inside our _LoginScreenState, we'll declare our animation controllers and define the animations themselves. We'll use an AnimationController to manage the animation's lifecycle.

We will create two animations:

  1. _slideAnimation: This will control the vertical position of our login card, making it slide up from the bottom of the screen.
  2. _fadeAnimation: This will handle the opacity of the logo, making it fade in gracefully.

We'll set these up in the initState() method, which is called once when the widget is first created.

late AnimationController _controller;
late Animation<Offset> _slideAnimation;
late Animation<double> _fadeAnimation;

@override
void initState() {
  super.initState();
  _controller = AnimationController(
    vsync: this,
    duration: Duration(milliseconds: 800),
  );

  // 1. Slide Animation
  _slideAnimation = Tween<Offset>(
    begin: Offset(0, 1), // Start off-screen (bottom)
    end: Offset.zero,   // End at its natural position
  ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOut));

  // 2. Fade Animation
  _fadeAnimation = Tween(begin: 0.0, end: 1.0).animate(
    CurvedAnimation(
      parent: _controller,
      curve: Interval(0.5, 1.0), // Fade in during the second half of the animation
    ),
  );

  // Start the animation
  _controller.forward();
}        

Key Concepts:

  • AnimationController: Manages the animation, like playing, stopping, or reversing it. The duration is set to 800 milliseconds.
  • Tween: Defines the range of values for our animation. For the slide, it's a Tween<Offset> that goes from an off-screen position (Offset(0, 1)) to its final position (Offset.zero). For the fade, it's a Tween<double> from 0.0 (transparent) to 1.0 (opaque).
  • CurvedAnimation: Applies a non-linear curve to the animation, making it feel more natural. Curves.easeOut starts fast and slows down at the end.
  • Interval: Delays an animation. We use it to make the fade animation start only after the slide animation is halfway through.
  • _controller.forward(): This kicks off the animation.

And don't forget to dispose of the controller when the widget is removed to prevent memory leaks!

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

Step 3: Building the UI

Now, let's build the visual components. We'll use a Stack to layer a gradient background behind our animated elements.

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Stack(
      children: [
        // The beautiful gradient background
        Container(
          decoration: BoxDecoration(
            gradient: LinearGradient(
              colors: [Colors.blue.shade700, Colors.blue.shade300],
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
            ),
          ),
        ),
        Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              // Our animated logo will go here
              // Our animated login form will go here
            ],
          ),
        ),
      ],
    ),
  );
}        

Step 4: Applying the Animations

This is where the magic happens. We'll use the FadeTransition and SlideTransition widgets to apply our animations to the UI elements.

  • Wrap the Image widget with FadeTransition.
  • Wrap the login card widget with SlideTransition.

// Inside the Center's child Column
...
FadeTransition(
  opacity: _fadeAnimation,
  child: Image(
    image: AssetImage('assets/logo.png'), // Make sure you have a logo in your assets
    width: 240,
    height: 240,
    color: Colors.white,
  ),
),
SlideTransition(
  position: _slideAnimation,
  child: _buildLoginCard(), // We will create this helper method next
),
...        

Step 5: Creating the Login Card

To keep our build method clean, we'll extract the login form into its own method, _buildLoginCard(). This method returns a styled Container with text fields and a button.

Widget _buildLoginCard() {
  return Container(
    margin: EdgeInsets.all(20),
    padding: EdgeInsets.all(25),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: BorderRadius.circular(20),
      boxShadow: [
        BoxShadow(
          color: Colors.black26,
          blurRadius: 10,
          offset: Offset(0, 5),
        ),
      ],
    ),
    child: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Text(
          "Welcome Back!",
          style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
        ),
        SizedBox(height: 25),
        TextField(
          decoration: InputDecoration(
            labelText: "Email",
            prefixIcon: Icon(Icons.email),
            border: OutlineInputBorder(),
          ),
        ),
        SizedBox(height: 15),
        TextField(
          obscureText: true,
          decoration: InputDecoration(
            labelText: "Password",
            prefixIcon: Icon(Icons.lock),
            border: OutlineInputBorder(),
          ),
        ),
        SizedBox(height: 25),
        ElevatedButton(
          onPressed: () {},
          style: ElevatedButton.styleFrom(
            padding: EdgeInsets.symmetric(vertical: 15, horizontal: 40),
          ),
          child: Text("Sign In"),
        ),
      ],
    ),
  );
}        

Conclusion

And that's it! With just a few animation widgets and a controller, we've transformed a static login screen into a dynamic and professional-looking entry point for your application. These small details in the user experience are what set great apps apart.

The full code is provided above. Feel free to experiment with different Curves, Durations, and Tween values to create your own unique animations.

What are your favorite ways to add a little flair to your Flutter apps? Share your thoughts in the comments below!

#Flutter #Dart #MobileDevelopment #UIUX #Animation #FlutterDev #AppDevelopment

To view or add a comment, sign in

More articles by indra lesmana

Explore content categories