312

In Android, every single View subclass has a setVisibility() method that allows you modify the visibility of a View object

There are 3 options of setting the visibility:

  • Visible: Renders the View visible inside the layout
  • Invisible: Hides the View, but leaves a gap that is equivalent to what the View would occupy if it were visible
  • Gone: Hides the View, and removes it entirely from the layout. It's as if its height and width were 0dp

Is there something equivalent to the above for Widgets in Flutter?

For a quick reference: https://developer.android.com/reference/android/view/View.html#attr_android:visibility

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
user3217522
  • 5,786
  • 7
  • 26
  • 34

16 Answers16

519

Definition:

Invisible: The widget takes up physical space on the screen but is not visible to user. This can be achieved using Visibility widget.

Gone: The widget doesn't take up any physical space and is completely gone. This can be achieved using Visibility, if or if-else condition.

Invisible example:

Visibility(
  child: Text("Invisible"),
  maintainSize: true, 
  maintainAnimation: true,
  maintainState: true,
  visible: false, 
),

Gone example:

Visibility(
  child: Text("Gone"),
  visible: false,
),

Using if:

  • For one child:

    Column(
      children: <Widget>[
        Text('Good Morning'), // Always visible
        if (wishOnePerson) Text(' Mr ABC'), // Only visible if condition is true
      ],
    ) 
    
  • For multiple children:

    Column(
      children: [
        Text('Good Morning'), // Always visible
        if (wishAll) ... [ // These children are only visible if condition is true
          Text('Mr ABC'),
          Text('Mr DEF'),
          Text('Mr XYZ'),
        ],
      ],
    )
    

Using if-else:

  • For one child:

    Column(
      children: <Widget>[
        // Only one of them is visible based on 'isMorning' condition
        if (isMorning) Text('Good Morning')
        else Text ('Good Evening'),
      ],
    ) 
    
  • For multiple children:

    Column(
      children: [
        // Only one of the children will be shown based on `beforeSunset` condition
        if (beforeSunset) ... [
          Text('Good morning'),
          Text('Good afternoon'),
        ] else ... [
          Text('Good evening'),
          Text('Good night'),
        ],
      ],
    )
    
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
  • how to use else condition here? – Quick learner Nov 06 '20 at 16:47
  • 3
    @Quicklearner You can use `if(show) Text('Showing) else Text('Not showing)` – CopsOnRoad Nov 06 '20 at 17:04
  • For someone who doesn't understand using if-else there is an example https://codeshare.io/qPLAPA – can çağlar kırıcı Sep 24 '21 at 09:06
  • If I am not wrong, I think Visibility widget still renders the widget, even if visible is set to false. So, if you have some FutureBuilder where you are requesting the data, it's better to use ternary operator. – Karanveer Singh Aug 24 '22 at 10:26
  • I just want to state that changing the visibility to 'false' does not make it gone. The widget is still counted as there, even if it has zero height and width. For example, if you have 2 text fields, and 2 "invisible" widgets in a row, then set the row to "spaceEvenly," you will see this issue first hand. I have tested Offstage as well. I suppose it's time to try the builder... – FoxDonut Jan 19 '23 at 06:30
145

UPDATE: Since this answer was written, Visibility was introduced and provides the best solution to this problem.


You can use Opacity with an opacity: of 0.0 to draw make an element hidden but still occupy space.

To make it not occupy space, replace it with an empty Container().

EDIT: To wrap it in an Opacity object, do the following:

            new Opacity(opacity: 0.0, child: new Padding(
              padding: const EdgeInsets.only(
                left: 16.0,
              ),
              child: new Icon(pencil, color: CupertinoColors.activeBlue),
            ))

Google Developers quick tutorial on Opacity: https://youtu.be/9hltevOHQBw

