In Android, we can design our own Latyout animation based on layout changes, and even vector animation can seamlessly switch the path, such as the natural transition from circle to rectangle

The name of the Flutter has been changed to “implicit animation”. I would like to say that there is no need to make coder feel uncomfortable. It is not good to continue the android tradition

  • AnimatedSwitcher– The widget can play its own animation when the content changes
  • AnimatedContainer– The Container is used like a Container. When the color, width, height, and rounded corners of the Container change, the animation can not be controlled. It is similar to the animation of path
  • AnimatedCrossFade– Animation can be displayed when switching between different layouts, but you can’t set your own animation, the default is to fade in and out, and the display is not good when switching between different sizes
  • DecoratedBoxTransition– Border animation, the core is to change the shape by changing the rounded Angle, this change is natural transition, this is the same as path animation
  • AnimatedDefaultTextStyle– The switching animation when the text style is changed, mainly presents the animation about the size transformation, the color gradient is not obvious, but the experience is not good, the change of font thickness when the size word is changed is really a little bit irritating, a little bit slow
  • AnimatedModalBarrier– The color change animation must be placed in the child of the widget. It has a specific application scenario, such as changing the background color when the dialog pops up
  • AnimatedOpacity– Opacity change animation
  • AnimatedPhysicalModel– Shadow transform animation, the setup is a bit complicated
  • AnimatedPositioned– Stack widget position and size change animation
  • AnimatedSize– Widget size change animation

AnimatedContainer

AnimatedContainer, as the name implies, is the Container that drives the drawing. The property Settings are the same as those used in the Container. The difference is that you can set the animation time and interpolator

Animation effect is similar to vector animation, which can achieve seamless switching between the states before and after, and the system completes the numerical output of each frame of animation. But can achieve the effect of vector animation attributes only: color, width, height, rounded corners, other are not, such as picture switching is a switch, shape switching, such as zooming from a circle to a rectangle, the circle in the maximum moment will switch to a rectangle, rectangle then slowly zooming

The use of AnimatedContainer is to write the property value outside and change the property value with setState to switch the animation

Here’s my example:

class Test3 extends State<TestWidget> with SingleTickerProviderStateMixin {
  double width = 50;
  double height = 50;
  Color color = Colors.blueAccent;
  BoxShape shape = BoxShape.circle;

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        AnimatedContainer(
          duration: Duration(seconds: 1),
          width: width,
          height: height,
          decoration: BoxDecoration(
            color: color,
            shape: shape,
          ),
        ),
        RaisedButton(
          child: Text("AAA"),
          onPressed: () {
            setState(() {
              width = 300;
              height = 300; color = Colors.pink; shape = BoxShape.rectangle; }); },),,); }}Copy the code

A good example is given here, as far as the limits of the AnimatedContainer can be achieved, where the shape is changed by changing the Angle of the rounded rectangle: borderradius.circular (8);

class TestWidgetState extends State<TestWidget>
    with SingleTickerProviderStateMixin {
  double _width = 50;
  double _height = 50;
  Color _color = Colors.green;
  BorderRadiusGeometry _borderRadius = BorderRadius.circular(8);

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Center(
          child: AnimatedContainer(
            // Use the properties stored in the State class.
            width: _width,
            height: _height,
            decoration: BoxDecoration(
              color: _color,
              borderRadius: _borderRadius,
            ),
            // Define how long the animation should take.
            duration: Duration(seconds: 1),
            // Provide an optional curve to make the animation feel smoother.
            curve: Curves.fastOutSlowIn,
          ),
        ),
        RaisedButton(
          child: Icon(Icons.play_arrow),
          // When the user taps the button
          onPressed: () {
            // Use setState to rebuild the widget with new values.
            setState(() {
              // Create a random number generator.
              final random = Random();

              // Generate a random width and height.
              _width = random.nextInt(300).toDouble();
              _height = random.nextInt(300).toDouble();

              // Generate a random color.
              _color = Color.fromRGBO(
                random.nextInt(256),
                random.nextInt(256),
                random.nextInt(256),
                1,);// Generate a random border radius.
              _borderRadius =
                  BorderRadius.circular(random.nextInt(100).toDouble()); }); },),,); }}Copy the code

