Back

Advanced Animation Techniques for Flutter: A Guide

Advanced Animation Techniques for Flutter: A Guide

Animations play a crucial role in enhancing the user experience and improving an application’s UI’s overall look and feel. They help convey changes, transitions, and interactions more engagingly and intuitively. They can make your app feel more responsive, provide visual feedback to user actions, guide users’ attention, and create a more polished and professional appearance. This article will show you ways to create great animations in Flutter.

Flutter is an open-source UI software development kit created by Google. It is used to develop applications for Android, iOS, Linux, Mac, Windows, and the web from a single codebase. Flutter uses the Dart programming language and follows a reactive programming paradigm. It provides a rich set of pre-built and customizable widgets and a powerful framework for building beautiful, fluid User Interfaces(UI).

Animations in Flutter

In Flutter, animations are controlled by an AnimationController object, which manages the animation’s duration, status (e.g., playing, paused, completed), and forward or reverse playback. The AnimationController is connected to an Animation object, which generates a series of values over time (e.g., numbers, colors, sizes) that can be used to update the UI.

Below are some key classes that are frequently used in creating animations in Flutter:

  • Tween: A Tween defines a range of values for interpolation in an animation. For example, a Tween<double> can define a range of double values, while a Tween<Color> can define a range of colors.
  • AnimationController: AnimationController controls the animation process, including its duration, status, and playback direction. It is typically used with an Animation object to update the UI based on the animation’s current value.
  • AnimatedBuilder: An AnimatedBuilder widget is used to rebuild itself whenever the animation changes. It is useful for encapsulating complex UI animations that require updating multiple widgets.
  • Curve: A Curve is used to dictate an animation’s progression rate. Flutter provides several built-in curve types, such as Curves.linear, Curves.easeIn, Curves.easeOut, and Curves.easeInOut, among others.

Setting Up Animations In Flutter

To set up animations in Flutter, you typically create an AnimationController and configure it with duration and optional settings such as initial value, lower bound, and upper bound. You then create an Animation object by chaining the AnimationController with a Tween or Curve, which defines the range of values or the progression rate of the animation. Finally, you can use the addListener method to update the UI based on the animation’s current value.

Here’s an example of how you can create an AnimationController for a simple fade-in animation:

import 'package:flutter/material.dart';

// The entry point for the Flutter app
void main() {
  // Run the app by passing the MyApp widget to runApp
  runApp(MyApp());
}

// MyApp is a stateless widget that returns MaterialApp
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // MaterialApp is the root of your application
    return MaterialApp(
      // MyHomePage is set as the home page of the app
      home: MyHomePage(),
    );
  }
}

// MyHomePage is a StatefulWidget
class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

// _MyHomePageState is the State class for MyHomePage class
class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  // Declare an AnimationController
  late AnimationController _controller;

  // Declare an Animation
  late Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    // Initialize the AnimationController with a duration of 2 seconds
    // and the 'vsync' parameter set to 'this' for performance reasons
    _controller = AnimationController(
      duration: Duration(seconds: 2),
      vsync: this,
    );

    // Create an animation that goes from 0.0 to 300.0
    _animation = Tween(begin: 0.0, end: 300.0).animate(_controller);

    // Add a listener to the animation to update the UI when the animation value changes
    _controller.addListener(() {
      setState(() {});
    });

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

  @override
  void dispose() {
    // Dispose the AnimationController when the widget is removed from the widget tree
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // Scaffold widget provides a default app bar, title, and body
    return Scaffold(
      appBar: AppBar(
        title: Text('Animation Examples'),
      ),
      // Center widget centers its child widget
      body: Center(
        // Container widget is a box model that can have width, height, and color
        child: Container(
          // Set the width and height of the container to the current value of the animation
          width: _animation.value,
          height: _animation.value,
          color: Colors.blue, // Set the color of the container to blue
        ),
      ),
    );
  }
}

In this example, we’re using an AnimationController to animate the size of a Container widget. The AnimationController is initialized in the initState method, and the animation’s value is updated using a Tween and a listener. The dispose method is used to clean up the animation controller when the widget is removed from the widget tree.

Animation

