如何在 Flutter 中以编程方式 show/hide 小部件

How to show/hide widgets programmatically in Flutter

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

设置可见性有3个选项:

Flutter 中的 Widgets 是否有与上述等效的东西?

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

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


您可以使用 Opacity0.0opacity: 来绘制使元素隐藏但仍然占据 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,完全消失了。这可以使用 Visibilityifif-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();
  }
}

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

更多阅读

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

          ],
        )
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,您希望它不可见

示例:假设您想隐藏一行:

  1. 单击 lamp 并选择(用小部件包裹)
  2. 将小部件重命名为 Visibility
  3. 添加可见的 属性 并将其设置为 false
  4. 新创建的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,不留空隙,用户自然无法点击。

能见度

为了您的方便,此小部件结合了上述(以及更多)。它具有 maintainStatemaintainAnimationmaintainSizemaintainInteractivity 等参数。根据您设置这些属性的方式,它由以下内容决定:

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

  2. 如果你根本不想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: () {},
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ]),
      ),
    );
  }
}