Finally, note that the AnimatedContainer animation can only manipulate the properties of the Container itself, not the child widgets in the Child


AnimatedSwitcher

The basic use

AnimatedSwitcher is an animation style provided by Flutter for switching between widgets. Currently, content changes can only be supported for the same widget. Switching between widgets of different types is still being investigated

There are several AnimatedSwitcher attributes:

  • child– Content switch animation on widgets
  • duration– Animation from A -> B time
  • reverseDuration– Animation is reversed from B -> A time
  • switchInCurve– Animation from A -> B animation interpolator,Curves.linear
  • switchOutCurve– Get interpolator in reverse
  • transitionBuilderAnimation –
  • layoutBuilder– The component that wraps the old and new widgets, a Stack by default

Note that the flutter widget tree has its own cache. The same widget will not be rebuilt if its content is updated, but the AnimatedSwitcher must have 2 widgets to animate it. Therefore, manually set the key to circumvent the widget caching mechanism in Flutter

This part of the web is mostly from official documentation: 9.6 general “AnimatedSwitcher”

The official document does not have GIF, can not see the effect, here I post the effect and code:

class TestWidgetState extends State<TestWidget> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          AnimatedSwitcher(
            duration: const Duration(milliseconds: 500),
            transitionBuilder: (Widget child, Animation<double> animation) {
              // Perform the zoom animation
              return ScaleTransition(child: child, scale: animation);
            },
            child: Text(
              '$_count'.// Display the specified key. Different keys are treated as different Text so that animation can be performed
              key: ValueKey<int>(_count),
              style: Theme.of(context).textTheme.display1,
            ),
          ),
          RaisedButton(
            child: const Text('+ 1',),
            onPressed: () {
              setState(() {
                _count += 1; }); },),],),); }}Copy the code

Then let’s do another example of an icon toggle

class TestWidgetState extends State<TestWidget> {
  IconData _actionIcon = Icons.delete;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          AnimatedSwitcher(
            transitionBuilder: (child, anim) {
              return ScaleTransition(child: child, scale: anim);
            },
            duration: Duration(milliseconds: 200),
            child: IconButton(
              key: ValueKey(_actionIcon),
              icon: Icon(_actionIcon),
            ),
          ),
          RaisedButton(
            child: Text("Toggle icon"),
            onPressed: () {
              setState(() {
                if (_actionIcon == Icons.delete)
                  _actionIcon = Icons.done;
                else_actionIcon = Icons.delete; }); },),],),); }}Copy the code

AnimatedSwitcher principle

Actually the principle is very simple, say to understand. Because the Child Widget in the AnimatedSwitcher has a different key, each time the content of the Child Widget changes, it is considered that a new widget appears and will be rebuilt. We get the new old widget in the didUpdateWidget, animate the old widget backward, animate the new widget forward, and that’s it. Here’s the source code:

void didUpdateWidget(AnimatedSwitcher oldWidget) {
  super.didUpdateWidget(oldWidget);
  // Check whether the new and old child have changed (return true if the key and type are equal)
  if (Widget.canUpdate(widget.child, oldWidget.child)) {
    // Child does not change...
  } else {
    // The child is changed, and a Stack is constructed to animate the old and new child separately
   _widget= Stack(
      alignment: Alignment.center,
      children:[
        // Old child application FadeTransition
        FadeTransition(
         opacity: _controllerOldAnimation,
         child : oldWidget.child,
        ),
        // New child application FadeTransition
        FadeTransition(
         opacity: _controllerNewAnimation,
         child : widget.child,
        ),
      ]
    );
    // Perform a reverse exit animation for the old child
    _controllerOldAnimation.reverse();
    // Animate the forward entry for the new child_controllerNewAnimation.forward(); }}Copy the code

AnimatedSwitcher Advanced use

The AnimatedSwitcher will perform a forward and reverse animation between the old and new widgets. The special token is where the animation came from, so the old text comes in from the right, so the old text goes out from the right. In general, the animation must be executed sequentially

So we can do what we want, like in on the right, out on the left. We can do that, we don’t have to change the AnimatedSwitcher, we can change the animation API FlideTransition, all the animations are written on top of the AnimationWidget

For this, we’ll do what FlideTransition does internally, adding a minus sign to the X-axis when it flips, and that’s how we do it most of the time

This example is from the official document. I have changed the code to some extent, mainly to make it more convenient to use and higher encapsulation, and to make encapsulation for the X/Y axis

  • This is a modified version of SlideTransition, internally using the original implementation of SlideTransition. The core is to process the Offset coordinate data in response to the inversion of the animation. Remember this routine, as well as everything else

enum FreeSlideTransitionMode {
  reverseX,
  reverseY,
}

class FreeSlideTransition extends AnimatedWidget {
  
  Animation<Offset> animation;
  var child;
  Offset begin;
  Offset end;
  FreeSlideTransitionMode type;

  // Different data processing for x and y axis reversal playback, with map bearing
  Map<FreeSlideTransitionMode, Function(Animation animation, Offset offset)> worksMap = {
    // X-axis reverse operation, typical application, right in and left out
    FreeSlideTransitionMode.reverseX: (Animation animation, Offset offset) {
      if (animation.status == AnimationStatus.reverse) {
        return offset = Offset(-offset.dx, offset.dy);
      }
      return offset;
    },
    FreeSlideTransitionMode.reverseY: (Animation animation, Offset offset) {
      if (animation.status == AnimationStatus.reverse) {
        return offset = Offset(offset.dx, -offset.dy);
      }
      returnoffset; }};// Constructor polymorphisms are a bit cumbersome to write and look like
  FreeSlideTransition(this.type, this.child,
      {Key key, Animation<double> animation, Offset begin, Offset end})
      : super(key: key, listenable: animation) {
    this.animation = Tween<Offset>(begin: begin, end: end).animate(animation);
  }

  FreeSlideTransition.reverseX(
      {Widget child,
      Animation<double> animation,
      Key key,
      Offset begin,
      Offset end})
      : this(FreeSlideTransitionMode.reverseX, child,
            key: key, animation: animation, begin: begin, end: end);

  FreeSlideTransition.reverseY(
      {Widget child,
      Animation<double> animation,
      Key key,
      Offset begin,
      Offset end})
      : this(FreeSlideTransitionMode.reverseY, child,
            key: key, animation: animation, begin: begin, end: end);

  @override
  Widget build(BuildContext context) {
    var offset = animation.value;
    offset = worksMap[type](animation, offset);

    // SlideTranslation is implemented in this way
    returnFractionalTranslation( translation: offset, child: child, ); }}Copy the code
  • Try it out: In Column, sometimes the position will be invalid, and a padding in the middle will be fine
class Test3 extends State<TestWidget> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          AnimatedSwitcher(
            duration: const Duration(milliseconds: 500),
            transitionBuilder: (Widget child, Animation<double> animation) {
              // Perform the zoom animation
              return FreeSlideTransition.reverseY(
                animation: animation,
                begin: Offset(0.1),
                end: Offset(0.0),
                child: child,
              );
            },
            child: Text(
              '$_count'.// Display the specified key. Different keys are treated as different Text so that animation can be performed
              key: ValueKey<int>(_count),
              style: Theme.of(context).textTheme.display1,
            ),
          ),

// Padding(
/ / padding: EdgeInsets. All (20.0),
/ /),
// Text("AAA"),

          RaisedButton(
            child: Text(
              '+ 1',
            ),
            onPressed: () {
              setState(() {
                _count += 1; }); },),],),); }}Copy the code

This FreeSlideTransition can be stored in the lib library. If you want to write this transition, you can write it in the lib library

Some of you don’t understand why you have to write tween in AnimatedSwitcher. The default value of the AnimationControl is [0-1]. If we want to use our own data, we have to write our own Tween


AnimatedCrossFade

AnimatedCrossFade displays animations when switching between different layouts, but you can’t set the animations yourself. The default is to fade in and out, and it doesn’t look good when switching between different sizes

