How to show/hide widgets programmatically in Flutter
FlutterDartFlutter LayoutFlutter WidgetFlutter Problem Overview
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 theView
would occupy if it were visible - Gone: Hides the
View
, and removes it entirely from the layout. It's as if itsheight
andwidth
were0dp
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
Flutter Solutions
Solution 1 - Flutter
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,
),
if
:
Using -
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'), ], ], )
if-else
:
Using -
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'), ], ], )
Solution 2 - Flutter
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
Solution 3 - Flutter
To collaborate with the question and show an example of replacing it with an empty Container()
.
Here's the example below:
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],
),
),
),
],
),
)
),
],
)
],
)
);
}
}
Solution 4 - Flutter
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
),
Solution 5 - Flutter
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"),
),
Solution 6 - Flutter
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:
-
Click in the lamp and choose (Wrap with widget)
-
Rename the widget to Visibility
-
Add the visible property and set it to false
-
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
Solution 7 - Flutter
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('');
},),
),
),
),
),
),
Solution 8 - Flutter
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
- Article on how to make a visibility widget.
Solution 9 - Flutter
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;
});
},)
],
)
Solution 10 - Flutter
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";
}
});
}
}
Solution 11 - Flutter
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"
)
],
),
)
]))
Solution 12 - Flutter
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
Solution 13 - Flutter
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:
-
if you want to maintain state, it will either wrap the child with an
Opacity
or with anOffstage
, depends on whether you also want to maintain size. Further, unless you want tomaintainInteractivity
, it will also wrap anIgnorePointer
for you, because clicking on a transparent button is kinda weird. -
if you don't want to
maintainState
at all, it directly replaces thechild
with aSizedBox
so it's completely gone. You can change the blankSizedBox
to anything you want, with thereplacement
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 14 - Flutter
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
Solution 15 - Flutter
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: () {},
),
],
),
),
),
],
),
]),
),
);
}
}
Solution 16 - Flutter
Maybe you can use the Navigator function like this Navigator.of(context).pop();
Solution 17 - Flutter
One solution is to set tis widget color property to Colors.transparent. For instance:
IconButton(
icon: Image.asset("myImage.png",
color: Colors.transparent,
),
onPressed: () {},
),