'Visibility vs Offstage vs Opacity, which is best in hiding a child from widget tree?

From this thread I found that there are many ways of hiding a widget from the tree hierarchy. Like using:

Visibility:

Visibility(
  visible: false,
  child: Foo(),
);

Offstage:

Offstage(
  offstage: true,
  child: Foo(),
);

Opacity:

Opacity(
  opacity: 0,
  child: Foo(),
);

if condition:

if (visible) {
  child
}

and what not.

Which one of them is preferred (I know Visibility is the one, by why Visibility), what's the difference between if condition and Visibility, how do I measure the performance. Opacity docs mentions:

Opacity is more efficient than adding and removing the child widget from the tree on demand.

What does that mean, was it for if condition? If yes, where does if stand then among the four?



Solution 1:[1]

Opacity

This one sets the opacity (alpha) to anything you want. Setting it to 0.0 is just slightly less visible than setting it to 0.1, so hopefully that's easy to understand. The widget will still maintain its size and occupy the same space, and maintain every state, including animations. Since it leaves a gap behind, users can still touch it or click it. (BTW, if you don't want people to touch an invisible button, you can wrap it with an IgnorePointer widget.)

Offstage

This one hides the child widget. You can imagine it as putting the widget "outside of the screen" so users won't see it. The widget still goes through everything in the flutter pipeline, until it arrives at the rendering stage, where it doesn't layout or paint anything at all. This means it will maintain all the state and animations, but just won't occupy any space during "layout stage", and won't draw anything during "paint stage". Because it leaves no gap behind, naturally users cannot click it.

Visibility

This one combines the above (and more) for your convenience. It has parameters like maintainState, maintainAnimation, maintainSize, maintainInteractivity etc. Depending on how you set those properties, it decides from the following:

  1. if you want to maintain state, it will either wrap the child with an Opacity or with an Offstage, depends on whether you also want to maintain size. Further, unless you want to maintainInteractivity, it will also wrap an IgnorePointer for you, because clicking on a transparent button is kinda weird.

  2. if you don't want to maintainState at all, it directly replaces the child with a SizedBox so it's completely gone. You can change the blank SizedBox to anything you want, with the replacement property.

Removing Widget

If you don't need to maintain states and etc, it's usually recommended to completely remove the widget from the tree. For example, you can use if (condition) to decide whether to include a widget in a list, or use condition ? child : SizedBox() to replace it with a SizedBox directly. This avoid unnecessary calculations and is the best for performance.

Solution 2:[2]

I think the above answer is very useful and clear. About using condition way, I introduce 2 ways very useful:

condition ? child : SizedBox() or if (condition) child if you using dart ver at least 2.3.0

See more https://github.com/flutter/flutter/issues/3783

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Quyen Anh Nguyen