Why isn't my Flutter animated checkmark working?

Viewed 2431

I'm trying to replicate the following animation from dribble.com:

I've gotten the ScaleTransition to work but the SizeTransition does not. What am I doing wrong or what don't I understand?

When I only swap out the SizeTransition with a FadeTransition (and keep the same controllers & animations), the animations both run. When I move the Center widget from being a child of the SizeTransition to the parent the animation runs. However it is not properly centered.

import 'package:flutter/material.dart';

class AnimatedCheck extends StatefulWidget {
  @override
  _AnimatedCheckState createState() => _AnimatedCheckState();
}

class _AnimatedCheckState extends State<AnimatedCheck> with TickerProviderStateMixin {
  late AnimationController scaleController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this);
  late Animation<double> scaleAnimation = CurvedAnimation(parent: scaleController, curve: Curves.elasticOut);
  late AnimationController checkController = AnimationController(duration: const Duration(milliseconds: 400), vsync: this);
  late Animation<double> checkAnimation = CurvedAnimation(parent: checkController, curve: Curves.linear);

  @override
  void initState() {
    super.initState();
    scaleController.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        checkController.forward();
      }
    });
    scaleController.forward();
  }

  @override
  void dispose() {
    scaleController.dispose();
    checkController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    double circleSize = 140;
    double iconSize = 108;

    return ScaleTransition(
      scale: scaleAnimation,
      child: Container(
        height: circleSize,
        width: circleSize,
        decoration: BoxDecoration(
          color: Colors.green,
          shape: BoxShape.circle,
        ),
        child: SizeTransition(
          sizeFactor: checkAnimation,
          axis: Axis.horizontal,
          axisAlignment: -1,
          child: Center(
            child: Icon(Icons.check, color: Colors.white, size: iconSize)
          )
        ),
      ),
    );
  }
}
3 Answers

When you move the Center to the outside, it doesn't center the children of it's children, just puts it's child in the center of the area it occupies. Try setting the Container alignment using alignment: Alignment.center, inside of the Container. Please let me know if it doesn't work and I'll replicate the code to figure out the problem is.

Edit: This is because there is space around the visual check you see, the SizeTransition doesn't know this so it animates from the edge of the Icon which causes the visual bug you see. I'd recommend you use a tool like Rive (rive.app) to accomplish what you want, alternatively, you can use another animation or use some clunky workaround like animating the position while the size transition occurs so that it appears to be centered.

Hi if this is an issue for you, I figured out how both animations will be triggerd. See code below.

Stack(
    children: [
      Center(
        child: ScaleTransition(
          scale: scaleAnimation,
          child: Container(
            height: circleSize,
            width: circleSize,
            decoration: BoxDecoration(
              color: kApprovedColor,
              shape: BoxShape.circle,
            ),
          ),
        ),
      ),
      SizeTransition(
        sizeFactor: checkAnimation,
        axis: Axis.horizontal,
        axisAlignment: -1,
        child: Center(
          child: Icon(Icons.check, color: Colors.white, size: iconSize),
        ),
      ),
    ],
  ),

Flutter's documentation has an explanation:

Like most widgets, SizeTransition will conform to the constraints it is given, so be sure to put it in a context where it can change size. For instance, if you place it into a Container with a fixed size, then the SizeTransition will not be able to change size, and will appear to do nothing.

Thank you. Now I know what I'm doing wrong.

Related