I’m going to do a GIF to show you how the widgets feel when they switch between different sizes. Small to large is fine, but small to small is not

AnimatedCrossFade: Unfortunately, you can’t set the animation by yourself. The default is a gradient animation, which is quite limited

In code, we need to specify whether to display the Frist widget or the second widget. We write a flag around the widget. SetState changes this flag to trigger the animation

class Test3 extends State<TestWidget> with SingleTickerProviderStateMixin {
  var isFristShow = true;

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        AnimatedCrossFade(
          firstChild: Container(
            alignment: Alignment.center,
            width: 200,
            height: 200,
            decoration: BoxDecoration(
              shape: BoxShape.circle,
              color: Colors.blueAccent,
            ),
            child: Text("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
          ),
          secondChild: Container(
            alignment: Alignment.center,
            width: 300,
            height: 300,
            child: Text("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"),
            decoration:
                BoxDecoration(shape: BoxShape.rectangle, color: Colors.pinkAccent),
          ),
          crossFadeState: isFristShow
              ? CrossFadeState.showFirst
              : CrossFadeState.showSecond,
          duration: Duration(milliseconds: 300),
          reverseDuration: Duration(milliseconds: 300),
        ),
        RaisedButton(
          child: Text("AAA"), onPressed: () { setState(() { isFristShow = ! isFristShow; }); },),,); }}Copy the code

DecoratedBoxTransition

DecoratedBoxTransition is a bounding animation, you can only animate a bounding animation, but it’s what we’ve been looking for, and it allows for a natural transition of widget shapes, just like we do with AnimatedContainer, The natural transition of the shape still depends on the degree of the rounded rectangle

DecoratedBoxTransition attributes:

  • child
  • decoration– Represents the change in the animation property value passed in, and fills the border of the child by fetching its valueposition– Represents the position of the border animation, either the foreground position or the background position, which will cover the Child element

The DecoratedBoxTransition child does not have a background. The DecorationTween value generator automatically assigns the shape background to the child

    _animation = DecorationTween(
      begin: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(0.0)),
        color: Colors.red,
      ),
      end: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(100.0)),
        color: Colors.green,
      ),
    )
Copy the code