Collin Jackson
  • 110,240
  • 31
  • 221
  • 152
  • 3
    Thank you! Yeah that's not the cleanest way of doing it, but it will definitely do the purpose. Any chance of having a visibility functionality integrated with widgets in the future? – user3217522 Jun 12 '17 at 00:32
  • 7
    If the widget normally reacts to user input, be sure to wrap it in an [`IgnorePointer`](https://docs.flutter.io/flutter/widgets/IgnorePointer-class.html) as well, otherwise the user can still trigger it. – Duncan Jones Apr 20 '18 at 18:48
  • 1
    This is not ideal since the widget is still there and can respond to taps etc. See answer below using Visibility widget for best way to handle this. – Russell Zornes Jun 16 '19 at 12:57
  • As the comments upwards says, using opacity will render the Widget at the renderTree, with in some cases is not what you want. Using visibility widget is most recommended. – Isac Moura Jan 24 '20 at 17:09
  • 1
    Making a widget invisible and having opacity as0 are two different things. With an invisible widget, you can still interact with it, it's just invisible. The visibility widget allows you to remove the widget until needed. – UndercoverCoder Mar 29 '20 at 09:47
  • In my tests Visibility and Opacity behave differently. They both preserve the space occupied by the widget, but Opacity will also preserve the actual widget, while Visibility seems to be recreating it when visible, which in my case wasn't what I wanted, as it is expensive to recreate it, unless absolutely necessary. – Babao Mar 13 '21 at 14:55
78

To collaborate with the question and show an example of replacing it with an empty Container().

Here's the example below:

enter image description here

import "package:flutter/material.dart";

void main() {
  runApp(new ControlleApp());
}

class ControlleApp extends StatelessWidget { 
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: "My App",
      home: new HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  HomePageState createState() => new HomePageState();
}

class HomePageState extends State<HomePage> {
  bool visibilityTag = false;
  bool visibilityObs = false;

  void _changed(bool visibility, String field) {
    setState(() {
      if (field == "tag"){
        visibilityTag = visibility;
      }
      if (field == "obs"){
        visibilityObs = visibility;
      }
    });
  }

  @override
  Widget build(BuildContext context){
    return new Scaffold(
      appBar: new AppBar(backgroundColor: new Color(0xFF26C6DA)),
      body: new ListView(
        children: <Widget>[
          new Container(
            margin: new EdgeInsets.all(20.0),
            child: new FlutterLogo(size: 100.0, colors: Colors.blue),
          ),
          new Container(
            margin: new EdgeInsets.only(left: 16.0, right: 16.0),
            child: new Column(
              children: <Widget>[
                visibilityObs ? new Row(
                  crossAxisAlignment: CrossAxisAlignment.end,
                  children: <Widget>[
                    new Expanded(
                      flex: 11,
                      child: new TextField(
                        maxLines: 1,
                        style: Theme.of(context).textTheme.title,
                        decoration: new InputDecoration(
                          labelText: "Observation",
                          isDense: true
                        ),
                      ),
                    ),
                    new Expanded(
                      flex: 1,
                      child: new IconButton(
                        color: Colors.grey[400],
                        icon: const Icon(Icons.cancel, size: 22.0,),
                        onPressed: () {
                          _changed(false, "obs");
                        },
                      ),
                    ),
                  ],
                ) : new Container(),

                visibilityTag ? new Row(
                  crossAxisAlignment: CrossAxisAlignment.end,
                  children: <Widget>[
                    new Expanded(
                      flex: 11,
                      child: new TextField(
                        maxLines: 1,
                        style: Theme.of(context).textTheme.title,
                        decoration: new InputDecoration(
                          labelText: "Tags",
                          isDense: true
                        ),
                      ),
                    ),
                    new Expanded(
                      flex: 1,
                      child: new IconButton(
                        color: Colors.grey[400],
                        icon: const Icon(Icons.cancel, size: 22.0,),
                        onPressed: () {
                          _changed(false, "tag");
                        },
                      ),
                    ),
                  ],
                ) : new Container(),
              ],
            )
          ),
          new Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              new InkWell(
                onTap: () {
                  visibilityObs ? null : _changed(true, "obs");
                },
                child: new Container(
                  margin: new EdgeInsets.only(top: 16.0),
                  child: new Column(
                    children: <Widget>[
                      new Icon(Icons.comment, color: visibilityObs ? Colors.grey[400] : Colors.grey[600]),
                      new Container(
                        margin: const EdgeInsets.only(top: 8.0),
                        child: new Text(
                          "Observation",
                          style: new TextStyle(
                            fontSize: 12.0,
                            fontWeight: FontWeight.w400,
                            color: visibilityObs ? Colors.grey[400] : Colors.grey[600],
                          ),
                        ),
                      ),
                    ],
                  ),
                )
              ),
              new SizedBox(width: 24.0),
              new InkWell(
                onTap: () {
                  visibilityTag ? null : _changed(true, "tag");
                },
                child: new Container(
                  margin: new EdgeInsets.only(top: 16.0),
                  child: new Column(
                    children: <Widget>[
                      new Icon(Icons.local_offer, color: visibilityTag ? Colors.grey[400] : Colors.grey[600]),
                      new Container(
                        margin: const EdgeInsets.only(top: 8.0),
                        child: new Text(
                          "Tags",
                          style: new TextStyle(
                            fontSize: 12.0,
                            fontWeight: FontWeight.w400,
                            color: visibilityTag ? Colors.grey[400] : Colors.grey[600],
                          ),
                        ),
                      ),
                    ],
                  ),
                )
              ),
            ],
          )                    
        ],
      )
    );
  }
}
rafaelcb21
  • 12,422
  • 28
  • 62
  • 86
  • 9
    This should be the accepted answer. This is the correct implementation of "show/hide widgets programmatically" – Bishwajyoti Roy Jun 19 '18 at 17:04
  • Yes this definitely should be the accepted because this uses a foundation pillar of Flutter i.e setState()... otherwise how else are you going to go back and forth between Visible/InVisible in your Stateful widget!?. – Yo Apps Feb 04 '20 at 11:10
  • 1
    This answer is incomplete. Only takes care of the "gone" part. The point is that sometimes you just want to hide/show a widget, but not recreate it all the time, as that might be expensive. The other use case would be when you want to preserve the layout. Opacity or Visibility are what you need in this case. – Babao Mar 13 '21 at 14:46
  • This code is quite deprecated and has one error at the time of writing the comment. The code will work if change colors: to textColor: on line 45. use "child: new FlutterLogo(size: 100.0, textColor: Colors.blue),". The 4 other deprecation issues will not prevent the code from running at this stage. – Alexandre Jean Jun 09 '21 at 05:04
