【布局 widget】Flutter Stack

当你想叠放一些组件的时候, stack 组件非常有用。相当于 web 中 的 relativeabsolute 定位产生的效果。

为了方便说明,约定 stack 有两种子组件,定位子组件和非定位子组件,区别就是是否被有定位属性的Positioned 包起来。

// 非定位组件
Stack( children:[ const Text('hello')]) 
// 非定位组件,Positioned 为没有定位属性,会被忽略。
Stack( children:[ const Positioned( child: Text('hello'))])
// 定位组件
Stack( children:[ const Positioned( left: 0, child: Text('hello'))])

Stack 组件的大小

如果父级传过来的是 tight 约束,那么 Stack 就不再考虑子组件的尺寸,Stack 的大小就是 tight 约束的大小。

如果 是 loose 约束,有两种情况:

  1. 如果 Stack 只有定位组件,那么 Stack 会倾向于最大。

  2. 如果 Stack 有非定位组件,那么它会尝试缩小自己,让自己能 wrap 所有非定位组件。

比如下面的 stack 会尽量缩小自己,Positioned 没有定位属性,也属于非定位组件。

Center(
      child: Stack(alignment: AlignmentDirectional.bottomCenter, children: [
        Positioned(
            child: Container(
              color: Colors.green,
          height: 100,
          width: 100,
        ))
      ]),
 )

在没有约束且只有定位组件的情况下,stack 会报错。比如下面的例子就会报错。

UnconstrainedBox(
    child: Stack(
          children: const [
            Positioned(left: 0, child: Text('hello')),
        ],
))

子组件绘制顺序

Stack(
 children: [
   Container(width: 160,height: 160,color: Colors.blue,),
   Container(width: 140,height: 140,color: Colors.red,),
   Container(width: 120,height: 120,color: Colors.green,),
],)

前面的子组件最先绘制,所以看起来在最底下。

如果把一个定位子组件排在第 4 位,那么它就会排在第 4 个绘制。在绘制顺序上定位组件和非定位组件没有差别。

子组件对齐方式

子组件默认的定位方式是 topLeft,可以通过 alignment 参数修改。

 Stack(
   alignment: Alignment.center,
   ...

如果你指定了alignment却没有效果,可能是因为 Stack 和子组件一样大

如果把 其中的 Containter 换成 Positioned(child:Container) 效果一样,同样受 alignment属性控制。因为 Positioned 没有定位属性。

子组件的大小

Stack 默认给非定位子组件的约束是 loose,fit: StackFit.loose,在允许范围内子组件可以自己决定大小。

Stack 也可以传给非定位子组件 tight 约束,fit: StackFit.expand 这时非定位子组件大小不能自己决定,而是直接充满整个 Stack。

fit 的可选值还有一个 StackFit.passthrough。这个时候, Stack 把父约束透传给子组件。

定位子组件的大小

定位子组件比较超然,不受fit 值的影响。

定位子组件是以 Stack 的左上角为原点计算 x,y值。

定位子组件的大小与 top,bottom,left,right,width,height 这几个值有关。

满足下面两条 定位子组件 获得 tight 约束

  1. 同时指定 top,bottom,或单独指定 height
  2. 同时指定 left,right,或单独指定 width

根据 top,bottom 可以得出 height = child.size.height-top-bottom

根据 left,right 可以得出 width = child.size.width-left-right

如果不满足上面两条,子组件就彻底放飞了,没有约束。

比如下面的蓝块因为只有bottom一个值,所以没有约束,除了下面,其它三面可以无限延伸。

Center(child: Stack(
        clipBehavior: Clip.none,
        alignment: Alignment.bottomCenter,
        children: [
          Container(
            color: Colors.green,
            width: 300,
            height: 100,
          ),
          Positioned(
            bottom: 0,
              child: Container(
                    color: Colors.blue,
                    height: 200,
                    width: 150,
                  )
                
              )
        ],
      ),
    )

IndexedStack

IndexedStack 是 Stack 的子类,和 Stack 唯一不同的是 IndexedStack 只绘制 index 属性指定的 child,而不是绘制所有 child. 其它都一样。

RenderIndexedStack 类 override paintStack 方法,从默认的绘制所有子组件,修改成只绘制当前组件。

 @override
  void paintStack(PaintingContext context, Offset offset) {
    if (firstChild == null || index == null) {
      return;
    }
    //找到 index 属性指定的 child 只绘制这一个
    final RenderBox child = _childAtIndex();
    final StackParentData childParentData = child.parentData! as StackParentData;
    context.paintChild(child, childParentData.offset + offset);
  }

参考

猜你喜欢

转载自blog.csdn.net/m0_55635384/article/details/128777713