Flutter 隐藏/显示某widget组件、切换显示状态

Thursday, Jun 25, 2020 | 2 minute read | Updated at Thursday, Jun 25, 2020

在进行Flutter开发时, 我们需要对某个widget进行显示与隐藏状态的切换. 这个需求还是蛮常见的. 本篇文章就来记录下Flutter如何隐藏/显示某widget组件、切换显示状态.

效果

有图有真相, 先来看下我们要实现的最终效果:

Flutter-隐藏显示某widget组件-06

隐藏Widget

Flutter 隐藏显示某widget组件-08

隐藏且不影响布局

需具备的条件

  • 你已经掌握Flutter基础

本篇文章环境:

[epcl_box type=“information”]老规矩, 速度快的同学直接去看核心代码![/epcl_box]

实战开始

准备工作

创建一个Flutter项目, 替换文件./lib/main.dart为:

`import 'package:flutter/material.dart';  void main() => runApp(MyApp());  class MyApp extends StatelessWidget {   @override   Widget build(BuildContext context) {     return MaterialApp(       title: 'Show Hide widget',       theme: ThemeData(         primarySwatch: Colors.blue,       ),       home: MyHomePage(title: 'Show & Hide Widget'),     );   } }  class MyHomePage extends StatefulWidget {   MyHomePage({Key key, this.title}) : super(key: key);   final String title;    @override   _MyHomePageState createState() => _MyHomePageState(); }  class _MyHomePageState extends State {   // 存储"是否显示"状态   bool _isShow = true;    @override   Widget build(BuildContext context) {     return Scaffold(       appBar: AppBar(         title: Text(widget.title),       ),       body: Center(         child: Column(           mainAxisAlignment: MainAxisAlignment.center,           children: [             Text('一个文本Widget', style: Theme.of(context).textTheme.headline3),             RaisedButton(               child: Text(                 _isShow ? "隐藏" : "显示",                 style: Theme.of(context).textTheme.headline5,               ),               onPressed: () {                 setState(() {                   _isShow = !_isShow;                 });               },             )           ],         ),       ),     );   } }`

我们创建了布尔类型的实例变量_isShow, 用于保存widget的显示状态.

可以看到我们在界面内分别添加了一个TextRaisedButton. 若_isShow的值为true, Text的值为"隐藏". 反之则为"显示".

每次点击按钮, RaisedButton的onPress将会反转_isShow的值.

💡 代码解析

🟢 运行项目, 可以看到界面有一行文字和一个按钮:

Flutter-隐藏显示某widget组件-01

目标: 通过点击"隐藏"/“显示"按钮来切换上方文本的显示状态.

方案一: 直接判断

替换:

`Text('一个文本Widget', style: Theme.of(context).textTheme.headline3),`

为:

`_isShow     ? Text('一个文本Widget',         style: Theme.of(context).textTheme.headline3)     : Container(),`

我们运用了Dart的三目运算. 当_isShow的值为true, 返回文本. 反之返回一个空的Container.

💡 代码解析

Flutter-隐藏显示某widget组件-03

🟢 运行项目, 查看效果:

Flutter-隐藏显示某widget组件-02

直接判断

[epcl_box type=“success”]轻松搞定! 最容易理解的一种方案![/epcl_box]

速度快的同学可能已经举一反三了. 该思路是不是有多种方案? 答案是肯定的:

变体: 使用if

替换整个Text为:

`if (_isShow)   Text('一个文本Widget', style: Theme.of(context).textTheme.headline3),`

代码截图

非常简单, 我们在Text前加上了if (_isShow). 直接可以实现效果.

💡 代码解析

[epcl_box type=“notice”]但是为什么不作为教程的首选呢? 因为该方法会使IDE报错, 并且需要flutter的最小版本大于2.2.2. 贸然提升flutter的最小版本是会出问题的![/epcl_box]

该思路虽然简单, 但也有着自己的缺点. 比如隐藏后影响排版、代码不简洁等等.

方案二: 使用Visibility

我们使用Flutter自带的Visibility组件. 替换Text组件的代码为:

`Visibility(   child: Text(     '一个文本Widget',     style: Theme.of(context).textTheme.headline3,   ),   visible: _isShow, ),`

代码截图

🟢 运行项目, 查看效果:

Flutter 隐藏显示某widget组件-06

使用Visibility

有同学问, 如果我想隐藏后不影响排版怎么办? 很简单! 在Visibility添加属性:

`Visibility(   child: Text(     '一个文本Widget',     style: Theme.of(context).textTheme.headline3,   ),   maintainSize: true,   maintainAnimation: true,   maintainState: true,   visible: _isShow, ),`

代码截图

Visibility新增的3个属性值决定了其child是否使其占位、效果及数据绑定生效. 当maintainSize为true时, 无论child显示或隐藏, 其占位都不会改变. 从而达到不影响排版的目的.

💡 代码解析

🟢 运行项目. 阿航这里直接给出前后的对比图:

Flutter-隐藏显示某widget组件-06

maintainSize 为 false

Flutter 隐藏显示某widget组件-08

maintainSize 为 true

通过按钮的位置可以看出, maintainSize为true后, 按钮位置没有发生变化, 也就是排版未被影响.

[epcl_box type=“success”]成功搞定! 并且比其他方式更优雅! 支持的功能更多![/epcl_box]

其他方案

还有其他能达成同样效果的解决方案, 比如Opacity、Offstage、TickerMode、ExcludeSemantics、IgnorePointer等. 但是这些方案都不如上述的解决方案那么优雅. 这里就不再赘述.

核心代码

下面的_isShow变量是一个定义好的布尔类型变量, 决定是否显示.

方案一:

三目:

`_isShow ? Text('一个文本Widget',     style: Theme.of(context).textTheme.headline3) : Container(),`

if方式(需要改变flutter版本, 慎用):

`if (_isShow) Text('一个文本Widget', style: Theme.of(context).textTheme.headline3),`

方案二:

maintainSize为true时, 显示/隐藏不会影响排版.

`Visibility(   child: Text(     '一个文本Widget',     style: Theme.of(context).textTheme.headline3,   ),   maintainSize: true,   maintainAnimation: true,   maintainState: true,   visible: _isShow, ),`

感谢