45

Flutter now contains a Visibility Widget that you should use to show/hide widgets. The widget can also be used to switch between 2 widgets by changing the replacement.

This widget can achieve any of the states visible, invisible, gone and a lot more.

    Visibility(
      visible: true //Default is true,
      child: Text('Ndini uya uya'),
      //maintainSize: bool. When true this is equivalent to invisible;
      //replacement: Widget. Defaults to Sizedbox.shrink, 0x0
    ),
Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56
31

Try the Offstage widget

if attribute offstage:true the not occupy the physical space and invisible,

if attribute offstage:false it will occupy the physical space and visible

Offstage(
   offstage: true,
   child: Text("Visible"),
),
AskNilesh
  • 67,701
  • 16
  • 123
  • 163
  • 2
    Note on this: the [Flutter docs](https://api.flutter.dev/flutter/widgets/Offstage-class.html) state, "Offstage can be used to measure the dimensions of a widget without bringing it on screen (yet). To hide a widget from view while it is not needed, prefer removing the widget from the tree entirely rather than keeping it alive in an Offstage subtree.". – lukeic Aug 15 '20 at 23:20
15

You can encapsulate any widget in your code with a new widget called (Visibility), this is from the yellow lamp at the very left side of the widget that you want it to be in-visible

example: say you want to make a row invisible:

  1. Click in the lamp and choose (Wrap with widget)
  2. Rename the widget to Visibility
  3. Add the visible property and set it to false
  4. The Child of the newly created widget (Visibility Widget) is the Widget that you want it to be invisible

              Visibility(
                  visible: false,
                  child: Row(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      SizedBox(
                        width: 10,
                      ),
                      Text("Search",
                        style: TextStyle(fontSize: 20
                        ),),
                    ],
                  ),
                ),
    

I hope it will help someone in the future

14
bool _visible = false;

 void _toggle() {
    setState(() {
      _visible = !_visible;
    });
  }

