Flutter Animation: use flutter to realize a clapping animation, "golden three silver four" spring move guide

Keywords: Android Design Pattern html Flutter

  • Add and animate these small widget s.

Let's slowly increase the learning curve one by one. First of all, we need to know some basic knowledge about fluent animation.

Understand the components of basic animation in fluent

Animation is just some values that change over time. For example, when we click a button, we want to use animation to make the widget displaying scores rise from the bottom, and when the finger leaves the button, it continues to rise and then hide.

If we only look at the score Widget, we need to change the position and opacity value of the Widget over a period of time.

new Positioned(
        child: new Opacity(opacity: 1.0, 
          child: new Container(
            ...
          )),
        bottom: 100.0
    ); 

Suppose we want the score widget to take 150 milliseconds to display from the bottom. Consider the following timeline:

[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-flyy42f-1630844469980)( https://user-gold-cdn.xitu.io/2019/9/30/16d7fb01f848162e?imageView2/0/w/1280/h/960/ignore -error/1)]

This is a simple 2D graphic. position will change over time. Notice that the diagonal is a straight line. If you like, it can also be a curve.

You can make position increase slowly over time and then become faster and faster. Or you can let it enter at super high speed and slow down at the end.

Here is the first component we introduced: Animation Controller.

scoreInAnimationController = new AnimationController(duration: new Duration(milliseconds: 150), vsync: this); 

Here, we create a simple controller for animation. We have specified that we want the animation to run for 150ms. But what is vsync?

The mobile device refreshes the screen every few milliseconds. This is how we think of a set of images as a continuous stream or movie.

The rate at which the screen refreshes varies from device to device. Suppose the mobile device refreshes the screen 60 times per second (60 frames per second). That is, every 16.67 milliseconds, we provide new images to the brain. Sometimes, the image will be misplaced (a different image will be issued when the screen is refreshed) and the screen will be seen tearing. VSync solves this problem.

We set up a listener for the controller and start the animation:

scoreInAnimationController.addListener(() {
      print(scoreInAnimationController.value);
    });
scoreInAnimationController.forward(from: 0.0);
/* OUTPUT
I/flutter ( 1913): 0.0
I/flutter ( 1913): 0.0
I/flutter ( 1913): 0.22297333333333333
I/flutter ( 1913): 0.3344533333333333
I/flutter ( 1913): 0.4459333333333334
I/flutter ( 1913): 0.5574133333333334
I/flutter ( 1913): 0.6688933333333335
I/flutter ( 1913): 0.7803666666666668
I/flutter ( 1913): 0.8918466666666668
I/flutter ( 1913): 1.0
*/ 

The controller generates a number from 0.0 to 1.0 within 150ms. Note that the resulting values are almost linear. 0.2, 0.3, 0.4... How can we change this behavior? This will be done in part 2: curve animation.

Curve animation
bounceInAnimation = new CurvedAnimation(parent: scoreInAnimationController, curve: Curves.bounceIn);
    bounceInAnimation.addListener(() {
      print(bounceInAnimation.value);
    });

/*OUTPUT
I/flutter ( 5221): 0.0
I/flutter ( 5221): 0.0
I/flutter ( 5221): 0.24945376519722218
I/flutter ( 5221): 0.16975716286388898
I/flutter ( 5221): 0.17177866222222238
I/flutter ( 5221): 0.6359024059750003
I/flutter ( 5221): 0.9119433941222221
I/flutter ( 5221): 1.0
*/ 

By setting the parent attribute as our controller and providing animation to follow curves, we can create a CurvedAnimation. A variety of curves are available on the fluent curve document page for us to choose from: api.flutter.dev/flutter/ani...

The controller provides values from 0.0 to 1.0 for the curve animation Widget in 150ms. The curve animation Widget interpolates these values according to the curve we set.

Although we get a series of values between 0.0 and 1.0, we want the Widget that displays the score to display the value of 0-100. We can simply multiply 100 to get the result, or we can use the third component: the Tween class.

tweenAnimation = new Tween(begin: 0.0, end: 100.0).animate(scoreInAnimationController);
    tweenAnimation.addListener(() {
      print(tweenAnimation.value);
    });

/* Output 
I/flutter ( 2639): 0.0
I/flutter ( 2639): 0.0
I/flutter ( 2639): 33.452000000000005
I/flutter ( 2639): 44.602000000000004
I/flutter ( 2639): 55.75133333333334
I/flutter ( 2639): 66.90133333333334
I/flutter ( 2639): 78.05133333333333
I/flutter ( 2639): 89.20066666666668
I/flutter ( 2639): 100.0
*/ 

The tween class generates values from begin to end. We have used the linear scoreInAnimationController earlier. On the contrary, we can use the rebound curve to obtain different values. The advantages of Tween are far more than these. You can also fill in other things. For example, you can fill in color, offset, position, and other Widget properties, which further extends the basic fill in class.

Score Widget position animation

So far, we have mastered enough knowledge to make our scoring Widget pop up from the bottom when we press the button and hide when we leave.

initState() {
    super.initState();
    scoreInAnimationController = new AnimationController(duration: new Duration(milliseconds: 150), vsync: this);
    scoreInAnimationController.addListener((){
      setState(() {}); // Calls render function
    });
  }

void onTapDown(TapDownDetails tap) {
    scoreInAnimationController.forward(from: 0.0);
    ...    
}
Widget getScoreButton() {
    var scorePosition = scoreInAnimationController.value * 100;
    var scoreOpacity = scoreInAnimationController.value;
    return new Positioned(
        child: new Opacity(opacity: scoreOpacity, 
                           child: new Container(...)
                          ),
        bottom: scorePosition
    );
  } 

[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-vnlphzdq-1630844469982)( https://user-gold-cdn.xitu.io/2019/9/30/16d7faedf946840e?imageslim )]

As shown in the figure above, the score widget pops up from the bottom when you click the button, but there is another small problem: when you click the button many times, the score widget pops up again and again. This is due to a small error in the above code. Each time we click the button, we tell the controller to start from 0, that is, forward(from: 0.0).

score widget exit animation

Now, we add an exit animation for the score Widget. First, we add an enumeration to more easily manage the status of the score Widget.

enum ScoreWidgetStatus {
  HIDDEN,
  BECOMING_VISIBLE,
  BECOMING_INVISIBLE
} 

Then, create a controller to exit the animation. The animation controller will change the position of the score widget from 100 to 150. We also added a state listener for the animation. After the animation, we set the status of the scoring component to hidden.

scoreOutAnimationController = new AnimationController(vsync: this, duration: duration);
    scoreOutPositionAnimation = new Tween(begin: 100.0, end: 150.0).animate(
      new CurvedAnimation(parent: scoreOutAnimationController, curve: Curves.easeOut)
    );
    scoreOutPositionAnimation.addListener((){
      setState(() {});
    });
    scoreOutAnimationController.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        _scoreWidgetStatus = ScoreWidgetStatus.HIDDEN;
      }
    }); 

