Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

preface

When we use the Stack, we usually use the Positioned child of the Stack to position the Positioned child against the Stack. Offer the position and size of the vertical position through top, bottom and height. Choose two from right and width to determine the horizontal position and size. Correspondingly, we can use the animatedtoy component to achieve the movement of the child in the Stack, the change in size and so on. In fact, we celebrate the launch of Shenzhou XIII at 🚀🚀🚀, a rocket launch animation has introduced the Animatedtoy assembly, and in this piece we use this assembly to create a animation of the two balls chasing, so as to understand fully the use of this assembly.

Effect of parsing

As you can see in the driven image, the blue ball and orange ball move one after the other along the left, bottom, right, and top vertices of the white circle, with the effect of bouncing when the switch is initiated. The specific implementation logic is as follows:

  • Moving the effect around the big circle for 4 points, we can control the starting position of the ball and we are done.
  • Bounce effects can be usedbounceInCurve implementation.
  • For the one-first-one-last chase effect, you can set an ordered array of positions, where the position of one ball is the next position of another ball, by fetching positions from the array of positions using different subscripts.
  • The realization of repetitive dynamic effect: the use of repetitive dynamic effectAnimatedPositionedonEndTrigger, called at the end of eachsetStateAdd 1 to the subscript of the control position to achieve the effect of animation loop.

Code implementation

The code implementation is as follows:

class _AnimatedPositionedDemoState extends State<AnimatedPositionedDemo> {
  final roundSize = 200.0;
  var ballSize = 50.0;
  late List<Offset> animatedOffsets;
  int index = 0;
  @override
  void initState() {
    animatedOffsets = [
      Offset(0.0, (roundSize - ballSize) / 2),
      Offset((roundSize - ballSize) / 2, roundSize - ballSize),
      Offset(roundSize - ballSize, (roundSize - ballSize) / 2),
      Offset((roundSize - ballSize) / 2.0.0)];super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('AnimatedPositioned'),
        brightness: Brightness.dark,
        backgroundColor: Colors.black,
      ),
      backgroundColor: Colors.black,
      body: Center(
        child: Stack(children: [
          ClipOval(
            child: Container(
              width: roundSize,
              height: roundSize,
              color: Colors.white,
            ),
          ),
          AnimatedPositioned(
            top: animatedOffsets[index].dy,
            height: ballSize,
            left: animatedOffsets[index].dx,
            width: ballSize,
            child: ClipOval(
              child: Container(
                color: Colors.blue,
              ),
            ),
            duration: Duration(seconds: 2),
            curve: Curves.bounceIn,
            onEnd: () {
              setState(() {
                index = (index + 1) % animatedOffsets.length;
              });
            },
          ),
          AnimatedPositioned(
            top: animatedOffsets[(index + 1) % animatedOffsets.length].dy,
            height: ballSize,
            left: animatedOffsets[(index + 1) % animatedOffsets.length].dx,
            width: ballSize,
            child: ClipOval(
              child: Container(
                color: Colors.orange,
              ),
            ),
            duration: Duration(seconds: 2),
            curve: Curves.bounceIn,
          ),
        ]),
      ),
      floatingActionButton: FloatingActionButton(
        child: Text(
          'walk you',
          style: TextStyle(
            color: Colors.white,
          ),
          textAlign: TextAlign.center,
        ),
        onPressed: () {
          setState(() {
            index = (index + 1) % 4; }); },),); }}Copy the code

The key elements are:

AnimatedPositioned(
  top: animatedOffsets[index].dy,
  height: ballSize,
  left: animatedOffsets[index].dx,
  width: ballSize,
  child: ClipOval(
    child: Container(
      color: Colors.blue,
    ),
  ),
  duration: Duration(seconds: 2),
  curve: Curves.bounceIn,
  onEnd: () {
    setState(() {
      index = (index + 1) % animatedOffsets.length; }); },),Copy the code

We control the starting position of the ball with left and top, and then call setState on onEnd to update the position subscript to switch the ball position to the next adjacent vertex.

Variation of gameplay

We can actually increase the ball, adjust the span or position of the ball at which it moves, and do some other interesting animations. For example, if there are 4 balls, each time the span of 2 vertices (subscript each time +2), there will be a feeling of 4 balls passing through the opposite side. The source code for this animation has been uploaded to: animation related source code.

.

conclusion

21. This introduces the effect of the animation of chasing the ball using animatedtoy. By changing the position, size or color, you can do some interesting animation. Flutter fans who are interested in Flutter can also make other attempts to create interesting animations so that the loading process will not be boring if it takes a long time to load.

I am dao Code Farmer with the same name as my wechat official account. This is a column about the introduction and practice of Flutter, providing systematic learning articles about Flutter. See the corresponding source code here: The source code of Flutter Introduction and Practical column. If you have any questions, please add me to the wechat account: island-coder. If you feel you have something to gain, please give three pairs of love as follows:

👍🏻 : a praise to encourage!

🌟 : Collect articles, easy to look back!

💬 : Comment exchange, mutual progress!