Skip to main content

Creating Animations with AnimationController

1. Understanding AnimationController Class

Property/Method Description Notes
AnimationController Manages the state and timing of animations. Core class for controlling animations in Flutter.
duration Duration of the animation. Defaults to 1 second. Can be set to Duration(milliseconds: 500).
value Current value of the animation (0.0 to 1.0). Read-only property. Updated automatically during animation.
forward() Starts the animation from 0.0 to 1.0. Returns a Future that completes when animation finishes.
reverse() Reverses the animation from 1.0 to 0.0. Useful for creating bounce or return effects.
reset() Resets the animation to 0.0. Does not stop the animation if it's running.
dispose() Releases resources used by the controller. Must be called to prevent memory leaks in widgets.

Example: Creating an AnimationController

import 'package:flutter/material.dart';

class MyAnimatedWidget extends StatefulWidget {
  @override
  _MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}

class _MyAnimatedWidgetState extends State<MyAnimatedWidget> with TickerProviderStateMixin {
  late AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      duration: Duration(seconds: 2),
      vsync: this,
    );
  }

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

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: controller,
      builder: (context, child) {
        return Container(
          width: 100 + controller.value * 100,
          height: 100 + controller.value * 100,
          color: Colors.blue.withOpacity(controller.value),
        );
      },
    );
  }
}
Note: The vsync parameter is required for AnimationController and should typically be set to this when using TickerProviderStateMixin.
Warning: Always call dispose() on the controller to prevent memory leaks.

2. Creating AnimationController

Example: Basic AnimationController Setup

<AnimationController> controller = AnimationController(
  duration: const Duration(seconds: 2),
  vsync: this,
);
Property Values Description
duration Duration Length of the animation in time
vsync TickerProvider Required for animations; typically this in widgets
lowerBound double Minimum value of the animation (default: 0.0)
upperBound double Maximum value of the animation (default: 1.0)
value double Current value of the animation (0.0 to 1.0)
Note: The vsync parameter is required to prevent animation drift and ensure proper synchronization with the display refresh rate.

Example: AnimationController with TickerProviderStateMixin

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> with TickerProviderStateMixin {
  late AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      duration: const Duration(seconds: 2),
      vsync: this,
    );
  }

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

3. Using TickerProvider

Property Description Usage
TickerProvider Interface for creating Ticker objects that drive animations. Implement in widgets that manage animation controllers.
Ticker Object that provides time-based updates to animation controllers. Used internally by AnimationController to drive animations.

Example: Implementing TickerProvider

class MyAnimatedWidget extends StatefulWidget {
  @override
  _MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}

class _MyAnimatedWidgetState extends State<MyAnimatedWidget> with TickerProviderStateMixin {
  late AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      duration: Duration(seconds: 2),
      vsync: this, // TickerProvider
    );
  }

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

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: controller,
      builder: (context, child) {
        return Transform.rotate(
          angle: controller.value * 2 * pi,
          child: Container(
            width: 100,
            height: 100,
            color: Colors.blue,
          ),
        );
      },
    );
  }
}
Note: with TickerProviderStateMixin provides the vsync parameter required by AnimationController.

4. Controlling Animations

Method Description Usage
forward() Starts or resumes the animation in the forward direction controller.forward();
reverse() Starts or resumes the animation in the reverse direction controller.reverse();
stop() Stops the animation and resets the animation position to 0.0 controller.stop();
pause() Pauses the animation at its current position controller.pause();
reset() Resets the animation to its initial state (0.0) controller.reset();
animate() Starts the animation from a specific position controller.animate(0.5);
Note: Use forward() to begin or continue an animation. Use reverse() to play it backward.
Warning: Calling stop() resets the animation to 0.0, which may not be desired if you want to resume from a specific point.

5. Using Animation Status

Status Description Usage
AnimationStatus.dismissed Animation is dismissed (ended at the start position) Used to detect when animation completes and returns to initial state
AnimationStatus.forward Animation is playing forward Useful for tracking progress during forward animation
AnimationStatus.reverse Animation is playing in reverse Used to detect when animation plays backward
AnimationStatus.completed Animation is completed (ended at the end position) Triggers actions when animation finishes

Example: Listening to Animation Status

final animationController = AnimationController(
  duration: const Duration(seconds: 2),
  vsync: this,
);

animationController.addStatusListener((status) {
  switch (status) {
    case AnimationStatus.forward:
      print('Animation playing forward');
      break;
    case AnimationStatus.reverse:
      print('Animation playing in reverse');
      break;
    case AnimationStatus.completed:
      print('Animation completed');
      break;
    case AnimationStatus.dismissed:
      print('Animation dismissed');
      break;
  }
});
Note: The addStatusListener callback is called whenever the animation status changes, allowing for real-time updates or triggers based on animation state.