This GIF demonstrates a simple animation in Flutter using an AnimationController. The blue square smoothly transitions from a small size to a larger size over 2 seconds.

Physics-Based Animations

Flutter provides a Simulation class that allows you to create Physics-based animations, such as spring animations, by defining the animation’s behavior based on physical properties like mass, stiffness, and damping.

Let’s explore a physics-based animation example in Flutter called spring animation. By leveraging the SpringSimulation class, we can simulate an oscillator, which allows us to achieve a realistic spring-like effect in our animations.

Here’s how you can create a spring animation using SpringSimulation.

import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';

void main() {
  runApp(MaterialApp(
    home: PhysicsSpringAnimationExample(),
  ));
}

class PhysicsSpringAnimationExample extends StatefulWidget {
  @override
  _PhysicsSpringAnimationExampleState createState() =>
      _PhysicsSpringAnimationExampleState();
}

class _PhysicsSpringAnimationExampleState
    extends State<PhysicsSpringAnimationExample>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _animation;

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

    // Initialize the AnimationController
    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 500), // Animation duration
    );

    // Define the properties of the spring animation
    final SpringDescription _spring = SpringDescription(
      mass: 1, // Mass of the object
      stiffness: 100, // Stiffness of the spring
      damping: 10, // Damping of the spring
    );

    // Create a SpringSimulation for the spring animation
    final SpringSimulation _springSimulation =
        SpringSimulation(_spring, 0.0, 1.0, 0.5); // Starting position, end position, and velocity

    // Drive the animation using the SpringSimulation
    _animation = _controller.drive(
      Tween<double>(
        begin: _springSimulation.x(0), // Starting position
        end: _springSimulation.x(1), // End position
      ),
    );

    // Start the animation
    _controller.animateWith(_springSimulation);
  }

  @override
  void dispose() {
    // Dispose the AnimationController when done
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Spring Animation Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // AnimatedBuilder to rebuild the widget when the animation updates
            AnimatedBuilder(
              animation: _controller,
              builder: (context, child) {
                // Transform the widget using the scale value from the animation
                return Transform.scale(
                  scale: _animation.value,
                  child: Container(
                    width: 100, // Adjust size of animation
                    height: 100, // Adjust size of animation
                    color: Colors.blue,
                  ),
                );
              },
            ),
            SizedBox(height: 20),
            // Button to restart the animation
            ElevatedButton(
              onPressed: () {
                _controller.reset(); // Reset the animation
                _controller.forward(); // Start the animation
              },
              child: Text('Start Animation'),
            ),
          ],
        ),
      ),
    );
  }
}

This code sets up a Flutter app with a spring animation effect. When you run the app, a blue square will appear, animating as if it were attached to a spring. Pressing the ‘Start Animation’ button triggers the animation, causing the square to stretch and compress accordingly. The animation is controlled by an AnimationController and driven by a SpringSimulation, which defines the physics of the spring animation.

SpringAnimation

This GIF demonstrates a Flutter app with a spring animation effect. The animation shows a blue square that behaves as if it were attached to a spring, stretching and compressing based on the physics of an oscillator. Pressing the ‘Start Animation’ button triggers the animation, showcasing Flutter’s capabilities.

Hero Animation

The Hero widget in Flutter is used for shared element transitions, where an element (e.g., an image, or text) smoothly transitions from one screen to another. The Hero widget animates the shared element’s size, position, and opacity to create a seamless transition effect.

Implementation Example For Smooth Image Transitions

Let’s consider a simple example of a Hero animation using two screens: FirstScreen and SecondScreen. In FirstScreen, we have an image wrapped in a Hero widget. When tapped, the image navigates to SecondScreen, where the same image is displayed with a smooth animation effect.

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    home: FirstScreen(),
  ));
}

// First screen with a Hero animation
class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Screen'),
      ),
      body: GestureDetector(
        onTap: () {
          // Navigate to the second screen when the image is tapped
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => SecondScreen(),
            ),
          );
        },
        child: Hero(
          // Tag for the Hero animation, should be unique across screens
          tag: 'imageHero',
          // Image to be displayed in the Hero animation
          child: Image.network(
            "https://cdn.britannica.com/98/235798-050-3C3BA15D/Hamburger-and-french-fries-paper-box.jpg",
            // Make the image fill the width of the screen
            width: MediaQuery.of(context).size.width,
            // Set a fixed height for the image
            height: 200,
            // Cover the entire image with no distortion
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }
}

