ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

flutter —— 布局原理与约束

2022-07-27 11:34:41  阅读:329  来源: 互联网

标签:size 约束 child 组件 原理 Row flutter constraints


一、布局模型

主要有两种布局模型:
① 基于 RenderBox 的盒模型布局。
② 基于 Sliver ( RenderSliver ) 按需加载列表布局。

两种布局方式在细节上略有差异,但大体流程相同,布局流程如下:

  1. 上层组件向下层组件传递约束(constraints)条件。
  2. 下层组件确定自己的大小,然后告诉上层组件。注意下层组件的大小必须符合父组件的约束。
  3. 上层组件确定下层组件相对于自身的偏移和确定自身的大小(大多数情况下会根据子组件的大小来确定自身的大小)。

二、盒模型的约束

基础约束对象

const BoxConstraints({
  this.minWidth = 0.0, //最小宽度
  this.maxWidth = double.infinity, //最大宽度
  this.minHeight = 0.0, //最小高度
  this.maxHeight = double.infinity //最大高度
})

多重约束

约束通常是收缩的,最小值以父级中最大的为准,最大值以父级中最小的为准。

ConstrainedBox(
  constraints: BoxConstraints(
    maxWidth: 30,
    maxHeight: 180,
  ),
  child: ConstrainedBox(
    constraints: BoxConstraints(maxWidth: 290, maxHeight: 30),
    child: SizedBox(
      width: 550,
      height: 550,
      child: redBox,
    ),
  ),
)

渲染的结果是 30,30 大小的正方形

SizedBox

严格约束

SizedBox(
  width: 80.0,
  height: 80.0,
  child: redBox
)

等价于

ConstrainedBox(
  constraints: BoxConstraints.tightFor(width: 80.0,height: 80.0),
  child: redBox, 
)

三、去除组件约束

假如有一个组件 A,它的子组件是B,B 的子组件是 C,则 C 必须遵守 B 的约束,同时 B 必须遵守 A 的约束,但是 A 的约束不会直接约束到 C,除非B将A对它自己的约束透传给了C。 利用这个原理,就可以实现一个这样的 B 组件:

① B 组件中在布局 C 时不约束C(可以为无限大)。
② C 根据自身真实的空间占用来确定自身的大小。
③ B 在遵守 A 的约束前提下结合子组件的大小确定自身大小。

而这个 B组件就是 UnconstrainedBox 组件,也就是说UnconstrainedBox 的子组件将不再受到约束,大小完全取决于自己。

将 UnconstrainedBox 换成 Center 或者 Align 也是可以的
需要注意,UnconstrainedBox 虽然在其子组件布局时可以取消约束(子组件可以为无限大),但是 UnconstrainedBox 自身是受其父组件约束的,所以当 UnconstrainedBox 随着其子组件变大后,如果UnconstrainedBox 的大小超过它父组件约束时,也会导致溢出报错。

四、约束原理

例1:自定义 center 组件

class CustomCenter extends SingleChildRenderObjectWidget {
  const CustomCenter({Key? key, required Widget child})
      : super(key: key, child: child);

  @override
  RenderObject createRenderObject(BuildContext context) {
    return RenderCustomCenter();
  }
}

class RenderCustomCenter extends RenderShiftedBox {
  RenderCustomCenter({RenderBox? child}) : super(child);

  @override
  void performLayout() {
    //1. 先对子组件进行layout,随后获取它的size
    child!.layout(
      constraints.loosen(), //将无限制的约束传递给子节点
      parentUsesSize: true, // 因为我们接下来要使用child的size,所以不能为false
    );
    //2.根据子组件的大小确定自身的大小
    size = constraints.constrain(Size(
      //如果父组件的宽度不限制,则使用子组件宽度,否则以父组件的最大宽度为准
      constraints.maxWidth == double.infinity
          ? child!.size.width
          : double.infinity,
      //如果父组件的高度不限制,则使用子组件高度,否则以父组件的最大高度为准
      constraints.maxHeight == double.infinity
          ? child!.size.height
          : double.infinity,
    ));

    // 3. 根据父节点子节点的大小,算出子节点在父节点中居中之后的偏移,然后将这个偏移保存在
    // 子节点的parentData中,在后续的绘制阶段,会用到。
    BoxParentData parentData = child!.parentData as BoxParentData;
    parentData.offset = ((size - child!.size) as Offset) / 2;
  }
}

