flutter Container設置固定的寬和高后為什么不生效?

1. 為什么Container設置width和height不生效?

下面這段代碼,Container設置了(width: 100, height: 100),但渲染出來是填充滿屏幕的,這是為什么?

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(width: 100, height: 100, color: Colors.red);
  }
}

可以從Container的初始化和build方法看下具體原因:

Container({
  double? width,
  double? height,
  ...
}) : 
     constraints =
      (width != null || height != null)
        ? constraints?.tighten(width: width, height: height)
          ?? BoxConstraints.tightFor(width: width, height: height)
        : constraints,
     super(key: key);
     
@override
Widget build(BuildContext context) {
  Widget? current = child;

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

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

  return current!;
}

從Container初始化可以看出constraints是一個緊約束,寬高是100。


image.png

通過build方法生成ColoredBox和ConstrainedBox,其對應的渲染類為_RenderColoredBox和RenderConstrainedBox。其對應關系如下:

image.png

flutter約束有下面特點:


image.png

可以理解RenderConstrainedBox的約束是從父節點向下傳遞,而大小是從_RenderColoredBox向上傳遞。

constraints是怎么來的?

constraints是通過RenderView.performLayout傳遞逐級傳遞下來的,此時的constraints是一個緊約束,寬高就是屏幕的高度。


image.png

_additionalConstraints是RenderConstrainedBox初始化傳遞進來的約束,也就是開始設置的(100,100)

_additionalConstraints.enforce(constraints)是決定了當前size的大小:

//BoxConstraints
BoxConstraints enforce(BoxConstraints constraints) {
  return BoxConstraints(
    minWidth: minWidth.clamp(constraints.minWidth, constraints.maxWidth),
    maxWidth: maxWidth.clamp(constraints.minWidth, constraints.maxWidth),
    minHeight: minHeight.clamp(constraints.minHeight, constraints.maxHeight),
    maxHeight: maxHeight.clamp(constraints.minHeight, constraints.maxHeight),
  );
}

_additionalConstraints是Container的約束(100,100),constraints是屏幕傳遞下來的寬度和高度(414,896)。_additionalConstraints.enforce(constraints)最后產生的結果就是屏幕的高度和寬度。而這個約束繼續傳遞給_RenderColoredBox進行繪制顏色,這也解釋了為什么Container設置了寬高不生效的原因。

2. 修改上面代碼,再想想結果是什么?

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(width: 100, height: 100, color: Colors.red),
    );
  }
}

直接看下Center繼承自Align,其對應的RenderObject是RenderPositionedBox,而performLayout方法做了什么?

@override
void performLayout() {
  ...
  if (child != null) {
    child!.layout(constraints.loosen(), parentUsesSize: true);
    ...
  } else {
    ...
  }

child.layout傳遞的是constraints.loosen(),

BoxConstraints(
  minWidth: 0.0,
  maxWidth: maxWidth,
  minHeight: 0.0,
  maxHeight: maxHeight,
)

maxWidth和maxHeight是double.infinity,也就是(100,100).enforce(constraints)返回是(100,100)。這就很好的解釋了為什么Center包裹之后child就生效了。

如果了解約束的原理,下面的兩個題應該很容易得出結果:

ConstrainedBox(
  constraints: const BoxConstraints(
    minWidth: 70,
    minHeight: 70,
    maxWidth: 150,
    maxHeight: 150,
  ),
  child: Container(color: Colors.red, width: 10, height: 10),
)

Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      minWidth: 70,
      minHeight: 70,
      maxWidth: 150,
      maxHeight: 150,
    ),
    child: Container(color: red, width: 1000, height: 1000),
  ),
)

總結:
約束是從父類向下傳遞的,在計算自身layout時會用自身的約束與父類約束做一個收縮,只有當子約束的最大最小高度和寬度都是在父控件的范圍內才生效。否則以父約束的臨界點生效。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容