// Second screen with a Hero animation
class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second Screen'),
      ),
      body: GestureDetector(
        onTap: () {
          // Pop the current screen off the navigation stack when tapped
          Navigator.pop(context);
        },
        child: Hero(
          // Tag for the Hero animation, should be the same as in the first screen
          tag: 'imageHero',
          // Image to be displayed in the Hero animation
          child: Image.network(
            "https://www.aheadofthyme.com/wp-content/uploads/2021/11/veggie-tray-2.jpg",
            // Make the image fill the width of the screen
            width: MediaQuery.of(context).size.width,
            // Set a fixed height for the image
            height: 200,
            // Cover the entire image with no distortion
            fit: BoxFit.cover,
          ),
        ),
      ),
    );
  }
}

This code demonstrates how to use the Hero widget in Flutter to create a shared element transition between two screens. On the first screen, an image is displayed with a Hero widget. When the image is tapped, the app navigates to the second screen where the same image is displayed with a Hero widget. The tag property of the Hero widget is used to identify the same image across screens and animate its transition.

Hero

This GIF demonstrates a Hero widget in action with this Flutter app. The animation smoothly transitions an image from the first screen to the second, creating a visually appealing effect. The imageHero tag ensures the images match up perfectly for a seamless transition.

Implicit Animations

Implicit animations in Flutter are animations that are automatically applied to widgets when their properties change. These animations are triggered by changes in the widget’s properties, such as size, position, or color, and do not require explicit animation controllers or builders.

The AnimatedContainer widget in Flutter is an example of an implicit animation. When the properties of the AnimatedContainer (e.g., width, height, color) change, Flutter automatically animates the transition between the old and new values, making smooth animation without the need for explicit code.

Implicit Animation Example

import 'package:flutter/material.dart';
import 'dart:math';

void main() {
  runApp(MaterialApp(
    home: ImplicitAnimationExample(),
  ));
}

class ImplicitAnimationExample extends StatefulWidget {
  @override
  _ImplicitAnimationExampleState createState() =>
      _ImplicitAnimationExampleState();
}

class _ImplicitAnimationExampleState extends State<ImplicitAnimationExample> {
  // Initial values for width, height, and color
  double _width = 100.0;
  double _height = 100.0;
  Color _color = Colors.blue;

