在Flutter框架分析(四)-RenderObject一文中,咱們簡單介紹了Flutter中RenderObject佈局的核心規則,Constraint向下,Size向上,父節點設置本節點的位置。在本文中,咱們會對這個規則進行詳細解析。markdown
RenderObject佈局的核心規則的具體解讀以下:架構
Flutter Framework對RenderObject Tree進行深度優先遍歷。並將Constraint經過parent傳給child的方式逐步向下傳遞。RenderObject爲了計算自身的Size,RenderObject必須遵循其父節點傳遞下來的Constraint。對於某些依賴其子節點Size的RenderObject來講,在計算其Size以前還須要獲取其子節點的Size。所以RenderObject的Size會逐步向上傳遞。 該規則的示意圖以下所示:框架
接下來,咱們將經過一個簡單的例子來解析這個核心規則。less
示例代碼以下:ide
class Example3Test extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(width: 100, height: 100, color: Colors.red),
);
}
}
複製代碼
能夠看到,這個代碼很是簡單,即在屏幕的中間畫一個長寬均爲100的紅色方塊。其圖以下所示。函數
該示例對應的RenderObject Tree以下:oop
接下來,咱們將結合源碼逐步講解Constraint和Size傳遞的過程。源碼分析
RenderView的performLayout函數以下:佈局
@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)),並經過子RenderObject的layout函數,將一個固定爲屏幕大小的Constraint(w=392.7, h=803.6)傳遞給子節點,即強制要求其子節點的Size也爲屏幕的大小。post
接下來,咱們看子節點RenderPositionedBox的performLayout函數。其源碼以下:
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));
}
}
複製代碼
能夠看到,首先會經過constraints的loosen函數,將RenderView傳遞下來的Constraint(w=392.7, h=803.6)放寬至Constraint(0<=w<=392.7, 0<=h<=803.6),並將其經過子節點的layout函數傳遞給子節點。
子節點在其performLayout函數中會計算出其Size,而後RenderPositionedBox根據該Size計算出本身的Size。子節點的performLayout會在接下來進行分析。
最後,根據RenderPositionedBox的Size和子節點的Size計算出子節點相對於RenderPositionedBox的位置,並將該值賦予子節點parentData的offset。
接下來,咱們分析RenderConstrainedBox的performLayout函數。其源碼以下:
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,此例中Constraint是Constraint(w=100, h=100)。
而後,因爲RenderConstrainedBox只是一個包含子節點的容器,將自身的Size設置爲子節點的Size(100, 100)。
這裏不須要設置子節點的parentData.offset,緣由是子節點會填充滿RenderConstrainedBox,所以其子節點的parentData.offset就是offset(0,0)。
最後,咱們來看_RenderColoredBox的performLayout函數。 RenderColoredBox未重寫performLayout函數,其函數調用關係以下:
最終會調用到RenderBox的performResize函數,其源代碼以下:
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,從而肯定其大小。該流程圖以下所示:
本文主要結合源碼分析了Flutter佈局的核心規則,其重點以下:
Flutter architectural overview
Flutter框架分析(一)--架構總覽
Flutter框架分析(二)-- Widget
Flutter框架分析(三)-- Element
Flutter框架分析(四)-RenderObject
Flutter框架分析(五)-Widget,Element,RenderObject樹
Flutter框架分析(七)-relayoutBoundary
Flutter框架分析(八)-Platform Channel
Flutter框架分析- Parent Data
Flutter框架分析 -InheritedWidget