flutter container约束分析

21 阅读4分钟

最近在看Flutter组件详解与实战书籍
flutter组件内部有个constraints约束的概念
首先一切皆组件包括 样式 布局 滚动 动画 约束 人机交互 触摸拖放
有了基础组件会层层封装成新的语法糖组件比如 flex 构成 Column Row 有多个组件构成的Continer container根据不同的参数包装不同的组件有外而内如下:

  1. ConstrainedBox
  2. LimitedBox
  3. Align
  4. Padding
  5. ColoredBox
  6. ClipPath
  7. DecoratedBox
  8. ConstrainedBox
  9. Padding
  10. Transform 可以参考web盒模型 里面属性有冲突自行选择比如color与decoration 这里width height 可以构成BoxConstraints
  11. child与BoxConstraints 空情况 渲染内部LimitedBox->ConstrainedBox
  12. 渲染外部ConstrainedBox
    Container({
    super.key,
    this.alignment,
    this.padding,
    this.color,
    this.decoration,
    this.foregroundDecoration,
    double? width,
    double? height,
    BoxConstraints? constraints,
    this.margin,
    this.transform,
    this.transformAlignment,
    this.child,
    this.clipBehavior = Clip.none,
  }) : assert(margin == null || margin.isNonNegative),
       assert(padding == null || padding.isNonNegative),
       assert(decoration == null || decoration.debugAssertIsValid()),
       assert(constraints == null || constraints.debugAssertIsValid()),
       assert(decoration != null || clipBehavior == Clip.none),
       assert(
         color == null || decoration == null,
         'Cannot provide both a color and a decoration\n'
         'To provide both, use "decoration: BoxDecoration(color: color)".',
       ),
       constraints =
           (width != null || height != null)
               ? constraints?.tighten(width: width, height: height) ??
                   BoxConstraints.tightFor(width: width, height: height)
               : constraints;
               
   @override
  Widget build(BuildContext context) {
    Widget? current = child;

    if (child == null && (constraints == null || !constraints!.isTight)) {
      current = LimitedBox(
        maxWidth: 0.0,
        maxHeight: 0.0,
        child: ConstrainedBox(constraints: const BoxConstraints.expand()),
      );
    } else if (alignment != null) {
      current = Align(alignment: alignment!, child: current);
    }

    final EdgeInsetsGeometry? effectivePadding = _paddingIncludingDecoration;
    if (effectivePadding != null) {
      current = Padding(padding: effectivePadding, child: current);
    }

    if (color != null) {
      current = ColoredBox(color: color!, child: current);
    }

    if (clipBehavior != Clip.none) {
      assert(decoration != null);
      current = ClipPath(
        clipper: _DecorationClipper(
          textDirection: Directionality.maybeOf(context),
          decoration: decoration!,
        ),
        clipBehavior: clipBehavior,
        child: current,
      );
    }

    if (decoration != null) {
      current = DecoratedBox(decoration: decoration!, child: current);
    }

    if (foregroundDecoration != null) {
      current = DecoratedBox(
        decoration: foregroundDecoration!,
        position: DecorationPosition.foreground,
        child: current,
      );
    }

    if (constraints != null) {
      current = ConstrainedBox(constraints: constraints!, child: current);
    }

    if (margin != null) {
      current = Padding(padding: margin!, child: current);
    }

    if (transform != null) {
      current = Transform(transform: transform!, alignment: transformAlignment, child: current);
    }

    return current!;
  }

image.png

现在来个demo分析书中提到情况 结构 column->container->container 在 Column 中,x 轴(横向、cross axis)对孩子通常是有界约束(bounded),不是无界。

  • 默认行为:Column 会把自身可用的最大宽度作为子组件的 maxWidth。
  • crossAxisAlignment.stretch:对子组件给紧约束(minWidth = maxWidth = 可用宽度,铺满)。
  • 其它对齐方式:给松约束(0..maxWidth),子组件可变窄但不能超过可用宽度。
  • 例外:只有当父级在横向本身就是无界(如某些横向滚动/未限宽场景)时,Column 的 x 轴才可能变成无界,这并非常见。
  • 易混点:无界更常发生在 Column 的主轴 y 轴(比如放进纵向 SingleChildScrollView),与 x 轴无关。 LimitedBox 的作用是:当父组件在某个方向上给的是“无界约束”(unbounded)时,只在该方向为子组件施加一个“最大尺寸”限制;若父组件已给出有界约束,则 LimitedBox 不起作用(直接透传)。

