如何在 Flutter 中以编程方式 show/hide 小部件
How to show/hide widgets programmatically in Flutter
在 Android 中,每个 View
子类都有一个 setVisibility()
方法,允许您修改 View
对象的可见性
设置可见性有3个选项:
- 可见:使
View
在布局内可见
- 不可见:隐藏
View
,但留下一个空隙,相当于 View
可见时所占据的空隙
- 消失:隐藏
View
,并将其从布局中完全移除。就好像它的 height
和 width
是 0dp
Flutter 中的 Widgets 是否有与上述等效的东西?
快速参考:
https://developer.android.com/reference/android/view/View.html#attr_android:visibility
更新:由于写了这个答案,Visibility
被引入并提供了这个问题的最佳解决方案。
您可以使用 Opacity
和 0.0
的 opacity:
来绘制使元素隐藏但仍然占据 space。
为了不占用space,将其替换为空的Container()
。
编辑:
要将其包裹在不透明度对象中,请执行以下操作:
new Opacity(opacity: 0.0, child: new Padding(
padding: const EdgeInsets.only(
left: 16.0,
),
child: new Icon(pencil, color: CupertinoColors.activeBlue),
))
Google 不透明度开发人员快速教程:https://youtu.be/9hltevOHQBw
与问题协作并展示将其替换为空 Container()
的示例。
下面是示例:
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],
),
),
),
],
),
)
),
],
)
],
)
);
}
}
初学者也可以试试这个。
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";
}
});
}
}
一种解决方案是将小部件颜色 属性 设置为 Colors.transparent。例如:
IconButton(
icon: Image.asset("myImage.png",
color: Colors.transparent,
),
onPressed: () {},
),
定义:
不可见:小部件在屏幕上占据物理空间space,但用户不可见。这可以使用 Visibility
小部件来实现。
消失:小部件不占用任何物理空间space,完全消失了。这可以使用 Visibility
、if
或 if-else
条件来实现。
看不见的例子:
Visibility(
child: Text("Invisible"),
maintainSize: true,
maintainAnimation: true,
maintainState: true,
visible: false,
),
消失的例子:
Visibility(
child: Text("Gone"),
visible: false,
),
使用if
:
一个child:
Column(
children: <Widget>[
Text('Good Morning'), // Always visible
if (wishOnePerson) Text(' Mr ABC'), // Only visible if condition is true
],
)
对于多个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
:
一个child:
Column(
children: <Widget>[
// Only one of them is visible based on 'isMorning' condition
if (isMorning) Text('Good Morning')
else Text ('Good Evening'),
],
)
对于多个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'),
],
],
)
更新
Flutter 现在有一个 Visibility 小部件。要实施您自己的解决方案,请从以下代码开始。
自己制作一个小部件。
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();
}
}
顺便问一下,有没有人为上面的小部件起一个更好的名字?
更多阅读
- Article 关于如何制作可见性小部件。
Flutter 现在包含一个 Visibility Widget,您应该将其用于 show/hide 小部件。该小部件还可以用于通过更改替换来在 2 个小部件之间切换。
这个小部件可以实现可见、不可见、消失等任何状态。
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
),
试试 Offstage
小部件
如果属性offstage:true
不占用物理space且不可见,
如果属性offstage:false
它将占用物理space和可见
Offstage(
offstage: true,
child: Text("Visible"),
),
在 flutter 1.5 和 Dart 2.3 中,可见性消失了,您可以通过在集合中使用 if 语句来设置可见性,而无需使用容器。
例如
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;
});
},)
],
)
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('');
},),
),
),
),
),
),
正如@CopsOnRoad 已经强调的那样,
您可以使用可见性小部件。但是,如果你想保持它的状态,比如你想构建一个viewpager,让某个按钮根据页面出现和消失,你可以这样做
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"
)
],
),
)
]))
您可以使用一个名为 (Visibility) 的新小部件在您的代码中封装任何小部件,这是来自小部件最左侧的黄色 lamp,您希望它不可见
示例:假设您想隐藏一行:
- 单击 lamp 并选择(用小部件包裹)
- 将小部件重命名为 Visibility
- 添加可见的 属性 并将其设置为 false
新创建的Widget(Visibility Widget)的Child就是那个Widget
你想让它不可见
Visibility(
visible: false,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
width: 10,
),
Text("Search",
style: TextStyle(fontSize: 20
),),
],
),
),
希望对以后的人有所帮助
也许你可以像这样使用导航器功能Navigator.of(context).pop();
恕我直言,在 Flutter 中不需要可见性 属性 或特殊小部件,因为如果您不需要显示小部件 - 只需不要将其添加到小部件树或将其替换为空的小部件:
@override
Widget build(BuildContext context) {
return someFlag ? Text('Here I am') : SizedBox();
}
我认为 Visibility 小部件存在的原因是因为很多人问:) 人们习惯于让元素的可见性由某些 属性
有条件地add/remove一个小部件
到 include/exclude 一个小部件:
if (this.isLuckyTime) TextButton(
child: Text('I am feeling lucky')
)
如果您想使小部件不可见但仍保持其大小,则将其包装到 <Visibility>
中并设置 maintainSize: true
。如果它是有状态的并且您需要保持它的状态,那么还要添加 maintainState: true
.
动画小部件淡入淡出
要使小部件平滑地淡入淡出,您可以使用 AnimatedOpacity。
AnimatedOpacity(
opacity: this.isLuckyTime ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Text('I am feeling lucky')
)
特别适用于来自原生 android 的开发人员:可能值得一提的是,您从来没有 show/hide 小部件,您重绘 UI 有或没有小部件需要:
Introduction to declarative UI
State Management
Simple app state management
在 Flutter 中有很多不同的方法可以实现这一点。在我解释它们中的每一个之前,我将首先提供等同于 Android-native“不可见”和“消失”的快速解决方案:
View.INVISIBLE:
Opacity(
opacity: 0.0,
child: ...
)
View.GONE:
Offstage(
child: ...
)
现在让我们比较这些方法和其他方法:
不透明度
此小部件可将不透明度 (alpha) 设置为您想要的任何值。将其设置为 0.0
只是比将其设置为 0.1
稍微不那么明显,所以希望这很容易理解。小部件仍将保持其大小并占据相同的 space,并保持每个状态,包括动画。由于它在后面留下了一个空隙,用户仍然可以触摸它或点击它。 (顺便说一句,如果你不想让人们触摸一个不可见的按钮,你可以用一个 IgnorePointer
小部件包装它。)
台下
此小部件隐藏了 child 小部件。您可以将其想象为将小部件“置于屏幕之外”,这样用户就不会看到它。小部件仍然会经历 flutter 管道中的所有内容,直到它到达最后的“绘画”阶段,它根本不绘制任何东西。这意味着它将保持所有状态和动画,但不会在屏幕上呈现任何内容。另外,它在布局的时候也不会占用任何空间space,不留空隙,用户自然无法点击。
能见度
为了您的方便,此小部件结合了上述(以及更多)。它具有 maintainState
、maintainAnimation
、maintainSize
、maintainInteractivity
等参数。根据您设置这些属性的方式,它由以下内容决定:
如果你想保持状态,它会用 Opacity
或 Offstage
包裹 child,这取决于你是否也想保持大小。此外,除非你想maintainInteractivity
,否则它还会为你包装一个IgnorePointer
,因为点击透明按钮有点奇怪。
如果你根本不想maintainState
,它直接用SizedBox
替换child
所以它完全没有了。您可以将空白 SizedBox
更改为您想要的任何内容,使用 replacement
属性.
正在删除小部件
如果您不需要维护状态等,通常建议将小部件从树中完全移除。例如,您可以使用 if (condition)
来决定是否在列表中包含一个小部件,或者使用 condition ? child : SizedBox()
直接将其替换为 SizedBox
。这避免了不必要的计算,对性能来说是最好的。
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: () {},
),
],
),
),
),
],
),
]),
),
);
}
}
在 Android 中,每个 View
子类都有一个 setVisibility()
方法,允许您修改 View
对象的可见性
设置可见性有3个选项:
- 可见:使
View
在布局内可见 - 不可见:隐藏
View
,但留下一个空隙,相当于View
可见时所占据的空隙 - 消失:隐藏
View
,并将其从布局中完全移除。就好像它的height
和width
是0dp
Flutter 中的 Widgets 是否有与上述等效的东西?
快速参考: https://developer.android.com/reference/android/view/View.html#attr_android:visibility
更新:由于写了这个答案,Visibility
被引入并提供了这个问题的最佳解决方案。
您可以使用 Opacity
和 0.0
的 opacity:
来绘制使元素隐藏但仍然占据 space。
为了不占用space,将其替换为空的Container()
。
编辑: 要将其包裹在不透明度对象中,请执行以下操作:
new Opacity(opacity: 0.0, child: new Padding(
padding: const EdgeInsets.only(
left: 16.0,
),
child: new Icon(pencil, color: CupertinoColors.activeBlue),
))
Google 不透明度开发人员快速教程:https://youtu.be/9hltevOHQBw
与问题协作并展示将其替换为空 Container()
的示例。
下面是示例:
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],
),
),
),
],
),
)
),
],
)
],
)
);
}
}
初学者也可以试试这个。
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";
}
});
}
}
一种解决方案是将小部件颜色 属性 设置为 Colors.transparent。例如:
IconButton(
icon: Image.asset("myImage.png",
color: Colors.transparent,
),
onPressed: () {},
),
定义:
不可见:小部件在屏幕上占据物理空间space,但用户不可见。这可以使用 Visibility
小部件来实现。
消失:小部件不占用任何物理空间space,完全消失了。这可以使用 Visibility
、if
或 if-else
条件来实现。
看不见的例子:
Visibility(
child: Text("Invisible"),
maintainSize: true,
maintainAnimation: true,
maintainState: true,
visible: false,
),
消失的例子:
Visibility(
child: Text("Gone"),
visible: false,
),
使用if
:
一个child:
Column( children: <Widget>[ Text('Good Morning'), // Always visible if (wishOnePerson) Text(' Mr ABC'), // Only visible if condition is true ], )
对于多个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
:
一个child:
Column( children: <Widget>[ // Only one of them is visible based on 'isMorning' condition if (isMorning) Text('Good Morning') else Text ('Good Evening'), ], )
对于多个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'), ], ], )
更新
Flutter 现在有一个 Visibility 小部件。要实施您自己的解决方案,请从以下代码开始。
自己制作一个小部件。
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();
}
}
顺便问一下,有没有人为上面的小部件起一个更好的名字?
更多阅读
- Article 关于如何制作可见性小部件。
Flutter 现在包含一个 Visibility Widget,您应该将其用于 show/hide 小部件。该小部件还可以用于通过更改替换来在 2 个小部件之间切换。
这个小部件可以实现可见、不可见、消失等任何状态。
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
),
试试 Offstage
小部件
如果属性offstage:true
不占用物理space且不可见,
如果属性offstage:false
它将占用物理space和可见
Offstage(
offstage: true,
child: Text("Visible"),
),
在 flutter 1.5 和 Dart 2.3 中,可见性消失了,您可以通过在集合中使用 if 语句来设置可见性,而无需使用容器。
例如
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;
});
},)
],
)
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('');
},),
),
),
),
),
),
正如@CopsOnRoad 已经强调的那样, 您可以使用可见性小部件。但是,如果你想保持它的状态,比如你想构建一个viewpager,让某个按钮根据页面出现和消失,你可以这样做
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"
)
],
),
)
]))
您可以使用一个名为 (Visibility) 的新小部件在您的代码中封装任何小部件,这是来自小部件最左侧的黄色 lamp,您希望它不可见
示例:假设您想隐藏一行:
- 单击 lamp 并选择(用小部件包裹)
- 将小部件重命名为 Visibility
- 添加可见的 属性 并将其设置为 false
新创建的Widget(Visibility Widget)的Child就是那个Widget 你想让它不可见
Visibility( visible: false, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ SizedBox( width: 10, ), Text("Search", style: TextStyle(fontSize: 20 ),), ], ), ),
希望对以后的人有所帮助
也许你可以像这样使用导航器功能Navigator.of(context).pop();
恕我直言,在 Flutter 中不需要可见性 属性 或特殊小部件,因为如果您不需要显示小部件 - 只需不要将其添加到小部件树或将其替换为空的小部件:
@override
Widget build(BuildContext context) {
return someFlag ? Text('Here I am') : SizedBox();
}
我认为 Visibility 小部件存在的原因是因为很多人问:) 人们习惯于让元素的可见性由某些 属性
有条件地add/remove一个小部件
到 include/exclude 一个小部件:
if (this.isLuckyTime) TextButton(
child: Text('I am feeling lucky')
)
如果您想使小部件不可见但仍保持其大小,则将其包装到 <Visibility>
中并设置 maintainSize: true
。如果它是有状态的并且您需要保持它的状态,那么还要添加 maintainState: true
.
动画小部件淡入淡出
要使小部件平滑地淡入淡出,您可以使用 AnimatedOpacity。
AnimatedOpacity(
opacity: this.isLuckyTime ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
child: Text('I am feeling lucky')
)
特别适用于来自原生 android 的开发人员:可能值得一提的是,您从来没有 show/hide 小部件,您重绘 UI 有或没有小部件需要:
Introduction to declarative UI
State Management
Simple app state management
在 Flutter 中有很多不同的方法可以实现这一点。在我解释它们中的每一个之前,我将首先提供等同于 Android-native“不可见”和“消失”的快速解决方案:
View.INVISIBLE:
Opacity(
opacity: 0.0,
child: ...
)
View.GONE:
Offstage(
child: ...
)
现在让我们比较这些方法和其他方法:
不透明度
此小部件可将不透明度 (alpha) 设置为您想要的任何值。将其设置为 0.0
只是比将其设置为 0.1
稍微不那么明显,所以希望这很容易理解。小部件仍将保持其大小并占据相同的 space,并保持每个状态,包括动画。由于它在后面留下了一个空隙,用户仍然可以触摸它或点击它。 (顺便说一句,如果你不想让人们触摸一个不可见的按钮,你可以用一个 IgnorePointer
小部件包装它。)
台下
此小部件隐藏了 child 小部件。您可以将其想象为将小部件“置于屏幕之外”,这样用户就不会看到它。小部件仍然会经历 flutter 管道中的所有内容,直到它到达最后的“绘画”阶段,它根本不绘制任何东西。这意味着它将保持所有状态和动画,但不会在屏幕上呈现任何内容。另外,它在布局的时候也不会占用任何空间space,不留空隙,用户自然无法点击。
能见度
为了您的方便,此小部件结合了上述(以及更多)。它具有 maintainState
、maintainAnimation
、maintainSize
、maintainInteractivity
等参数。根据您设置这些属性的方式,它由以下内容决定:
如果你想保持状态,它会用
Opacity
或Offstage
包裹 child,这取决于你是否也想保持大小。此外,除非你想maintainInteractivity
,否则它还会为你包装一个IgnorePointer
,因为点击透明按钮有点奇怪。如果你根本不想
maintainState
,它直接用SizedBox
替换child
所以它完全没有了。您可以将空白SizedBox
更改为您想要的任何内容,使用replacement
属性.
正在删除小部件
如果您不需要维护状态等,通常建议将小部件从树中完全移除。例如,您可以使用 if (condition)
来决定是否在列表中包含一个小部件,或者使用 condition ? child : SizedBox()
直接将其替换为 SizedBox
。这避免了不必要的计算,对性能来说是最好的。
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: () {},
),
],
),
),
),
],
),
]),
),
);
}
}