onPressed: _toggle,

Visibility(
            visible:_visible,
            child: new Container(
            child: new  Container(
              padding: EdgeInsets.fromLTRB(15.0, 0.0, 15.0, 10.0),
              child: new Material(
                elevation: 10.0,
                borderRadius: BorderRadius.circular(25.0),
                child: new ListTile(
                  leading: new Icon(Icons.search),
                  title: new TextField(
                    controller: controller,
                    decoration: new InputDecoration(
                        hintText: 'Search for brands and products', border: InputBorder.none,),
                    onChanged: onSearchTextChanged,
                  ),
                  trailing: new IconButton(icon: new Icon(Icons.cancel), onPressed: () {
                    controller.clear();
                    onSearchTextChanged('');
                  },),
                ),
              ),
            ),
          ),
          ),
Alan John
  • 767
  • 9
  • 15
12

Update

Flutter now has a Visibility widget. To implement your own solution start with the below code.


Make a widget yourself.

show/hide

class ShowWhen extends StatelessWidget {
  final Widget child;
  final bool condition;
  ShowWhen({this.child, this.condition});

  @override
  Widget build(BuildContext context) {
    return Opacity(opacity: this.condition ? 1.0 : 0.0, child: this.child);
  }
}

show/remove

class RenderWhen extends StatelessWidget {
  final Widget child;
  final bool condition;
  RenderWhen({this.child, this.show});

  @override
  Widget build(BuildContext context) {
    return this.condition ? this.child : Container();
  }
}

By the way, does any one have a better name for the widgets above?

More Reads

  1. Article on how to make a visibility widget.
Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56
Amsakanna
  • 12,254
  • 8
  • 46
  • 58
10

In flutter 1.5 and Dart 2.3 for visibility gone, You can set the visibility by using an if statement within the collection without having to make use of containers.

e.g

child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
              Text('This is text one'),
              if (_isVisible) Text('can be hidden or shown'), // no dummy container/ternary needed
              Text('This is another text'),
              RaisedButton(child: Text('show/hide'), onPressed: (){
                  setState(() {
                    _isVisible = !_isVisible; 
                  });
              },)

          ],
        )
nonybrighto
  • 8,752
  • 5
  • 41
  • 55
7

For beginner try this too.

class Visibility extends StatefulWidget {
  @override
  _VisibilityState createState() => _VisibilityState();
}

class _VisibilityState extends State<Visibility> {
  bool a = true;
  String mText = "Press to hide";

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: "Visibility",
      home: new Scaffold(
          body: new Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              new RaisedButton(
                onPressed: _visibilitymethod, child: new Text(mText),),
                a == true ? new Container(
                width: 300.0,
                height: 300.0,
                color: Colors.red,
              ) : new Container(),
            ],
          )
      ),
    );
  }

  void _visibilitymethod() {
    setState(() {
      if (a) {
        a = false;
        mText = "Press to show";
      } else {
        a = true;
        mText = "Press to hide";
      }
    });
  }
}
Hitesh Danidhariya
  • 709
  • 1
  • 7
  • 14
7

As already highlighted by @CopsOnRoad, you can use the Visibility widget. But, if you want to keep its state, for example, if you want to build a viewpager and make a certain button appear and disappear based on the page, you can do it this way

void checkVisibilityButton() {
  setState(() {
  isVisibileNextBtn = indexPage + 1 < pages.length;
  });
}    

 Stack(children: <Widget>[
      PageView.builder(
        itemCount: pages.length,
        onPageChanged: (index) {
          indexPage = index;
          checkVisibilityButton();
        },
        itemBuilder: (context, index) {
          return pages[index];
        },
        controller: controller,
      ),
      Container(
        alignment: Alignment.bottomCenter,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.end,
          children: <Widget>[
            Visibility(
              visible: isVisibileNextBtn,
              child: "your widget"
            )
          ],
        ),
      )
    ]))
AlexPad
  • 10,364
  • 3
  • 38
  • 48
  • 2
    `visible: isVisibileNextBtn == true ? true : false` could by simplified as `visible: isVisibileNextBtn` – ndelanou Jan 29 '21 at 08:56