要点

  • 只在无界方向生效:如滚动方向里的子项(ListView/SingleChildScrollView 中的 children)、Row/Column 某些场景。
  • 常用来防止子组件在无界约束下无限扩张,给出上限。
  • 构造参数 maxWidth/maxHeight(默认是无穷大),一般需要显式设置为期望的上限。
  • 与其他约束组件对比:
  • ConstrainedBox:无论父约束是否无界,都应用限制。
  • SizedBox:给出紧约束(固定尺寸/最小=最大)。
  • LimitedBox:仅在父约束无界时应用最大限制,其他情况不干预。

image.png

  1. 无子组件
    1.1 y轴 无界约束 高度为0 x轴 有界约束 宽度为0 撑满
        Column(
            children: [
              Container(
                // width: 200,
                // height: 200,
                foregroundDecoration: BoxDecoration(
                  color: Colors.grey.withOpacity(0.5),
                  shape: BoxShape.circle,
                ),
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    colors: [Colors.black,Colors.grey]
                  ),
                  boxShadow: [BoxShadow(blurRadius: 10)]
                ),
                alignment: Alignment.center,
                // child: Container(
                //   color: Colors.orange,
                //   width: 50,
                //   height: 50,
                // ),
              )
            ],
          ),
    

image.png
1.2 y轴 有界约束 高度为200 x轴 有界约束 宽度为200

Column(
        children: [
          Container(
            width: 200,
            height: 200,
            foregroundDecoration: BoxDecoration(
              color: Colors.grey.withOpacity(0.5),
              shape: BoxShape.circle,
            ),
            decoration: BoxDecoration(
              gradient: LinearGradient(
                colors: [Colors.black,Colors.grey]
              ),
              boxShadow: [BoxShadow(blurRadius: 10)]
            ),
            alignment: Alignment.center,
            // child: Container(
            //   color: Colors.orange,
            //   width: 50,
            //   height: 50,
            // ),
          )
        ],
      ),

image.png
2. 有子元素
​ 2.1 无alignment属性
​   2.1.1 y轴 有界约束 高度为200 x轴 有界约束 宽度为200

Column(
        children: [
          Container(
            width: 200,
            height: 200,
            foregroundDecoration: BoxDecoration(
              color: Colors.grey.withOpacity(0.5),
              shape: BoxShape.circle,
            ),
            decoration: BoxDecoration(
              gradient: LinearGradient(
                colors: [Colors.black,Colors.grey]
              ),
              boxShadow: [BoxShadow(blurRadius: 10)]
            ),
            // alignment: Alignment.center,
            child: Container(
              color: Colors.orange,
              width: 50,
              height: 50,
            ),
          )
        ],
      ),

image.png
​   2.1.2 y轴 无界约束 高度为0 x轴 无界约束 宽度为0

Column(
        children: [
          Container(
            // width: 200,
            // height: 200,
            foregroundDecoration: BoxDecoration(
              color: Colors.grey.withOpacity(0.5),
              shape: BoxShape.circle,
            ),
            decoration: BoxDecoration(
              gradient: LinearGradient(
                colors: [Colors.black,Colors.grey]
              ),
              boxShadow: [BoxShadow(blurRadius: 10)]
            ),
            // alignment: Alignment.center,
            child: Container(
              color: Colors.orange,
              width: 50,
              height: 50,
            ),
          )
        ],
      ),

image.png
​ 2.2 无alignment属性
​   2.2.1 y轴 有界约束 高度为200 x轴 有界约束 宽度为200

 Column(
        children: [
          Container(
            width: 200,
            height: 200,
            foregroundDecoration: BoxDecoration(
              color: Colors.grey.withOpacity(0.5),
              shape: BoxShape.circle,
            ),
            decoration: BoxDecoration(
              gradient: LinearGradient(
                colors: [Colors.black,Colors.grey]
              ),
              boxShadow: [BoxShadow(blurRadius: 10)]
            ),
            alignment: Alignment.center,
            child: Container(
              color: Colors.orange,
              width: 50,
              height: 50,
            ),
          )
        ],
      )

image.png
​   2.2.2 y轴 无界约束 高度为0 x轴 无界约束 宽度为0

Column(
        children: [
          Container(
            // width: 200,
            // height: 200,
            foregroundDecoration: BoxDecoration(
              color: Colors.grey.withOpacity(0.5),
              shape: BoxShape.circle,
            ),
            decoration: BoxDecoration(
              gradient: LinearGradient(
                colors: [Colors.black,Colors.grey]
              ),
              boxShadow: [BoxShadow(blurRadius: 10)]
            ),
            alignment: Alignment.center,
            child: Container(
              color: Colors.orange,
              width: 50,
              height: 50,
            ),
          )
        ],
      ),

image.png

1.规律 判断有没有child 没有child 父有界约束走 BoxConstraints.expand() 有child 没有alignment 自身有界约束 子有界约束 有child 有alignment 自身有界约束 父有界约束