flutter column和row常见异常解析及处理

1,004 阅读3分钟

column和row这两者的混合使用会经常导致[RenderFlex children have non-zero flex but incoming height constraints are unbounded]问题。

起因:

「column里面套column」、「row里面套row」,都会导致上面的问题。原因是column和row都会给自己的孩子「在主方向上设置一个类似null或者0的弹性因子,并且主方向上无限制

分析:

column源码注释:

/// Layout for a [Column] proceeds in six steps:
///
/// 1. Layout each child a null or zero flex factor (e.g., those that are not
///    [Expanded]) with unbounded vertical constraints and the incoming
///    horizontal constraints. If the [crossAxisAlignment] is
///    [CrossAxisAlignment.stretch], instead use tight horizontal constraints
///    that match the incoming max width.
/// 2. Divide the remaining vertical space among the children with non-zero
///    flex factors (e.g., those that are [Expanded]) according to their flex
///    factor. For example, a child with a flex factor of 2.0 will receive twice
///    the amount of vertical space as a child with a flex factor of 1.0.
/// 3. Layout each of the remaining children with the same horizontal
///    constraints as in step 1, but instead of using unbounded vertical
///    constraints, use vertical constraints based on the amount of space
///    allocated in step 2. Children with [Flexible.fit] properties that are
///    [FlexFit.tight] are given tight constraints (i.e., forced to fill the
///    allocated space), and children with [Flexible.fit] properties that are
///    [FlexFit.loose] are given loose constraints (i.e., not forced to fill the
///    allocated space).
/// 4. The width of the [Column] is the maximum width of the children (which
///    will always satisfy the incoming horizontal constraints).
/// 5. The height of the [Column] is determined by the [mainAxisSize] property.
///    If the [mainAxisSize] property is [MainAxisSize.max], then the height of
///    the [Column] is the max height of the incoming constraints. If the
///    [mainAxisSize] property is [MainAxisSize.min], then the height of the
///    [Column] is the sum of heights of the children (subject to the incoming
///    constraints).
/// 6. Determine the position for each child according to the
///    [mainAxisAlignment] and the [crossAxisAlignment]. For example, if the
///    [mainAxisAlignment] is [MainAxisAlignment.spaceBetween], any vertical
///    space that has not been allocated to children is divided evenly and
///    placed between the children.

第一步:对每个孩子设置一个竖直方向上null或者0的弹性因子(Expanded除外)且竖直方向上没有限制

第二步:将剩下的空间(孩子中有一些能确定自己大小的组件,如Text,能明确自己的大小,会成为第一批获得空间的组件,所以第二步中,描述为剩下的空间),给孩子中的Expanded们根据他们设置的flex分配。(执行完第二步之后,所有能确定自己大小的组件(如Text、固定高度的Container),以及使用Expanded的组件,都能明确自己的竖直方向的约束)

第三四五六步对本次的问题没有影响,不讨论。

可以看到,除了固定高度的组件以及Expanded组件,其他组件没有都是没办法限制高度的。因为Column本身的高度是有限制的,如果它的孩子高度没有限制,那是渲染不出来的。

异常例子

比如说:

Column(children:[
	Text("a widget that can determine height and width"),
	Column(children:[
		Expanded(child:Container(color:Colors.blue),),
	]),
]);

原因:因为第一层column会给它的所有孩子设置0的弹性,所以第二层的column是不知道自己的约束的,最后来到Expanded,这时候Expanded向自己的父节点要求撑满,轮到第二层的column懵逼了。它心里逼逼赖赖:我的父节点没给我约束,我的子节点又要求撑满,左右为难!直接抛出错误!

解决方法1:

如果第二层column在没有获得父组件给的约束时,自己的孩子又都是能确定大小的话,是不会报错的:

Column(children: [
      Text("a widget that can determine height and width"),
      Column(
        children: [
          Text("one"),
          Text("Two"),
          Container(
            height: 50,
            child: Column(children: [
              Expanded(
                child: Container(color: Colors.blue),
              ),
            ]),
          ),
        ],
      ),
    ]);

解决方法2:

如果第二层column外面裹上一层Expanded,那就可以在第二步里面获得剩余的空间,即获得约束,这样在第二层Column的孩子要撑满的时候,就可以给他提供确切的大小。

Column(children:[
	Text("a widget that can determine height and width"),
	Expanded(child:	
		Column(children:[
				Expanded(child:Container(
					color:Colors.blue),),
				Text("Two"),
			]),
	),
]);

参考:stackoverflow.com/questions/5…

同样的情况适用于row。