6

IMHO, there's no need in visibility property or special widget for that in Flutter cause if you don't need a widget displayed - just don't add it to widget tree OR replace it with an empty widget:

  @override
  Widget build(BuildContext context) {
    return someFlag ? Text('Here I am') : SizedBox();
  }

I think the reason for Visibility widget existance is because so many people asked:) People are used to having visibility of elements controled by some property

Maxim Saplin
  • 4,115
  • 38
  • 29
4

There are quite a few different ways to achieve this in Flutter. Before I explain each one of them, I'll first provide quick solutions equivalent to the Android-native "invisible" and "gone":

View.INVISIBLE:

Opacity(
  opacity: 0.0,
  child: ...
)

View.GONE:

Offstage(
  child: ...
)

Now let's compare these and other methods:

Opacity

This widget 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 widget 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 final "painting" stage, where it doesn't paint anything at all. This means it will maintain all the state and animations, but just won't render anything on the screen. In addition, it also won't occupy any space during layout, leaving no gap behind, and naturally users cannot click it.

Visibility

This widget 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.

WSBT
  • 33,033
  • 18
  • 128
  • 133
2
class VisibilityExample extends StatefulWidget {
  const VisibilityExample({Key? key}) : super(key: key);

  @override
  _VisibilityExampleState createState() => _VisibilityExampleState();
}

class _VisibilityExampleState extends State<VisibilityExample> {
  bool visible = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Lines'),
      ),
      body: Container(
        color: Colors.black87,
        child: Stack(alignment: Alignment.bottomCenter, children: [
          ListView(
            shrinkWrap: true,
            children: [
              Container(
                height: 200,
              ),
              InkWell(
                onTap: () {},
                onHover: (value) {
                  print(value);
                  setState(() {
                    visible = !visible;
                  });
                },
                child: Visibility(
                  maintainSize: true,
                  maintainAnimation: true,
                  maintainState: true,
                  visible: visible,
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      IconButton(
                        color: Colors.white54,
                        icon: const Icon(
                          Icons.arrow_left_outlined,
                        ),
                        onPressed: () {},
                      ),
                      const SizedBox(
                        width: 5,
                      ),
                      IconButton(
                        color: Colors.white54,
                        icon: const Icon(
                          Icons.add_circle_outlined,
                        ),
                        onPressed: () {},
                      ),
                      const SizedBox(
                        width: 5,
                      ),
                      IconButton(
                        color: Colors.white54,
                        icon: const Icon(
                          Icons.remove_circle,
                        ),
                        onPressed: () {},
                      ),
                      const SizedBox(
                        width: 5,
                      ),
                      IconButton(
                        color: Colors.white54,
                        icon: const Icon(
                          Icons.arrow_right_outlined,
                        ),
                        onPressed: () {},
                      ),
                      const SizedBox(
                        width: 5,
                      ),
                      IconButton(
                        color: Colors.white54,
                        icon: const Icon(Icons.replay_circle_filled_outlined),
                        onPressed: () {},
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ]),
      ),
    );
  }
}
 

enter image description here

X PersPective
  • 161
  • 1
  • 4
1

Conditionally add/remove a widget

To include/exclude a widget:

if (this.isLuckyTime) TextButton(
 child: Text('I am feeling lucky')
)

In case you want to make the widget invisible but still keep its size then wrap it into <Visibility> and set maintainSize: true. If it's stateful and you need to keep it's state then also add maintainState: true.

Animated Widget fade in and fade out

To make the widget fade in and out smoothly you can use AnimatedOpacity.

AnimatedOpacity(
   opacity: this.isLuckyTime ? 1.0 : 0.0,
   duration: Duration(milliseconds: 500),
   child: Text('I am feeling lucky')
)

Specifically for devs coming from native android: it's probably worth mentioning that you never show/hide widgets, you redraw the UI with or without the widgets you need:

Introduction to declarative UI
State Management
Simple app state management

ccpizza
  • 28,968
  • 18
  • 162
  • 169
-3

Maybe you can use the Navigator function like this Navigator.of(context).pop();

Onkar Nirhali
  • 216
  • 2
  • 8