When the user's finger leaves the component, we will set the state accordingly and start a 300 millisecond timer. After 300 milliseconds, we will add position and opacity animation to the scoring component.

void onTapUp(TapUpDetails tap) {
    // User removed his finger from button.
    scoreOutETA = new Timer(duration, () {
      scoreOutAnimationController.forward(from: 0.0);
      _scoreWidgetStatus = ScoreWidgetStatus.BECOMING_INVISIBLE;
    });
    holdTimer.cancel();
  } 

We also modified the onTapDown event to handle some corner situations.

void onTapDown(TapDownDetails tap) {
    // User pressed the button. This can be a tap or a hold.
    if (scoreOutETA != null) scoreOutETA.cancel(); // We do not want the score to vanish!
    if (_scoreWidgetStatus == ScoreWidgetStatus.HIDDEN) {
      scoreInAnimationController.forward(from: 0.0);
      _scoreWidgetStatus = ScoreWidgetStatus.BECOMING_VISIBLE;
    }
    increment(null); // Take care of tap
    holdTimer = new Timer.periodic(duration, increment); // Takes care of hold
  } 

Finally, we need to select the controller values for the location and opacity of the score widget. A simple switch is done.

Widget getScoreButton() {
    var scorePosition = 0.0;
    var scoreOpacity = 0.0;
    switch(_scoreWidgetStatus) {
      case ScoreWidgetStatus.HIDDEN:
        break;
      case ScoreWidgetStatus.BECOMING_VISIBLE :
        scorePosition = scoreInAnimationController.value * 100;
        scoreOpacity = scoreInAnimationController.value;
        break;
      case ScoreWidgetStatus.BECOMING_INVISIBLE:
        scorePosition = scoreOutPositionAnimation.value;
        scoreOpacity = 1.0 - scoreOutAnimationController.value;
    }
  return ...
} 

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (IMG ioeubznb-1630844469983)( https://user-gold-cdn.xitu.io/2019/9/30/16d7faeddc672fa7? imageslim)]

The score widget works very well. It pops up first and then disappears gradually.

Score Widget size animation

At this point, we almost know how to change the size as the score increases. Let's quickly add size animation, and then continue to work on the spark flicker effect.

I have updated the ScoreWidgetStatus enumeration to retain an additional VISIBLE value. Now we add a new controller for the size attribute.

 scoreSizeAnimationController = new AnimationController(vsync: this, duration: new Duration(milliseconds: 150));
    scoreSizeAnimationController.addStatusListener((status) {
      if(status == AnimationStatus.completed) {
        scoreSizeAnimationController.reverse();
      }
    });
    scoreSizeAnimationController.addListener((){
      setState(() {});
    }); 

The controller generates values from 0 to 1 within 150ms. After completion ((status == AnimationStatus.completed), it will generate values from 1 to 0. This will produce good growth and contraction effects.

void increment(Timer t) {
    scoreSizeAnimationController.forward(from: 0.0);
    setState(() {
      _counter++;
    }); 

We need to pay attention to handling the visible attribute of enumeration. To do this, we need to add some basic conditions to the T} out down event.

void onTapDown(TapDownDetails tap) {
    // User pressed the button. This can be a tap or a hold.
    if (scoreOutETA != null) {
      scoreOutETA.cancel(); // We do not want the score to vanish!
    }
    if(_scoreWidgetStatus == ScoreWidgetStatus.BECOMING_INVISIBLE) {
      // We tapped down while the widget was flying up. Need to cancel that animation.
      scoreOutAnimationController.stop(canceled: true);
      _scoreWidgetStatus = ScoreWidgetStatus.VISIBLE;
    }
    else if (_scoreWidgetStatus == ScoreWidgetStatus.HIDDEN ) {
        _scoreWidgetStatus = ScoreWidgetStatus.BECOMING_VISIBLE;
        scoreInAnimationController.forward(from: 0.0);
    }
    increment(null); // Take care of tap
    holdTimer = new Timer.periodic(duration, increment); // Takes care of hold
  } 

Finally, we use the value of the controller in the Widget

 extraSize = scoreSizeAnimationController.value * 10;
...
height: 50.0 + extraSize,
width: 50.0  + extraSize,
... 

The complete code can be found in github( gist.github.com/Kartik1607/... Found in. We use both size and position animation. The size animation needs some adjustment, which we will introduce at the end.

Finally, spark flicker animation

Before the spark flicker animation, we need to make some adjustments to the size animation. At present, the button has grown too much. The solution is simple. We change the additional multiplier from 10 to a smaller number.

Now let's look at the spark flicker animation. We can see that the spark is actually five pictures with changing position

I made a triangle and a circle picture in MS Paint and saved it as a fluent resource. Then, we can use the image as an Image asset.

Before implementing animation, let's consider positioning and some tasks to be completed:

  • 1. We need to locate 5 pictures, and each picture forms a complete circle at different angles.

  • 2. We need to rotate the picture according to the angle

  • 3. Increase the radius of the circle over time

  • 4. You need to find the coordinates based on the angle and radius.

The simple trigonometric function gives us the formula to obtain the x and y coordinates according to the sine and cosine of the angle.

var sparklesWidget =
        new Positioned(child: new Transform.rotate(
            angle: currentAngle - pi/2,
            child: new Opacity(opacity: sparklesOpacity,
                child : new Image.asset("images/sparkles.png", width: 14.0, height: 14.0, ))
          ),
          left:(sparkleRadius*cos(currentAngle)) + 20,
          top: (sparkleRadius* sin(currentAngle)) + 20 ,
      ); 

Now we need to create five widgets. Each widget has a different angle. A simple for loop is ok.

for(int i = 0;i < 5; ++i) {
      var currentAngle = (firstAngle + ((2*pi)/5)*(i));
      var sparklesWidget = ...
      stackChildren.add(sparklesWidget);
    } 

Divide the 2 * pi (360 degrees) into five parts and create a widget accordingly. Then, we add the widget to the stackChildren array.

Well, at this step, most of the preparations have been completed. We just need to animate sparkleRadius and generate a new firstAngle.

sparklesAnimationController = new AnimationController(vsync: this, duration: duration);
    sparklesAnimation = new CurvedAnimation(parent: sparklesAnimationController, curve: Curves.easeIn);
    sparklesAnimation.addListener((){
      setState(() { });
    });

 void increment(Timer t) {
    sparklesAnimationController.forward(from: 0.0);
     ...
    setState(() {
    ...
      _sparklesAngle = random.nextDouble() * (2*pi);
### Epilogue

Finally, I repeat, if you want to be an excellent person Android Developers, please concentrate on doing in-depth research on basic and important things.

For many junior and intermediate Android For engineers, if they want to improve their skills, they often grope and grow by themselves. The learning effect of fragmentation is inefficient, long and helpless. These architecture technologies hope to Android Development friends have reference and avoid detours. The focus of this paper is whether you have harvest and growth. The rest is not important. I hope readers can keep this in mind.

Finally, if you want to get a high salary, improve your technology and get a qualitative leap in salary. The quickest way is that someone can take you to analyze together, which is the most efficient way to learn. Therefore, in order for you to smoothly advance to middle and senior architects, I specially prepared a set of excellent source code and framework videos for you Android Architect tutorial to ensure that your salary will rise to a higher level after you learn it.

When you have a learning route, what to learn, and know how to go in the future, you always have to practice when you see too much theory.
**[CodeChina Open source projects:< Android Summary of study notes+Mobile architecture video+Real interview questions for large factories+Project practice source code](https://codechina.csdn.net/m0_60958482/android_p7)**

**Advanced learning video**

![](https://img-blog.csdnimg.cn/img_convert/4f1488144bf0e4b377a004e20d7afd84.png)

**Attached: We collected 20 sets of first and second tier Internet companies because of autumn recruitment Android Interview questions** (contain BAT,Xiaomi, Huawei, meituan, Didi) and I sort it out Android Review notes (including Android Basic knowledge points Android Expand knowledge points Android Source code analysis, design pattern summary Gradle Summary of knowledge points and common algorithm problems.)

Capital rose to a higher level.

When you have a learning route, what to learn, and know how to go in the future, you always have to practice when you see too much theory.
**[CodeChina Open source projects:< Android Summary of study notes+Mobile architecture video+Real interview questions for large factories+Project practice source code](https://codechina.csdn.net/m0_60958482/android_p7)**

**Advanced learning video**

[External chain picture transfer...(img-Wdy1WBqi-1630844469985)]

**Attached: We collected 20 sets of first and second tier Internet companies because of autumn recruitment Android Interview questions** (contain BAT,Xiaomi, Huawei, meituan, Didi) and I sort it out Android Review notes (including Android Basic knowledge points Android Expand knowledge points Android Source code analysis, design pattern summary Gradle Summary of knowledge points and common algorithm problems.)

![](https://img-blog.csdnimg.cn/img_convert/4751e4d1c7abe41f7e66927ee4fbee1c.png)

Posted by garrywinkler on Sun, 05 Sep 2021 12:58:44 -0700