class Test3 extends State<TestWidget> with SingleTickerProviderStateMixin {
  Animation<Decoration> _animation;
  AnimationController _controller;
  Animation _curve;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      duration: const Duration(milliseconds: 1000),
      vsync: this,); _curve = CurvedAnimation(parent: _controller, curve: Curves.fastOutSlowIn); _animation = DecorationTween( begin: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(0.0)),
        color: Colors.red,
      ),
      end: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(100.0)), color: Colors.green, ), ).animate(_curve) .. addStatusListener((AnimationStatus state) {if (state == AnimationStatus.completed) {
          _controller.reverse();
        } else if(state == AnimationStatus.dismissed) { _controller.forward(); }}); _controller.forward(); }@override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Column(
      children: <Widget>[
        DecoratedBoxTransition(
          position: DecorationPosition.background,
          decoration: _animation,
          child: Container(
              child: Container(
            padding: EdgeInsets.all(50),
            child: Text("AAAAAA"() (() [(). }}Copy the code

AnimatedDefaultTextStyle

AnimatedDefaultTextStyle switching animated text style changes, mainly presents the transformation in terms of the size of the animation, color gradient is not obvious, but the place with bad experience is that the size of the word when switching font weight change real is a bit hot eye, especially when large size

The AnimatedDefaultTextStyle and AnimatedContainer are designed, used, and named in completely different ways. Google what are you up to, code quality review? AnimatedDefaultTextStyle and AnimatedContainer are actually the same thing, but there is no communication between the developers. It is a pain to make two things

AnimatedDefaultTextStyle is used as an outer widget for text. Instead of text, it should be AnimatedContainer. Most AnimatedDefaultTextStyle AnimatedDefaultTextStyle AnimatedDefaultTextStyle AnimatedDefaultTextStyle AnimatedDefaultTextStyle AnimatedDefaultTextStyle AnimatedDefaultTextStyle

The trigger of the animation is again controlled by the outer variable, which is triggered by staState

class Test3 extends State<TestWidget> with SingleTickerProviderStateMixin {

  var _isSelected = true;
  var info1 = "Flutter !!!";
  var info2 = "is not you !!!";

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: <Widget>[
        AnimatedDefaultTextStyle(
          softWrap: false,
          textAlign: TextAlign.right,
          maxLines: 1,
          overflow: TextOverflow.ellipsis,
          curve: Curves.linear,
          duration: Duration(milliseconds: 300),
          child: Text( info2),
          style: _isSelected
              ? TextStyle(
                  fontSize: 10.0,
                  color: Colors.red,
                  fontWeight: FontWeight.bold,
                )
              : TextStyle(
                  fontSize: 30.0,
                  color: Colors.black,
                  fontWeight: FontWeight.w300,
                ),
        ),
        RaisedButton(
          child: Text("AA"), onPressed: () { setState(() { _isSelected = ! _isSelected; }); },),,); }}Copy the code

If the number of lines before and after the text switch is not the same, the animation will be more ugly, you see the following example, when using you remember to change one line to multiple lines


AnimatedModalBarrier

  • AnimatedModalBarrierThe color change animation is unique in that it must be placed in the child of the widget. It has specific application scenarios, such as changing the background color when the dialog pops up

AnimatedModalBarrier takes several parameters, which I don’t know what they are other than color:

  • color– Color value animation changes
  • dismissible– Whether to touch the current ModalBarrier to pop up the current route
  • semanticsLabel– Semantic labels
  • barrierSemanticsDismissible– Whether the ModalBarrier semantics are included in the semantic tree

Color receives an animation object, so we can set the color values, duration, add controls, etc. The API has much more freedom than other similar transform animation apis

In the example below I set up a loop

class Test3 extends State<TestWidget> with SingleTickerProviderStateMixin {

  AnimationController _controller;
  Animation _curve;

  Animation<Color> animation;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      duration: const Duration(milliseconds: 1000),
      vsync: this,).. addStatusListener((AnimationStatus state) {if (state == AnimationStatus.completed) {
          _controller.reverse();
        } else if(state == AnimationStatus.dismissed) { _controller.forward(); }}); animation = ColorTween( begin: Colors.blue, end: Colors.pinkAccent, ).animate(_controller); }@override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.center,
      fit: StackFit.expand,
      children: <Widget>[
        Container(
          width: 300,
          height: 300,
          child: AnimatedModalBarrier(
            semanticsLabel: "StackBarrier",
            barrierSemanticsDismissible: true,
            dismissible: true,
            color: animation,
          ),
        ),
        Positioned(
          left: 20,
          top: 20,
          child: RaisedButton(
            child: Text("AA"), onPressed: () { _controller.forward(); },),),],); }}Copy the code

AnimatedOpacity

AnimatedOpacity: AnimatedOpacity: AnimatedOpacity: AnimatedOpacity: AnimatedOpacity: AnimatedOpacity: AnimatedOpacity: AnimatedOpacity: AnimatedOpacity: AnimatedOpacity: AnimatedOpacity

class Test3 extends State<TestWidget> with SingleTickerProviderStateMixin {

  AnimationController _controller;
  Animation<Color> animation;
  double opacity = 1.0;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this,); animation = ColorTween( begin: Colors.blue, end: Colors.pinkAccent, ).animate(_controller); }@override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.center,
      fit: StackFit.expand,
      children: <Widget>[
        AnimatedOpacity(
          curve: Curves.fastOutSlowIn,
          opacity: opacity,
          duration: Duration(seconds: 1),
          child: Container(
            width: 300,
            height: 300,
            child: AnimatedModalBarrier(
              semanticsLabel: "StackBarrier",
              barrierSemanticsDismissible: true,
              dismissible: true,
              color: animation,
            ),
          ),
        ),
        Positioned(
          left: 20,
          top: 20,
          child: RaisedButton(
            child: Text("AA"),
            onPressed: () {
              setState(() {
                opacity = 0.3; _controller.forward(); }); },),),],); }}Copy the code

AnimatedPhysicalModel

