Flutter框架分析(六)-Constraint

1. 前言

Flutter框架分析(四)-RenderObject一文中,咱們簡單介紹了FlutterRenderObject佈局的核心規則,Constraint向下,Size向上,父節點設置本節點的位置。在本文中,咱們會對這個規則進行詳細解析。markdown

2. 佈局原則解析

RenderObject佈局的核心規則的具體解讀以下:架構

  1. 一個Widget從它的父節點獲取Constraint,並將其傳遞給子節點。
  2. Widget對其子節點進行佈局。
  3. 最終,該節點告訴其父節點它的Size

Flutter FrameworkRenderObject Tree進行深度優先遍歷。並將Constraint經過parent傳給child的方式逐步向下傳遞。RenderObject爲了計算自身的SizeRenderObject必須遵循其父節點傳遞下來的Constraint。對於某些依賴其子節點SizeRenderObject來講,在計算其Size以前還須要獲取其子節點的Size。所以RenderObjectSize會逐步向上傳遞。 該規則的示意圖以下所示:框架

image.gif

接下來,咱們將經過一個簡單的例子來解析這個核心規則。less

3. 示例

示例代碼以下:ide

class Example3Test extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(width: 100, height: 100, color: Colors.red),
    );
  }
}
複製代碼

能夠看到,這個代碼很是簡單,即在屏幕的中間畫一個長寬均爲100的紅色方塊。其圖以下所示。函數

image.gif

該示例對應的RenderObject Tree以下:oop

image.gif

接下來,咱們將結合源碼逐步講解ConstraintSize傳遞的過程。源碼分析

RenderViewperformLayout函數以下:佈局

@override
void performLayout() {
  assert(_rootTransform != null);
//將其大小設置爲屏幕的大小
  _size = configuration.size;
  assert(_size.isFinite);

//將子節點的size設置爲屏幕的size
  if (child != null)
    child.layout(BoxConstraints.tight(_size));
}
複製代碼

能夠看到,其Size是屏幕的大小(Size(392.7, 803.6)),並經過子RenderObjectlayout函數,將一個固定爲屏幕大小的Constraint(w=392.7, h=803.6)傳遞給子節點,即強制要求其子節點的Size也爲屏幕的大小。post

接下來,咱們看子節點RenderPositionedBoxperformLayout函數。其源碼以下:

void performLayout() {
  final BoxConstraints constraints = this.constraints;
  final bool shrinkWrapWidth = _widthFactor != null || constraints.maxWidth == double.infinity;
  final bool shrinkWrapHeight = _heightFactor != null || constraints.maxHeight == double.infinity;

  if (child != null) {
//傳遞constrains給子節點
    child.layout(constraints.loosen(), parentUsesSize: true);
//利用子節點大小計算本身的size
    size = constraints.constrain(Size(shrinkWrapWidth ? child.size.width * (_widthFactor ?? 1.0) : double.infinity,
                                          shrinkWrapHeight ? child.size.height * (_heightFactor ?? 1.0) : double.infinity));
//設置子節點parentData中的offset,用於繪製時確認子節點的位置
    alignChild();
  } else {
    size = constraints.constrain(Size(shrinkWrapWidth ? 0.0 : double.infinity,
                                          shrinkWrapHeight ? 0.0 : double.infinity));
  }
}
複製代碼

能夠看到,首先會經過constraintsloosen函數,將RenderView傳遞下來的Constraint(w=392.7, h=803.6)放寬至Constraint(0<=w<=392.7, 0<=h<=803.6),並將其經過子節點的layout函數傳遞給子節點。

子節點在其performLayout函數中會計算出其Size,而後RenderPositionedBox根據該Size計算出本身的Size。子節點的performLayout會在接下來進行分析。

最後,根據RenderPositionedBoxSize和子節點的Size計算出子節點相對於RenderPositionedBox的位置,並將該值賦予子節點parentDataoffset

接下來,咱們分析RenderConstrainedBoxperformLayout函數。其源碼以下:

void performLayout() {
  final BoxConstraints constraints = this.constraints;
  if (child != null) {
//傳遞constrains給子節點
    child.layout(_additionalConstraints.enforce(constraints), parentUsesSize: true);
//利用子節點大小計算本身的size
    size = child.size;
  } else {
    size = _additionalConstraints.enforce(constraints).constrain(Size.zero);
  }
}
複製代碼

首先,經過父節點傳遞過來的Constraint(constraints)和自身構造函數中傳遞的Constraint(_additionalConstraints)計算出子節點所需的Constraint,此例中ConstraintConstraint(w=100, h=100)。

而後,因爲RenderConstrainedBox只是一個包含子節點的容器,將自身的Size設置爲子節點的Size(100, 100)。

這裏不須要設置子節點的parentData.offset,緣由是子節點會填充滿RenderConstrainedBox,所以其子節點的parentData.offset就是offset(0,0)。

最後,咱們來看_RenderColoredBox的performLayout函數。 RenderColoredBox未重寫performLayout函數,其函數調用關係以下:

image.gif

最終會調用到RenderBoxperformResize函數,其源代碼以下:

void performResize() {
  // default behavior for subclasses that have sizedByParent = true
  size = constraints.smallest;
  assert(size.isFinite);
}
複製代碼

可見,最終是經過父節點傳遞的Constraint計算獲得自身的Size(100, 100)。

總結上述流程,Constraint是父節點在performLayout函數中經過layout函數層層傳遞下來的,在子節點調用layout後,子節點會計算其Size,而後父節點會根據子節點的Size計算自身的Size,從而肯定其大小。該流程圖以下所示:

image.gif

4. 小結

本文主要結合源碼分析了Flutter佈局的核心規則,其重點以下:

  • 核心佈局規則是Constraint向下,Size向上,父節點設置本節點的位置。
  • performLayout通常包括如下幾步:首先將Constraint經過layout函數傳遞給子節點,子節點會經過layout函數在其performLayout函數中計算自身的Size,而後經過子節點的Size計算自身的Size,最後經過自身Size和子節點的Size計算子節點的parentDataOffset。該Offset會在繪製子節點的時候使用。

5. 參考文檔

Flutter architectural overview

6. 相關文章

Flutter框架分析(一)--架構總覽
Flutter框架分析(二)-- Widget
Flutter框架分析(三)-- Element
Flutter框架分析(四)-RenderObject
Flutter框架分析(五)-Widget,Element,RenderObject樹
Flutter框架分析(七)-relayoutBoundary
Flutter框架分析(八)-Platform Channel
Flutter框架分析- Parent Data
Flutter框架分析 -InheritedWidget

相關文章
相關標籤/搜索