【Flutter 组件集录】Flexible、Expanded 和 Spacer

2,214 阅读5分钟
前言:

这是我参与8月更文挑战的第 19 天,活动详情查看:8月更文挑战。为应掘金的八月更文挑战,我准备在本月挑选 31 个以前没有介绍过的组件,进行全面分析和属性介绍。这些文章将来会作为 Flutter 组件集录 的重要素材。希望可以坚持下去,你的支持将是我最大的动力~

本系列组件文章列表
1.NotificationListener2.Dismissible3.Switch
4.Scrollbar5.ClipPath6.CupertinoActivityIndicator
7.Opacity8.FadeTransition9. AnimatedOpacity
10. FadeInImage11. Offstage12. TickerMode
13. Visibility14. Padding15. AnimatedContainer
16.CircleAvatar17.PhysicalShape18.Divider
19.Flexible、Expanded 和 Spacer 20.Card

一、 认识 Flexible 组件

从源码中可以看出 Flexible 组件只能用于 RowColumnFlex 组件中。我们知道 RowColumn 的本质也是 Flex 组件,在很久之前写过 Flex 的使用文章。今天来看一下 Flex 组件的御用周边组件。Flexible的作用是:使子组件可以灵活地填充主轴的可用空间


1.Flexible 基本信息

Flexible 继承自 ParentDataWidget<FlexParentData> ,这个类型的父组件可能大家都没见过,毕竟我们很少自定义 ParentDataWidget 类型的组件。Flexible 构造中必须传入 child 组件。另外还有两个参数:int 型的 flex,和 FlexFit 枚举型的 fit

可以说类的定义还是比较简单的,下面一起看一下该组件的使用,及两个属性的作用。


2.Flexible 的使用

我们用如下的 Row 组件进行测试,外框是 Row 组件的区域,目前里面只有一个头像,占位为深蓝色区域。前面提到:Flexible可使子组件可以灵活地填充主轴中的可用空间。那么这里的可以空间就是 Row 除深蓝色区域。

class FlexibleDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
        width: 200,
        height: 54,
        color: Colors.grey.withAlpha(11),
        child: Row(
            children:[
              buildHead(),
            ],
      ),
    );
  }

  Widget buildHead(){
    return Padding(
      padding: const EdgeInsets.all( 8.0),
      child: Image.asset('assets/images/icon_head.png',width: 40,height: 40,),
    );
  }
}

现在做个小测试,在 Row 里添加一个蓝色的 Container ,那么会如何显示。Container 组件的占位又会是怎样呢?

Row(
    children:[
      buildHead(),
      Container(
        color: Colors.blueAccent,
      ),
    ]
)

可以看出在为 54RowContainer 默认的约束为 [w(0,0) - h(0,54)] 。再加上 Container 在父区域内延展的特性,尺寸为 Size(0,54),也就是说它不可见。

当然,我们可以通过指定宽度来修改区域约束,让 Container 显示出来。但有个问题:如何让 Container 填充剩余的空间呢?

虽然我们可以通过计算剩余尺寸来设置 Container的宽,但是这个计算过程比较麻烦,特别是 Row 里子组件非常多或不固定,用算的自然比较费劲。其实这些 Flutter 内部已经帮你做了,并暴露一个 Flexible 组件来给你用。我们只需要简单地套一个 Flexible 即可。

Row(
    children:[
      buildHead(),
      Flexible( //<--- 使用 Flexible
        child: Container(
          color: Colors.blueAccent,
        ),
      ),
    ],
  )

可以看出加上 Flexible 组件后,约束变成了 [w(0,144) - h(0,54)] ,其中 144 是框架内部帮我们计算出来的。我们只需要使用 Flexible 组件,不必考虑计算的细节,是不是很妙。


3.Flexible 的 flex 属性作用

如果只有一个子组件套 Flexible ,那么 flex 属性设成什么都是一样的效果。当有多个 Flexible 组件时,会根据 flex瓜分剩余空间,如下是 蓝 3 红 1 的效果。

Row(
    children:[
      buildHead(),
      Flexible(
        flex:3,
        child: Container(
          color: Colors.blueAccent,
        ),
      ),
      Flexible(
        flex: 1,
        child: Container(
          color: Colors.red,
        ),
      )
    ],
),

也就是蓝色占据剩余宽度 (3/(3+1)) 的百分比,剩余宽是 144 ,从树中可以看出,第一个 Container 的宽确实是 108 。这样多个 Flexible 组件时,根据 flex 属性我们可以确定该组件在剩余空间的占比。


4.Flexible 的 fit 属性作用

fit 是一个 FlexFit 型的枚举,只有两个元素 tightloss ,所以并不是很难。默认下是 loss

enum FlexFit {
  tight,
  loose,
}

tightloss 有什么作用呢?我们通过之前的例子再看一下:如果 Flexible 包裹的子组件有固定的尺寸,默认情况下 loss 是无法使其区域延展的,甚至 Flexible 本身的尺寸也不会扩展。

Row(
    children:[
      buildHead(),
      Flexible(
        fit: FlexFit.loose,
        child: Container(
          width: 40,
          color: Colors.blueAccent,
        ),
      ),
    ],
),

当为 tight 时,Flexible会强制 延展。这就是两者最大的区别。

Row(
    children:[
      buildHead(),
      Flexible(
        fit: FlexFit.tight,
        child: Container(
          width: 40,
          color: Colors.blueAccent,
        ),
      ),
    ],
),

可能 Container 自身的延展性上面的例子体现并不明显。可以通过一个固定尺寸的 Icon 来说明一下:如下左侧是 loose ,右侧是 tight 。只要记住 tight 会强制延展自身区域即可。


二、Expanded 组件的实现

Expanded 的实现非常非常简单,下面是它的全部代码。它就是继承自 Flexible 组件,将 fit 值固定为 tight 而已,所以说 Expanded 就是一个强制延展的 Flexible 组件。

两者在使用上并没有什么区别,由于 Flexible 可以设置 fit 值,所以用途要比 Expanded 广泛。而强制延展的场景使用 Expanded 组件语义更好,而且简单一点。


三、Spacer 组件的实现

Spacer 的实现也非常简单,下面是它的全部代码。它是一个 StatelessWidget ,内部依赖 Spacer 组件实现功能,特点是:它不能设置子组件,本身作为空白占位使用。

总的来说 SpacerExpanded 的功能都基于 Flexible 组件实现。了解了 Flexible 组件的使用,就可以一通百通。那本文到这里就结束了,谢谢观看,明天见~