例2:UnconstrainedBox 布局原理

  void performLayout() {
    final BoxConstraints constraints = this.constraints;
    final RenderBox? child = this.child;
    if (child != null) {
      final BoxConstraints childConstraints = constraintsTransform(constraints);
      assert(childConstraints != null);
      assert(childConstraints.isNormalized, '$childConstraints is not normalized');
      _childConstraints = childConstraints;
      // 将上层组件约束传递给下层组件,下组件确定自己的大小(并且使用子组件的大小)
      child.layout(childConstraints, parentUsesSize: true);
      // 上层组件通过 child.size 得到子组件大小,确定自己的大小(给 size 赋值即上报上层组件自己的大小)
      size = constraints.constrain(child.size);
      alignChild();
      final BoxParentData childParentData = child.parentData! as BoxParentData;
      _overflowContainerRect = Offset.zero & size;
      _overflowChildRect = childParentData.offset & child.size;
    } else {
      size = constraints.smallest;
      _overflowContainerRect = Rect.zero;
      _overflowChildRect = Rect.zero;
    }
    _isOverflowing = RelativeRect.fromRect(_overflowContainerRect, _overflowChildRect).hasInsets;
  }

注:此处未设置子组件的偏移量。完整代码位于 RenderConstraintsTransformBox 中。

例3:Row、Column 组件布局原理

对于子级

以 Row 组件为例,则水平方向不限,垂直方向尽可能大(但不能超出父级约束)

对于自身

1)纵轴方向

大小来自子级最大的那个,但不能超出父级约束

2)主轴方向

① 如果主轴不是无限大,且 mainAxisSize = MainAxisSize.max(Row,Column 默认此设置),则主轴方向大小为父级最大约束。

② 如果主轴是无限大,则主轴方向大小为 allocatedSize,即子组件在主轴方向大小的和。

举例1:

Row 行内布局

Container(
  constraints: BoxConstraints(
    maxWidth: 210,
    maxHeight: 220,
  ),
  color: Colors.green,
  child: Row(
    children: [
      Container(
        width: 301,
        height: 102,
        color: Colors.red,
      ),
      Container(
        width: 50,
        height: 50,
        color: Colors.blue,
      ),
    ],
  ),
)

对子级约束是 宽度不限,高度最大220

对自身大小是 宽度210,高度102

举例2:

Row 设置 主轴 min

同样,自身大小也是 50

Row(
  mainAxisSize: MainAxisSize.min,
  children: [
    Container(
      width: 50,
      height: 50,
      color: Colors.blue,
    ),
  ],
)

举例3:

Row,其父级约束不限

由于父级主轴方向是不限宽度,最终其自身宽度为 50

Container(
  constraints: BoxConstraints(
    maxWidth: 210,
    maxHeight: 220,
  ),
  color: Colors.green,
  child: UnconstrainedBox(
    child: Container(
      constraints: BoxConstraints(
        maxWidth: double.infinity,
      ),
      child: Row(
        children: [
          Container(
            width: 50,
            height: 50,
            color: Colors.blue,
          ),
        ],
      ),
    ),
  ),
)

举例4:

对于两个嵌套的 Row,里面那个 Row 自身的宽度也是 50

Container(
  constraints: BoxConstraints(
    maxWidth: 210,
    maxHeight: 220,
  ),
  color: Colors.green,
  child: Row(
    children: [
      Row(
        children: [
          Container(
            width: 50,
            height: 50,
            color: Colors.blue,
          ),
        ],
      ),
    ],
  ),
)

举例5:

两个嵌套的 Row 里面使用 Expanded

报错:RenderFlex children have non-zero flex but incoming width constraints are unbounded.

即主轴方向上无限大时,子组件的 flex 不能大于 0。Flexible 或 Expanded 默认 flex 为 1。

Container(
  width: 300,
  height: 300,
  color: Colors.green,
  child: Row(
    children: [
      Row(
        children: [
          Expanded(
            child: Container(
              width: 100,
              height: 100,
              color: Colors.red,
            ),
          ),
        ],
      )
    ],
  ),
)

参考文档


https://book.flutterchina.club/chapter14/layout.html#_14-4-1-单子组件布局示例-customcenter

标签:size,约束,child,组件,原理,Row,flutter,constraints
来源: https://www.cnblogs.com/lemos/p/16524032.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有