Reputation: 6294
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:
View
visible inside the layoutView
, but leaves a gap that is equivalent to what the View
would occupy if it were visibleView
, 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
Upvotes: 349
Views: 398241
Reputation: 191
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: () {},
),
],
),
),
),
],
),
]),
),
);
}
}
Upvotes: 3
Reputation: 268334
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.
Visibility(
child: Text("Invisible"),
maintainSize: true,
maintainAnimation: true,
maintainState: true,
visible: false,
),
Visibility(
child: Text("Gone"),
visible: false,
),
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'),
],
],
)
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'),
],
],
)
Upvotes: 581
Reputation: 976
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
),
Upvotes: 50
Reputation: 12954
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
Upvotes: 14
Reputation: 36373
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 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.
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.
Upvotes: 4
Reputation: 31801
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
.
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
Upvotes: 1
Reputation: 4652
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
Upvotes: 6
Reputation: 10879
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"
)
],
),
)
]))
Upvotes: 8
Reputation: 236
Maybe you can use the Navigator function like this Navigator.of(context).pop();
Upvotes: -3
Reputation: 581
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:
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
Upvotes: 15
Reputation:
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"),
),
Upvotes: 33
Reputation: 116828
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
Upvotes: 151
Reputation: 777
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('');
},),
),
),
),
),
),
Upvotes: 15
Reputation: 9593
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;
});
},)
],
)
Upvotes: 10
Reputation: 759
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";
}
});
}
}
Upvotes: 8
Reputation: 13304
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],
),
),
),
],
),
)
),
],
)
],
)
);
}
}
Upvotes: 82