6. Using Animation Values

Property Description Values Notes
value Current value of the animation double Read-only property, updated during animation
isAnimating Indicates if the animation is currently running bool True when animation is in progress
status Current status of the animation AnimationStatus Can be AnimationStatus.dismissed, AnimationStatus.forward, AnimationStatus.reverse, or AnimationStatus.completed

Example: Using Animation Values in a Widget

class AnimatedWidgetExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final animationController = AnimationController(
      duration: Duration(seconds: 2),
      vsync: this,
    );

    return AnimatedBuilder(
      animation: animationController,
      builder: (context, child) {
        return Container(
          width: animationController.value * 200,
          height: animationController.value * 100,
          color: Color.lerp(
            Colors.blue,
            Colors.red,
            animationController.value,
          ),
        );
      },
    );
  }
}
Note: The value property of an AnimationController ranges from 0.0 to 1.0 during a typical animation cycle.

Animation Value Usage Summary

  • value is used to get the current progress of an animation (0.0 to 1.0)
  • isAnimating helps determine if an animation is currently running
  • status provides the current state of the animation (forward, reverse, completed, dismissed)

7. Disposing AnimationControllers

Example: Properly disposing AnimationController

class AnimatedWidget extends StatefulWidget {
  @override
  _AnimatedWidgetState createState() => _AnimatedWidgetState();
}

class _AnimatedWidgetState extends State<AnimatedWidget> with TickerProviderStateMixin {
  late AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      duration: Duration(seconds: 2),
      vsync: this,
    );
  }

  @override
  void dispose() {
    controller.dispose(); // Important: dispose the controller
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: controller,
      builder: (context, child) {
        return Container(
          width: controller.value * 200,
          height: 100,
          color: Colors.blue,
        );
      },
    );
  }
}
Property Description Notes
controller.dispose() Releases all resources used by the AnimationController Must be called to prevent memory leaks
Note: Always call dispose() in the widget's dispose method to free up resources and avoid memory leaks.

8. Using Tween Class

Property Description Values Notes
Tween.begin Initial value of the animation double Start point for interpolation
Tween.end Final value of the animation double End point for interpolation
Tween.evaluate Evaluates the animation at a given value double Returns interpolated value between begin and end

Example: Tween Usage

import 'package:flutter/material.dart';

class TweenExample extends StatefulWidget {
  @override
  _TweenExampleState createState() => _TweenExampleState();
}

class _TweenExampleState extends State<TweenExample> with TickerProviderStateMixin {
  late AnimationController controller;
  late Tween<double> tween;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      duration: Duration(seconds: 2),
      vsync: this,
    );
    tween = Tween<double>(begin: 0.0, end: 1.0);
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: controller,
      builder: (context, child) {
        double value = tween.evaluate(controller);
        return Container(
          width: 100 + value * 100,
          height: 100 + value * 100,
          color: Color.lerp(Colors.blue, Colors.red, value),
        );
      },
    );
  }
}
Note: Tween is used to interpolate between two values over the duration of an animation.

9. Chaining Tweens

Example: Chaining Tweens

import &#x27;package:flutter/material.dart&#x27;;

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text("Chained Tweens")),
        body: Center(
          child: TweenAnimationBuilder(
            tween: Tween<double>(begin: 0, end: 1),
            duration: Duration(seconds: 2),
            builder: (context, value, child) {
              return Container(
                width: 100 + value * 100,
                height: 100 + value * 100,
                color: Color.lerp(Colors.blue, Colors.red, value),
              );
            },
          ),
        ),
      ),
    );
  }
}
Property Description Values
tween Defines the animation range Tween<double>(begin: 0, end: 1)
duration Duration of the animation Duration(seconds: 2)
builder Function to build the animated widget (context, value, child) => Widget

10. Using CurvedAnimation

Property Values Description
parent AnimationController The controller that drives the animation.
curve Curve The curve to apply to the animation, e.g., Curves.easeInOut.
reverseCurve Curve Optional reverse curve for the animation.

Example: Using CurvedAnimation

final controller = AnimationController(
  duration: const Duration(seconds: 2),
  vsync: this,
);

final curvedAnimation = CurvedAnimation(
  parent: controller,
  curve: Curves.easeInOut,
);

AnimatedBuilder(
  animation: curvedAnimation,
  builder: (context, child) {
    return Transform.scale(
      scale: curvedAnimation.value,
    );
  },
)
Note: CurvedAnimation modifies the timing of an animation using a curve, allowing for more natural motion.

Was this article helpful?