前言:
这是我参与8月更文挑战的第 13 天,活动详情查看:8月更文挑战。为应掘金的八月更文挑战
,我准备在本月挑选 31
个以前没有介绍过的组件,进行全面分析和属性介绍。这些文章将来会作为 Flutter 组件集录
的重要素材。希望可以坚持下去,你的支持将是我最大的动力~
一、认识 Visibility 组件
前面介绍了 Offstage 组件可以控制 child
的显隐
,与它相比较的往往是 Visibility
组件。Offstage 源码中有对 Visibility
的一句介绍:它可以更高效地隐藏
组件(尽管不那么微妙)。
现在仔细想想,Opacity
、Offstage
和 Visibility
都可以控制组件的显示/隐藏
。那它们之间有什么样的区别和联系呢?通过本文,我将从 Visibility
源码中为你揭晓答案。
1.Visibility 基本信息
下面是 Visibility
组件类的定义
和 构造方法
,可以看出它继承自 StatelessWidget
。实例化时必须传入 child
组件,另外还有一堆选填的 bool
属性和一个 replacement
组件。
2.Visibility 的简单使用
下面是 Visibility
组件的默认效果,中间的图标是待显隐
组件,通过点击按钮切换现有状态。可以看出,直接使用 Visibility
时,组件隐藏时不会占位
。
class OffstageDemo extends StatefulWidget {
@override
_OffstageDemoState createState() => _OffstageDemoState();
}
class _OffstageDemoState extends State<OffstageDemo> {
bool _visible = true;
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
child: const Text('切换显隐'),
onPressed: _toggleVisible,
),
Visibility(
visible: _visible,
child: buildChild(),
),
Text('默认情况'),
],
);
}
void _toggleVisible() {
setState(() {
_visible = !_visible;
});
}
Widget buildChild() => const Padding(
padding: const EdgeInsets.all(10),
child: Icon(
Icons.camera_outlined,
color: Colors.green,
size: 50,
),
);
}
3.Visibility 的占位组件
Visibility
中有一个 replacement
的可选属性,类型是 Widget
,用于占位。
this.replacement = const SizedBox.shrink(),
final Widget replacement;
默认 replacement
是 SizedBox.shrink()
,即宽高为 0
的 SizedBox
。
我们可以通过 Flutter Inspector
查看树的详细信息,可以看出在隐藏时。渲染树中确实有通过 SizedBox
对象的渲染对象节点,且宽高为 0 。
下面是设置 replacement
的效果,可以看出在隐藏时,会显示 replacement
对应的组件。
Visibility(
visible: _visible,
child: buildChild(),
replacement: buildPlaceholder(),
),
Widget buildPlaceholder() => Container(
width: 50,
height: 50,
padding: const EdgeInsets.all(10),
child: Placeholder(),
);
4. 五个 maintainXXX
下面就剩下来五个 maintainXXX
的布尔属性。
我们先来看一下,构造中的断言处理:
先看一下 maintainSize
,表示隐藏时是否保持尺寸。默认为 false
。通过断言 2 可以看出:
maintainSize: true 时,必须 maintainAnimation: true ,否则断言失败。
通过断言 1 可以看出:
maintainAnimation: true 时,必须 maintainState: true ,否则断言失败。
也就是说,如果想要保持尺寸,必须 maintainAnimation
和 maintainState
。如下是 maintainSize
的效果,这样隐藏时,原尺寸区域不会消失。
Visibility(
visible: _visible,
child: buildChild(),
maintainSize: true,
maintainAnimation: true,
maintainState: true,
),
看一下结构,有一个透明度为 0
的 Opacity
组件,看懂的应该能会心一笑。
maintainInteractivity
的作用也非常简单,它可以控制在隐藏时是否响应事件。
另外 maintainAnimation
和 maintainState
作用是什么,就让源码来告诉你吧。
二、 Visibility 的源码实现
1. 尺寸和点击事件的保持
Visibility
组件作为一个 StatelessWidget
,就决定它本身并不掌握"核心科技"
,只是在 build
方法中组合使用其他组件而已。在源码 build
方法中,可以看出:
如果 maintainSize
为 true
,会通过 Opacity
组件进行包裹,根据 visible
控制透明度。 maintainInteractivity
为 false
, 会通过 IgnorePointer
组件进行包裹,忽略点击事件。
2. 保持状态和动画
如果 maintainState
为 true
,会通过 Offstage
组件进行包裹,根据 visible
控制是否显示,对于 Offstage 组件介绍,详见此文。如果 maintainAnimation
为 false
, 会通过 TickerMode
组件进行包裹,在隐藏时禁用子组件动画。 TickerMode 组件介绍,详见此文
3.为什么说 Visibility
是高效的?
如果五个 maintainXXX
是默认情况,那么 Visibility
组件直接通过 visible
属性决定返回组件,不需要通过 Opacity
或 Offstage
组件完成显隐功能。 另外,maintainState
和 maintainAnimation
必须同时为 true
,这也能给使用者一个保障,避免使用 Offstage
是忘记子组件禁止动画。
到这里可以看出,Visibility 组件
可以说是显隐功能的合集。能控制四个属性:是否接受事件
、是否保持尺寸
、是否保持状态
、是否停止动画
,分别由 IgnorePointer
、Opacity
、Offstage
和 TickerMode
实现。是一个上层的简单封装,目的是方便用户使用,尽可能少犯错误。所以如果想显隐组件,又不知道用什么好,Visibility
可以无脑翻牌。
那Visibility
的使用方式到这里就介绍完毕,那本文到这里就结束了,谢谢观看,明天见~