12、 Flutter Widgets 之 Stack,Positioned叠加组件

2,762 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情

层叠布局和Web中的绝对定位、Android中的Frame布局是相似的,子widget可以根据到父容器四个角的位置来确定本身的位置。绝对定位允许子widget堆叠(按照代码中声明的顺序)。Flutter中使用Stack和Positioned来实现绝对定位,Stack允许子widget堆叠,而Positioned可以给子widget定位(根据Stack的四个角)

Stack children:一个数组,里面可放多个Widget alignment:Alignment.topCenter//对齐方式,会对所有的子组件统一设置对齐,不灵活,一般不用

Positioned child:一个子组件 left,top,right,bottom,width,heigh://设置这些值会改变子组件的大小,位置(相对于最外层组件)

Stack

构造函数:

Stack({\
this.alignment = AlignmentDirectional.topStart,\
this.textDirection,\
this.fit = StackFit.loose,\
this.overflow = Overflow.clip,\
List<Widget> children = const <Widget>[],\
})

  • alignment:此参数决定如何去对齐没有定位(没有使用Positioned)或部分定位的子widget。所谓部分定位,在这里特指没有在某一个轴上定位:left、right为横轴,top、bottom为纵轴,只要包含某个轴上的一个定位属性就算在该轴上有定位。

  • textDirection:和Row、Wrap的textDirection功能一样,都用于决定alignment对齐的参考系即:textDirection的值为TextDirection.ltr,则alignment的start代表左,end代表右;textDirection的值为TextDirection.rtl,则alignment的start代表右,end代表左。

  • fit:此参数用于决定没有定位的子widget如何去适应Stack的大小。StackFit.loose表示使用子widget的大小,StackFit.expand表示扩伸到Stack的大小。

  • overflow:此属性决定如何显示超出Stack显示空间的子widget,值为Overflow.clip时,超出部分会被剪裁(隐藏),值为Overflow.visible 时则不会。

Stack组件可以将子组件叠加显示,根据子组件的顺利依次向上叠加,用法如下:

Stack(
      children: <Widget>[
        Container(
          height: 200,
          width: 200,
          color: Colors.red,
        ),
        Container(
          height: 170,
          width: 170,
          color: Colors.blue,
        ),
        Container(
          height: 140,
          width: 140,
          color: Colors.yellow,
        ),
        ElevatedButton(onPressed: (){}, child: Text('这个视图按钮好'))
      ],
    )

运行效果:

图片.png

从效果可以看出视图一次从左上方开始堆叠,子视图最下方的视图展示再最外层。

  • fit属性 Stack未定位的子组件大小由fit参数决定,默认值是StackFit.loose,表示子组件自己决定,StackFit.expand表示尽可能的大, fit三个可选值:
enum StackFit { 
//使用子Widget 自身的大小 loose,
//使子widget与stack大小一致 expand, 
//stack的父widget的布局大小约束无修改的传递给 Stack 的子Widget passthrough, 
}

用法如下:

Stack(
  fit: StackFit.expand,
  ...
)

Stack未定位的子组件的默认左上角对齐,通过alignment参数控制,用法如下:

Stack(
  alignment: Alignment.center,
  ...
)

运行效果:

图片.png

有没有注意到fitalignment参数控制的都是未定位的子组件,那什么样的组件叫做定位的子组件?使用Positioned包裹的子组件就是定位的子组件,用法如下:

Stack(
      fit: StackFit.loose,
      alignment: Alignment.center,
      children: <Widget>[
        Container(
          height: 200,
          width: 200,
          color: Colors.red,
        ),
        Container(
          height: 170,
          width: 170,
          color: Colors.blue,
        ),
        Container(
          height: 140,
          width: 140,
          color: Colors.yellow,
        ),
       Positioned(
         bottom: 0,
           right: 0,
           child:  ElevatedButton(onPressed: (){}, child: Text('这个视图按钮好')))
      ],
    )

运行效果:

图片.png

如果子组件超过Stack边界由clipBehavior控制,默认是裁剪,下面设置总是显示的用法:

Stack(
      clipBehavior: Clip.none,
      children: <Widget>[
        Container(
          height: 200,
          width: 200,
          color: Colors.red,
        ),
        Positioned(
          left: 100,
          top: 100,
          height: 150,
          width: 150,
          child: Container(
            color: Colors.green,
          ),
        )
      ],
    )

运行效果:

图片.png

IndexedStack

IndexedStack是Stack的子类,Stack是将所有的子组件叠加显示,而IndexedStack只显示指定的子组件,用法如下:

Column(
        children: [
          _buildView3(),
          Row(children: [
            ElevatedButton(onPressed: (){
              setState(() {
                _index=0;
              });
            }, child: Icon(Icons.fastfood)),
            ElevatedButton(onPressed: (){
              setState(() {
                _index=1;
              });
            }, child: Icon(Icons.cake)),
            ElevatedButton(onPressed: (){
              setState(() {
                _index=2;
              });
            }, child: Icon(Icons.local_cafe))
          ],)
        ],
      )
 
 
 
_buildView3(){
    return IndexedStack(
      index: _index,
      children: [
        Center(
          child: Container(
            height: 300,
            width: 300,
            color: Colors.yellow,
            child: Icon(Icons.fastfood,color: Colors.blue,),
          ),
        ),
 
        Center(
          child: Container(
            height: 300,
            width: 300,
            color: Colors.red,
            child: Icon(Icons.cake,color: Colors.blue,),
          ),
        ),
        Center(
          child: Container(
            height: 300,
            width: 300,
            color: Colors.blueGrey,
            child: Icon(Icons.local_cafe,color: Colors.blue,),
          ),
        ),
      ],
    );
  }

效果(点击切换_inex,只显示第index层视图):

图片.png

Positioned

Positioned用于定位Stack子组件,Positioned必须是Stack的子组件,基本用法如下:

Container(
      height: 300,
      width: 300,
      color: Colors.amberAccent,
      child: Stack(
        children: <Widget>[
          Positioned(
            left: 10,
            right: 10,
            top: 10,
            bottom: 10,
            child: Container(color: Colors.red),
          ),
        ],
      ),
    )

图片.png

总结:

  • top、bottom、left、right四种定位属性,分别表示距离上下左右的距离。

  • 用于Stack组件中。

  • left,rigit,with,3数只能设置其中2个,因为设置了其中2个,第三个已经确定了,同理top、bottom和height也只能设置其中2个。

Positioned提供便捷的构建方式,比如Positioned.fromRect、Positioned.fill等,这些便捷的构建方式万变不离其宗,只不过换了一种方式设置top、bottom、left、right四种定位属性。