如何在Flutter中以编程方式显示/隐藏小部件

在Android中,每个View子类都有一个setVisibility()方法,允许你修改View对象的可见性

有3个设置可见性的选项:

  • Visible:在布局中呈现可见的View
  • 不可见:隐藏View,但留下一个空白,相当于如果View是可见的
  • Gone:隐藏View,并将其完全从布局中移除。就好像它的heightwidth0dp

对于Flutter中的小部件,是否有与上述相同的东西?

快速参考: https://developer.android.com/reference/android/view/View.html#attr_android:visibility < / p >

307937 次浏览

更新:由于这个答案是写的,Visibility被引入,并提供了这个问题的最佳解决方案。


你可以使用Opacity0.0opacity:来绘制隐藏的元素,但仍然占用空间。

为了使它不占用空间,将其替换为空Container()

< p >编辑: 要将它包装在一个不透明度对象中,请执行以下操作:

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

开发者关于不透明度的快速教程:https://youtu.be/9hltevOHQBw

与问题协作,并显示一个用空Container()替换它的示例。

下面是一个例子:

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],
),
),
),
],
),
)
),
],
)
],
)
);
}
}

初学者也可以试试

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";
}
});
}
}

定义:

看不见的:小部件占用屏幕上的物理空间,但对用户不可见。这可以使用Visibility小部件来实现。

走了:小部件不占用任何物理空间,完全消失。这可以使用Visibilityifif-else条件来实现。

无形的例子:

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

例子:

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

使用if:

  • < p > 对于一个孩子:

    Column(
    children: <Widget>[
    Text('Good Morning'), // Always visible
    if (wishOnePerson) Text(' Mr ABC'), // Only visible if condition is true
    ],
    )
    
  • < p > 对于多个孩子:

    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:

  • < p > 对于一个孩子:

    Column(
    children: <Widget>[
    // Only one of them is visible based on 'isMorning' condition
    if (isMorning) Text('Good Morning')
    else Text ('Good Evening'),
    ],
    )
    
  • < p > 对于多个孩子:

    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现在有一个可见性小部件。要实现您自己的解决方案,请从下面的代码开始。


自己做一个小部件。

显示/隐藏

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);
}
}

显示/删除

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();
}
}

顺便问一下,有没有人对上面的小部件有更好的名字?

更多的阅读

  1. 文章关于如何创建可见性小部件。

Flutter现在包含一个能见度小部件,你应该使用它来显示/隐藏小部件。小部件还可以通过更改替换来在两个小部件之间切换。

这个小部件可以实现任何可见、不可见、消失等状态。

    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不占用物理空间且不可见,

如果属性offstage:false,它将占用物理空间和可见

Offstage(
offstage: true,
child: Text("Visible"),
),

颤振1.5省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已经强调的, 您可以使用Visibility小部件。但是,如果你想保持它的状态,例如,如果你想建立一个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)的新小部件封装任何小部件,这是你想让它不可见的小部件最左边的黄色灯

例如:假设你想让一行不可见:

  1. 单击灯并选择(用小部件包裹)
  2. 将小部件重命名为Visibility
  3. 添加可见属性并将其设置为false
  4. 新创建的小部件(可见性小部件)的子部件是指 你希望它是不可见的

              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

也许你可以像这样使用Navigator函数Navigator.of(context).pop();

恕我直言,在Flutter中不需要可见性属性或特殊的小部件,因为如果你不需要显示小部件,就不要将其添加到小部件树中或将其替换为空小部件:

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

我认为能见度小部件存在的原因是因为很多人问:)人们习惯了由某些属性控制的元素的可见性

有条件地添加/删除小部件

包含/排除一个小部件:

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的开发者:可能值得一提的是,你从来没有显示/隐藏小部件,你需要重绘UI,有或没有你需要的小部件:

< p > 👉声明式用户界面简介 < br > 👉State Management < br > 👉简单的应用状态管理 > < /晚餐

在Flutter中有很多不同的方法来实现这一点。在我解释它们之前,我将首先提供与android原生的“隐形”等效的快速解决方案。和“gone":

视图。看不见的:

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

视图。了:

Offstage(
child: ...
)

现在让我们比较一下这些方法和其他方法:

不透明度

这个小部件将不透明度(alpha)设置为您想要的任何值。将它设置为0.0比设置为0.1更不明显,所以希望这很容易理解。小部件仍将保持其大小和占用相同的空间,并保持每个状态,包括动画。由于它留下了一个间隙,用户仍然可以触摸或点击它。(顺便说一下,如果你不想让人们触摸一个看不见的按钮,你可以用IgnorePointer小部件包装它。)

后台

这个小部件隐藏了子小部件。你可以把它想象成把小部件“放在屏幕外面”。这样用户就看不到了。小部件仍然要经历flutter管道中的所有事情,直到它到达最终的“绘画”。阶段,它什么都不画。这意味着它将维护所有的状态和动画,但不会在屏幕上呈现任何东西。此外,它在布局时也不会占用任何空间,不会留下任何空隙,用户自然无法点击。

可见性

这个小部件结合了上述(以及更多)功能,方便您使用。它有诸如maintainStatemaintainAnimationmaintainSizemaintainInteractivity等参数。根据你设置这些属性的方式,它由以下决定:

  1. 如果你想保持状态,它会用OpacityOffstage来包装子对象,这取决于你是否也想保持大小。此外,除非你想要maintainInteractivity,否则它也会为你包装一个IgnorePointer,因为点击透明按钮有点奇怪。

  2. 如果你根本不想maintainState,它直接用SizedBox替换child,所以它完全消失了。你可以用replacement属性将空白的SizedBox更改为你想要的任何东西。

删除小部件

如果您不需要维护状态等,通常建议完全从树中删除小部件。例如,你可以使用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: () {},
),
],
),
),
),
],
),
]),
),
);
}
}
 

enter image description here