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

322

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

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

  • Visible:在布局中呈现可见的View
  • Invisible:隐藏View,但保留与View等效的间隙,就像View是可见的一样
  • Gone:隐藏View并完全从布局中移除。就好像它的heightwidth0dp 一样。

在Flutter中是否有类似上述方式可以设置小部件的可见性?

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

16个回答

533

定义:

Invisible:小部件在屏幕上占据物理空间但对用户不可见。这可以通过使用Visibility小部件实现。

Gone:小部件不占据任何物理空间并完全消失。这可以通过使用Visibilityifif-else条件实现。

Invisible示例:

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

已消失的示例:

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

使用 if 语句:

  • 对于一个孩子:

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

    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:

  • 对于一个孩子:

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

  • 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'),
        ],
      ],
    )
    

    如何在这里使用else条件? - Quick learner
    3
    你可以使用 if(show) Text('Showing') else Text('Not showing') - CopsOnRoad
    对于不理解如何使用if-else的人,这里有一个例子 https://codeshare.io/qPLAPA - can çağlar kırıcı
    如果我没错的话,我认为Visibility小部件即使visible设置为false,它仍然会呈现小部件。因此,如果您有一些FutureBuilder正在请求数据,则最好使用三元运算符。 - Karanveer Singh
    我只想说,将可见性更改为“false”并不意味着它已经消失了。即使它的高度和宽度为零,小部件仍然被计算在内。例如,如果您有2个文本字段和2个“不可见”小部件排成一行,然后将该行设置为“spaceEvenly”,您将首先看到这个问题。我也测试过Offstage。我想现在是尝试builder的时候了... - FoxDonut
    你每次使用maintainSize都需要将maintainStatemaintainAnimation设置为true吗?感觉这样做真的很不必要。 - undefined

    147

    更新:自回答编写以来,引入了Visibility,提供了解决这个问题的最佳方法。


    您可以使用Opacity,并将opacity:设置为0.0,以使元素隐藏但仍占用空间。

    要使其不占用空间,请将其替换为空的Container()

    编辑: 要将其包装在Opacity对象中,请执行以下操作:

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

    Google Developers快速教程:Opacity(不透明度)https://youtu.be/9hltevOHQBw


    3
    谢谢!是的,这不是最干净的方法,但绝对能实现目的。未来是否有可能将可见性功能与小部件集成在一起? - user3217522
    7
    如果该小部件通常会对用户的输入做出反应,请确保将其包装在 IgnorePointer 中,否则用户仍然可以触发它。 - Duncan Jones
    1
    这并不理想,因为小部件仍然存在,并且可以响应轻拍等操作。请参见下面使用可见性小部件的答案,以获得最佳处理方式。 - Russell Zornes
    正如上面的评论所说,使用不透明度会在渲染树中呈现小部件,在某些情况下这并不是您想要的。建议使用可见性小部件。 - Isac Moura
    1
    使小部件不可见和将不透明度设置为0是两个不同的概念。使用不可见小部件,您仍然可以与其进行交互,只是它不可见而已。可见性小部件允许您在需要时删除小部件。 - UndercoverCoder
    在我的测试中,Visibility和Opacity的行为不同。它们都保留了小部件占用的空间,但是Opacity还将保留实际的小部件,而Visibility似乎在可见时重新创建它,这在我的情况下并不是我想要的,因为重新创建它是很昂贵的,除非绝对必要。 - Babao

    80

    为了合作解决问题并展示一个将其替换为空的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],
                              ),
                            ),
                          ),
                        ],
                      ),
                    )
                  ),
                ],
              )                    
            ],
          )
        );
      }
    }
    

    9
    这应该是被采纳的答案。这是“以编程方式显示/隐藏小部件”的正确实现。 - Bishwajyoti Roy
    是的,这绝对应该被接受,因为它使用了Flutter的基础支柱——setState()...否则你怎么能在你的Stateful widget中来回切换Visible/InVisible呢!? - Yo Apps
    1
    这个答案不完整,只涉及了“消失”的部分。关键是有时候你只想隐藏/显示一个小部件,而不是每次都重新创建它,因为那可能很昂贵。另一个用例是当你想保留布局时。在这种情况下,你需要的是透明度或可见性。 - Babao
    这段代码已经相当过时,在编写评论时存在一个错误。如果在第45行将“colors:”更改为“textColor:”,则代码将正常工作。使用“child:new FlutterLogo(size:100.0,textColor:Colors.blue)”。另外4个弃用问题目前不会阻止代码运行。 - Alexandre Jean

    47

    Flutter现在包含一个Visibility Widget,您应该使用它来显示/隐藏小部件。该小部件还可用于通过更改replacement在2个小部件之间切换。

    此小部件可以实现任何状态的visible、invisible、gone等等。

        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
        ),
    

    32

    尝试使用Offstage小部件

    如果属性offstage:true,则不占用物理空间且不可见,

    如果属性offstage:false,则会占用物理空间并可见。

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

    2
    注意:Flutter文档指出,“Offstage可以用于测量小部件的尺寸,而不必将其带到屏幕上(但)。为了在不需要小部件时隐藏它,最好将小部件从树中完全删除,而不是在Offstage子树中保持其存在。”。 - lukeic

    15

    您可以使用名为“Visibility”的新小部件封装代码中的任何小部件,这是来自您希望它不可见的小部件的极左侧的黄色灯泡。

    例如:假设您想使一行不可见:

    1. 点击灯泡并选择(使用小部件包装)
    2. 将小部件重命名为Visibility
    3. 添加visible属性并将其设置为false
    4. 新创建的小部件(Visibility Widget)的子级是您希望它不可见的小部件

              Visibility(
                  visible: false,
                  child: Row(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      SizedBox(
                        width: 10,
                      ),
                      Text("Search",
                        style: TextStyle(fontSize: 20
                        ),),
                    ],
                  ),
                ),
    
    我希望这将有助于未来的某个人


    14

    更新

    Flutter现在有一个Visibility小部件。要实现自己的解决方案,请从以下代码开始。


    制作一个自定义的小部件。

    显示/隐藏

    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. 文章:如何创建一个可见性小部件。

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

    正是我所需要的,感谢您提供的其他支持代码。 - Noor Hossain

    10

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

    那比之前的Flutter/Dart版本可选项好多了。谢谢! - Hammer

    8

    对于初学者,也可以尝试这个。

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

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接