Preface:

This is the ninth day of my participation in the August More text Challenge. For details, see: August More Text Challenge. To prepare for the August challenge of the nuggets, I’m going to pick 31 components this month that haven’t been introduced before and do a full analysis and attribute introduction. These articles will be used as important material in the collection of the Flutter components. I hope I can stick to it, your support will be my biggest motivation ~

This series Component articles The list of
1.NotificationListener 2.Dismissible 3.Switch
4.Scrollbar 5.ClipPath 6.CupertinoActivityIndicator
7.Opacity 8.FadeTransition 9. AnimatedOpacity [this article]

First, understand AnimatedOpacity component

The previous two articles introduced Opacity and FadeTransition, both of which are related to the Opacity of the operating components. Besides these two, there is another component related to transparency: AnimatedOpacity. Just like the FadeTransition component, it also animates the opacity gradient. What are the similarities and differences between FadeTransition and FadeTransition? Why does the framework provide two components with the same function? With this question in mind, let’s begin to understand the AnimatedOpacity component.


1. Basic information about AnimatedOpacity

As we can see from the inheritance tree, this component is inherited from ImplicitlyAnimatedWidget and is a StatefulWidget. ImplicitlyAnimatedWidget is called implicit animation. The best feature of ImplicitlyAnimatedWidget is that users can animate transformations without having to provide their own animator.


As you can see by entering the structure of AnimatedOpacity, it does not need to be passed into the animator, but you can specify the duration and animation curve associated with the animation. From here, it’s easy to guess that the ImplicitlyAnimatedWidget related classes maintain the animation controller, encapsulating the actions of the animation. The goal is clear: avoid direct contact with the animator and make it easier to use. From here comes the question: since the user cannot directly manipulate the animator, how is the animation enabled? With questions in mind, keep reading.


2. The use of AnimatedOpacity

Let’s first take a look at a small demo used by AnimatedOpacity. In the following example, Switch the value of opacity in AnimatedOpacity. As you can see, when the opacity configuration changes, the animation is executed.

In the following code, use the Wrap component to vertically Wrap the Switch and the Container, and place AnimatedOpacity in the Container to animate an icon. When the Switch is clicked, AnimatedOpacity is recreated and the configuration value of opacity is updated. The animation is then magically executed, and as you can see, the execution of the animation is related to the component refactoring.

class AnimatedOpacityDemo extends StatefulWidget {
  @override
  _AnimatedOpacityDemoState createState() => _AnimatedOpacityDemoState();
}

class _AnimatedOpacityDemoState extends State<AnimatedOpacityDemo> {
  final double beginOpacity = 1.0;
  final double endOpacity = 0;

  late double _opacity;

  @override
  void initState() {
    super.initState();
    _opacity = beginOpacity;
  }

  bool get selected => _opacity == 0;

  @override
  Widget build(BuildContext context) {
    return Wrap(
      direction: Axis.vertical,
      crossAxisAlignment: WrapCrossAlignment.center,
      children: <Widget>[
        Switch(
          value: selected,
          onChanged: onChanged,
        ),
        Container(
          color: Colors.grey.withAlpha(22),
          width: 100,
          height: 100,
          child: buildAnimatedOpacity(),
        ),
      ],
    );
  }

  Widget buildAnimatedOpacity() =>
      AnimatedOpacity(
        duration: const Duration(seconds: 1),
        curve: Curves.fastOutSlowIn,
        opacity: _opacity,
        child: _buildChild(),
      );

  void onChanged(bool value) {
    setState(() {
      _opacity = value ? endOpacity : beginOpacity;
    });
  }

  Widget _buildChild() =>
      const Icon( Icons.add_to_drive, color: Colors.green, size: 60 );
}
Copy the code

The value of 3. AnimatedOpacity

The most important feature of AnimatedOpacity is that it animates whenever its opacity property changes and is rebuilt. The following example illustrates its value: Control the opacity of AnimatedOpacity by sliding the Slider, and rebuild the AnimatedOpacity. This will animate the gradient to the opacity whenever you slide to a new value. If you use the FadeTransition component and use the vertical animator to do this, it will be more complicated. Implicit animations, such as the AnimatedOpacity component, significantly lower the threshold for users to use animations.

// The Slider component is built
Container(
  height: 50,
  child: Slider(
    label: "$_opacity",
    value: _opacity,
    divisions: 5,
    onChanged: onChanged,
  ),
),

void onChanged(double value) {
  setState(() {
    _opacity = value;
  });
}
Copy the code

In addition, AnimatedOpacity can monitor the completion time of the animation through the onEnd callback.


Source implementation of AnimatedOpacity

1. AnimatedOpacity source analysis

As mentioned above, AnimatedOpacity is inherited from ImplicitlyAnimatedWidget.

While The ImplicitlyAnimatedWidget, most StatefulWidget, needs to implement createState to create a State object, but since it is an abstract class, it can choose not to implement it and let the subclass do it.

No implementation in ImplicitlyAnimatedWidget createState, and the condition that the return type will be restricted to ImplicitlyAnimatedWidgetState < ImplicitlyAnimatedWidget >.


AnimatedOpacity, as a subclass of ImplicitlyAnimatedWidget, implements the createState abstract method. As follows, the state class is _AnimatedOpacityState.

This source is clarified on the level of components, ImplicitlyAnimatedWidget equivalent to an intermediate layer, can do some things by ImplicitlyAnimatedWidgetState extra. Its subclasses of component to create the state of the class, also need to inherit from ImplicitlyAnimatedWidgetState. So let’s see what the state class does.


2. Processing of state classes

The implementation looks at _AnimatedOpacityState as the implementation class. Very simply, maintain _opacityAnimation, relying on the FadeTransition component for animation. If you want to call the good guy.


The source of the animation controller lies in the state of the abstract class ImplicitlyAnimatedWidgetState, here will provide the animation controller to monitor, and other maintenance work.

The animator is listened on in initState and the onEnd callback is executed if the animation is complete.


3. Start the animation

By looking at this, you should be able to guess when the animation will trigger. The state classes are not reinitialized when the StatefulWidget is rebuilt. Instead, the State#didUpdateWidget is triggered to notify the Widget configuration changes, where you can handle the logic of component updates. It can be seen that in ImplicitlyAnimatedWidgetState# didUpdateWidget, comparing the configuration, change will be updated. Finally, the _controller will perform the forward animation.


The AnimatedOpacity component is essentially a layer of encapsulation between the animation controller and the FadeTransition component. Simplify the threshold for users to use animation, reduce the error rate of animation use. The same goes for other implicit animation components. The use of AnimatedOpacity is over here. Thank you for watching and see you tomorrow