flutter animation
flutter animation with example

Mastering the Art of Image Animation in Flutter: A Step-by-Step Guide

Tips for adding flutter Animation in App

  1. Import the necessary animation classes, such as AnimationController and Animation from the flutter:animation package.
  2. Initialize the animation controller in the initState method of your widget. The animation controller is responsible for managing the animation, such as starting and stopping it, and specifying its duration.
  3. Create the animation using the animation controller. This is typically done by using the animation property of the animation controller.
  4. Set up the image with the animation. This can be done by wrapping the image in an animation widget, such as a SizeTransition, AnimatedOpacity, or AnimatedContainer, and passing the animation object to the appropriate property of the widget.
  5. Update the animation’s value using the animation controller in the appropriate location in your code. This is typically done in a callback function, such as in response to a button press, or in the setState method.
  6. Dispose the animation controller in dispose method of your widget to release resources, when the widget is removed from the tree.
  7. Build your widget and run the animation.

Step 01:

In this step, you need to import the necessary animation classes from the flutter:animation package. These classes include AnimationController, Animation, and other animation-related widgets such as SizeTransition, AnimatedOpacity, and AnimatedContainer.

AnimationController is the core class that manages the animation and defines its duration. The Animation class is used to represent the animation’s value over time. AnimationController inherits from Animation and represents an animation that can be controlled by a user.

The other animation-related widgets, such as SizeTransition, AnimatedOpacity, and AnimatedContainer, are used to wrap your image and provide animation capabilities to them. These widgets will take the animation object and use it to animate the properties of the wrapped widget.

By importing these classes, you can use them in your code to create, control and run the animation.

You can import the necessary classes by including the following line at the top of your Dart file:

import 'package:flutter/animation.dart';

It is also possible to import only the necessary classes if you are not using all the classes from the package.

Step 02:

The animation controller is responsible for managing the animation and specifying its duration. It’s created in the initState method of your widget, which is called when the widget is first created.

During the initialization, you can set the duration for the animation. The duration is specified in milliseconds and determines how long the animation will take to complete. You can also set the vsync property, which is used to synchronize the animation with the device’s frame rate.

Here is an example of how to initialize an animation controller:

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

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController _animationController;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 500),
    );
  }
  
  // ...
}

In this example, the animation controller is created with a duration of 500 milliseconds and with the vsync property set to this. The SingleTickerProviderStateMixin class is used to provide a single ticker provider for the animation controller.

It is important to note that when the widget is removed from the tree, the animation controller should be disposed of in the dispose method to release resources.

Step 03:

Once the animation controller has been initialized, the next step is to create the animation itself. This is typically done by using the animation property of the animation controller.

The animation property returns an Animation<double> object, which represents the animation’s value over time. This value can be used to change a property of a widget, such as its size, position, or opacity.

Here is an example of how to create an animation:

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController _animationController;
  Animation<double> _animation;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 500),
    );
    _animation = _animationController.animation;
  }
  // ...
}

In this example, the animation is created by using the animation property of the _animationController object, and it is saved to a variable _animation.

It’s also possible to create a more complex animation by using the Tween class, which allows you to specify the starting and ending values of the animation, and an easing curve to control the animation’s rate of change.

With this step, you’ve created the animation object, but it’s not running yet, you need to trigger it by calling forward method on the AnimationController object.

Step 04:

Once the animation has been created, the next step is to set up the image with the animation. This can be done by wrapping the image in an animation widget, such as a SizeTransition, AnimatedOpacity, or AnimatedContainer, and passing the animation object to the appropriate property of the widget.

For example, if you want to animate the size of an image, you can use the SizeTransition widget, and pass the _animation object to its sizeFactor property.

Here is an example of how to set up an image with a size transition animation:

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  AnimationController _animationController;
  Animation<double> _animation;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            SizeTransition(
              sizeFactor: _animation,
              child: Image.asset('images/dice.png'),
            ),
          ],
        ),
      ),
    );
  }
  //...
}

In this example, the SizeTransition widget is used to wrap the Image widget, and the _animation object is passed to the sizeFactor property. This causes the image’s size to change based on the animation’s value.

Similarly, you can use AnimatedOpacity and `AnimatedContainer

Complete Example Code:

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  int _counter = 1;
  var random = Random();
  late final AnimationController _controller = AnimationController(
    duration: const Duration(milliseconds: 500),
    vsync: this,
  );
  late final Animation<double> _animation = CurvedAnimation(
    parent: _controller,
    curve: Curves.fastOutSlowIn,
  );


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

  void _incrementCounter() {
    setState(() {
      _counter = random.nextInt(6) + 1;
    });
    _controller.forward(from: 0);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            SizeTransition(
              sizeFactor: _animation,
              axis: Axis.horizontal,
              axisAlignment: -1,
              child: Image.asset('images/dice$_counter.png'),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Press',
        child: const Icon(Icons.preview),
      ),
    );
  }
}