Preface:

This is the 11th 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
10. FadeInImage 11. Offstage[this article]

1. Understand the Offstage component

You probably know that the Offstage component makes child components show/hide, but it’s rarely used. After all, there are other ways to make a component show/hide. For example, what is the value of the Offstage component, why is it there, and what features does it have? With that in mind, let’s take a closer look at the Offstage component today.


1. Basic information about Offstage

Below is the Offstage component class definition and construction method, it can be seen that it inherits from SingleChildRenderObjectWidget. Instantiation can pass Boolean offstage and Child components.


2. Use of Offstage

The use of Offstage is very simple. You can show or hide the Child component by giving the value of Offstage. Offstage :true indicates not on the stage, that is, hidden. An example of the use of Offstage can be found in the source code comments.

Show or hide the buildChild build icon component by clicking the button to toggle the state of _offstage. As you can see, children hidden by the Offstage component take no place on the screen.

class OffstageDemo extends StatefulWidget {
  @override
  _OffstageDemoState createState() => _OffstageDemoState();
}

class _OffstageDemoState extends State<OffstageDemo> {
  bool _offstage = true;
  
  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        ElevatedButton(
          child: const Text('Switch between explicit and implicit'), onPressed: () { setState(() { _offstage = ! _offstage; }); }, ), Offstage( offstage: _offstage, child: buildChild(), ), Text('Whether icon is hidden:$_offstage'),]); } Widget buildChild() =>const Padding(
        padding: EdgeInsets.all(10),
        child: Icon(
          Icons.camera_outlined,
          color: Colors.green,
          size: 50,),); }Copy the code

3. The characteristics of the Offstage

The example above illustrates the use and function of Offstage, and many people have only dipped into it here. However, when we look at the rendering tree, we can find that the rendering object corresponding to the hidden component is still in the tree. In other words, it’s not visible, but it’s still there.

This allows us to retrieve the rendered object via GlobalKey and get the hidden component size. Note: This operation does not mean that the component size should be obtained by Offstage, but rather that Offstage can obtain the hidden component size. Any component can use GlobalKey to get the render object size. This is just a description of the components that are hidden by the Offstage; the corresponding render object is still in the tree.

final GlobalKey _key = GlobalKey();

Size _getSize() {
  finalRenderBox box = _key.currentContext! .findRenderObject()!as RenderBox;
  return box.size;
}

Widget buildChild() =>  Padding(
  key: _key, //<--- 
  padding: const EdgeInsets.all(10),
  child: const Icon(
    Icons.camera_outlined,
    color: Colors.green,
    size: 50,),);Copy the code

Because of this feature, be careful about hiding animated components. Take CupertinoActivityIndicator, for example, it is drawn by CustomPaint components, which can maintain a constant movement of the animation controller, to trigger redrawing of the drawing board. The animation controller will be listened on RenderCustomPaint, triggering markNeedsPaint.

Through debugging can be found, even if the CupertinoActivityIndicator is hidden, but the animation will continue running, RenderCustomPaint listening animation change, also can trigger markNeedsPaint constantly, this is clearly not friendly. So we need to hide CupertinoActivityIndicator at the same time, turn off animation. So the question comes, CupertinoActivityIndicator components of animation is inside the component state of maintenance, how do we control, here don’t press the table first, in the context of behind TickerMode components were discussed.

Widget buildChild() =>  Padding(
  key: _key,
  padding: const EdgeInsets.all(10),
  child:CupertinoActivityIndicator(
    radius: 20,),);Copy the code

2. Source implementation of Offstage

1. Offstage source analysis

It inherits from SingleChildRenderObjectWidget means, the component needs to maintain a RenderObject object to create and update.

In the createRenderObject method, create the RenderOffstage with the offstage as the constructor input. In updateRenderObject, update the RenderOffstage object. That is, the component is shown/hidden in RenderAnimatedOpacity.


2. RenderOffstage source code

RenderOffstage, as a RenderObject, is responsible for layout and drawing. As you can see in the source code processing, when calculating width and height, when offstage is true, it returns 0, which is why it is not displayed on the interface.

In performLayout, if offstage is true, the child render object executes the layout. Note The child nodes are laid out even if they are hidden.


3. Click and Draw

HitTest is used for click processing, so you can see that if offstage is true it will not respond to a click. Paint is used to draw. If offstage is true, it is returned directly, and no painting is done. That is offstage is true, even if CupertinoActivityIndicator trigger markNeedsPaint, also won’t perform rendering object mapping operations.


Thank you for watching. See you tomorrow