The AnimatedPhysicalModel properties are as follows:

  • shape: Shadow shape
  • clipBehavior: Shadow cropping method
    • Clip.noneNo pattern:
    • Clip.hardEdge: Slightly faster clipping speed, but easy to distort, serrated
    • Clip.antiAlias: Cutting edge anti-aliasing, making cutting smoother, this mode cutting speed is faster than antiAliasWithSaveLayer, but slower than hardEdge
    • Clip.antiAliasWithSaveLayer: After clipping, it has anti-aliasing features and allocates a screen buffer. All subsequent operations are carried out in the buffer
  • borderRadius: Border of the background
  • elevation: Depth of the shadow color value
  • colorBackground color:
  • animateColor: Whether the background color is displayed in animated form
  • shadowColor: Animation value of the shadow
  • animateShadowColor: Whether the shadows are animated

Look at a lot, but we don’t meng, in fact, a useful, is shadowColor, we change the shadowColor will trigger animation, but color this attribute must be set, or error

In terms of overall animation, I really don’t know where this animation is applied, whether it is like the above one, to do the change of scenery on the back of the click, who knows…

class Test3 extends State<TestWidget> with SingleTickerProviderStateMixin {
 
  var isShadow = true;

  @override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.center,
      fit: StackFit.loose,
      children: <Widget>[
        AnimatedPhysicalModel(
          curve: Curves.fastOutSlowIn,
          color: Colors.grey.withOpacity(0.2),
          clipBehavior: Clip.antiAliasWithSaveLayer,
          borderRadius: BorderRadius.circular(12.0),
          animateColor: true,
          animateShadowColor: true,
          shape: BoxShape.rectangle,
          shadowColor: isShadow ? _shadowColor1 : _shadowColor2,
          elevation: 5.0,
          duration: Duration(milliseconds: 300),
          child: Container(
            width: 200,
            height: 200,
            child: Text("AA"),
          ),
        ),
        Positioned(
          left: 20,
          top: 20,
          child: RaisedButton(
            child: Text("AA"), onPressed: () { setState(() { isShadow = ! isShadow; }); },),),],); }}Copy the code

AnimatedPositioned

And you can see by the name of the animatedtoy, which is exactly the same AS the ORIGINAL API, that would be nice, so you would know exactly what it is, but again diss the name and the design of the other piece of rubbish

I don’t want to talk about that, because you can read the code

class Test3 extends State<TestWidget> with SingleTickerProviderStateMixin {
 
  var isPosition = true;

  var top1 = 20.0;
  var left1 = 20.0;
  var width1 = 200.0;
  var height1 = 200.0;

  var top2 = 100.0;
  var left2 = 100.0;
  var width2 = 300.0;
  var height2 = 300.0;

  @override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.center,
      fit: StackFit.expand,
      children: <Widget>[
        AnimatedPositioned(
          top: isPosition ? top1 : top2,
          left: isPosition ? left1 : left2,
          width: isPosition ? width1 : width2,
          height: isPosition ? height1 : height2,
          child: Container(
            color: Colors.blueAccent,
          ),
          duration: Duration(milliseconds: 300),
        ),
        Positioned(
          left: 20,
          top: 20,
          child: RaisedButton(
            child: Text("AA"), onPressed: () { setState(() { isPosition = ! isPosition; }); },),),],); }}Copy the code

AnimatedSize

In the GIF below, you can see that if the size of the widget is changed, the widget will be moved, but the size will not be animated. This is not very nice

class Test3 extends State<TestWidget> with SingleTickerProviderStateMixin {
  double size1 = 200;
  double size2 = 300;

  var isSize = true;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        RaisedButton(
          child: Text("AA"), onPressed: () { setState(() { isSize = ! isSize; }); }, ), AnimatedSize( alignment: Alignment.center, curve: Curves.fastOutSlowIn, vsync:this,
          duration: Duration(seconds: 1),
          reverseDuration: Duration(seconds: 2), child: Container( width: isSize ? size1 : size2, height: isSize ? size1 : size2, color: Colors.blueAccent, ), ), ], ); }}Copy the code