  // Method to change the properties of the container
  void _changeProperties() {
    setState(() {
      final random = Random();
      _width = random.nextInt(300).toDouble(); // Random width
      _height = random.nextInt(300).toDouble(); // Random height
      _color = Color.fromARGB(
        255,
        random.nextInt(256), // Random red value
        random.nextInt(256), // Random green value
        random.nextInt(256), // Random blue value
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Implicit Animation Example'),
      ),
      body: Center(
        child: GestureDetector(
          onTap: _changeProperties, // Call _changeProperties on tap
          child: AnimatedContainer(
            duration: Duration(seconds: 1), // Animation duration
            curve: Curves.fastOutSlowIn, // Easing curve for the animation
            width: _width, // Animated width
            height: _height, // Animated height
            color: _color, // Animated color
            child: Center(
              child: Text(
                'Tap to change',
                style: TextStyle(color: Colors.white),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

This code creates a simple Flutter app with an AnimatedContainer widget that animates changes to its width, height, and color properties. Tapping on the container triggers the animation, randomly changing its size and color. The animation duration is set to 1 second, and the easing curve used is Curves.fastOutSlowIn

Implicit

Flutter app demonstrating implicit animation with AnimatedContainer. The container changes size and color automatically, showcasing Flutter’s smooth animation capabilities.

Animated Vector Graphics

Animated vector graphics (AVG) are graphics that use vector shapes and paths to create animations. In Flutter, AVG can be implemented using libraries like Rive and Lottie, which allow you to import vector graphics animations created in design tools like Adobe After Effects and Adobe Illustrator.

Rive

Rive is a powerful design and animation tool that allows you to create stunning animations and vector graphics for use in your Flutter applications. With Rive, you can easily design complex animations and export them to various formats for integration into your projects.

Here’s a basic example of how you can integrate a Rive animation into your Flutter app:

import 'package:flutter/material.dart';
import 'package:rive/rive.dart'; // Import Rive package

void main() {
  runApp(MaterialApp(
    home: RiveAnimationExample(),
  ));
}

class RiveAnimationExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Rive Animation Example'),
      ),
      body: Stack(
        children: [
          // Display a Rive animation from an asset file
          RiveAnimation.asset(
            "assets/shapes.riv", // Path to the Rive animation asset
            fit: BoxFit.cover, // Fit the animation to the available space
          ),
        ],
      ),
    );
  }
}

This code sets up a Flutter app with a Rive animation effect. When you run the app, you’ll see the Rive animation on the screen. The RiveAnimation.asset widget is used to load and display the Rive animation from a file

To incorporate animated vector graphics in your Flutter animations using Rive, follow these steps:

  1. Create the animation using the Rive website and download it in the appropriate format.
  2. Import the Rive package into your Flutter project.
  3. Utilize the RiveAnimation.asset widget, providing the path to your Rive file.

Untitleddesign-ezgif.com-video-to-gif-converter

This code sets up a Flutter app that displays a Rive animation consisting of bouncing shapes (circle, triangle, and hexagon) on the screen. The RiveAnimation.asset widget is used to load and display the Rive animation from the assets/shapes.riv file. The animation is overlaid on the screen using a Stack widget, allowing multiple widgets to be displayed on top of each other

Lottie Animations

Lottie is an open-source animation file format that’s lightweight and easy to use. It’s designed to render vector animations and graphics in a way that’s scalable and resolution-independent. Lottie animations are created using Adobe After Effects and exported using the Bodymovin plugin. These animations can be easily integrated into mobile, web, and desktop applications.

In Flutter, the Lottie library allows developers to add Lottie animations to their apps. This library provides widgets for loading and displaying Lottie animations, making it simple to include complex animations in Flutter apps without sacrificing performance. Lottie animations can enhance the user experience by adding visually appealing and engaging graphics to your app.

Here’s an example of how to create Lottie animations in Flutter:

import 'package:flutter/material.dart'; // Import the Flutter material library
import 'package:lottie/lottie.dart'; // Import the Lottie library for animations

void main() {
  runApp(MaterialApp(
    home: LottieAnimationExample(), // Set the home screen to LottieAnimationExample
  ));
}

class LottieAnimationExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Lottie Animation Example'), // Set the title of the app bar
      ),
      body: Center(
        // Center aligns its child widget
        child: Lottie.asset(
          'assets/lottie.json', // Path to the Lottie animation asset
          width: 200, // Set the width of the animation
          height: 200, // Set the height of the animation
          fit: BoxFit.cover, // Fit the animation to cover the entire space
        ),
      ),
    );
  }
}

This Flutter code sets up an app that displays a Lottie animation. The Lottie.asset widget loads and displays the animation from a JSON file, and the app bar (AppBar) provides a title for the app. When the app is run, the animation will be centered on the screen and cover the entire space available to it.

lottie

Bring your app to life with Lottie! This powerful library allows you to easily add high-quality animations to your Flutter app. Simply import your animation in Lottie format (JSON), use the Lottie package, and showcase your animations with ease.

Conclusion

In conclusion, Flutter’s comprehensive animation framework offers developers a wide array of tools and APIs to create captivating user experiences. By mastering these techniques, developers can seamlessly integrate animations into their apps, enhancing user engagement and overall app quality. From implicit animations to physics-based simulations and shared element transitions, Flutter provides the flexibility and scalability needed to bring apps to life.

As Flutter continues to evolve, developers are encouraged to explore these advanced animation techniques further, pushing the boundaries of what’s possible in mobile app development. By leveraging Flutter’s animation capabilities, developers can deliver apps that not only meet but exceed user expectations, setting new standards for user interface design and interaction

Scale Seamlessly with OpenReplay Cloud

Maximize front-end efficiency with OpenReplay Cloud: Session replay, performance monitoring and issue resolution, all with the simplicity of a cloud-based service.

OpenReplay