# 三顆渲染樹是如何運作的?
## 職責(What)
- Widget Tree:UI 藍圖、設定檔
- Element Tree:UI 生命週期的管理
- RenderObject Tree:UI 佈局與繪製
## 相互關係(Why & How)
### 性能優化
Declarative(陳述式) vs Imperative(命令式)
- Imperative:
```jave=
TextView.setText // 改變文字,不會重建TextView
```
- Declarative:
禁止直接改變 `RenderObject` 的屬性。
> 遵循陳述式的 `UI = f(state)`,在每次更新的時候,重新建立一份**完整包含所有參數、不會被改變的 UI 設定檔**,並透過它來更新`RenderObject`。而這個所謂的 UI 設定檔,就是我們最熟悉的`Widget`。
### 重建成本
Widget 的重建成本小,RenderObject的重建成本大
> 產生一個新的 widget instance 是很廉價,因為產生它只會生成資料不會直接生成 `Element` 和 `RenderObject`,昂貴與否得看你怎麼去變動你的 layout widget 那些。
### 誰產生誰,什麼時候產生(Who & When)
以 `RichText` 為例:
```dart=
class RichText extends MultiChildRenderObjectWidget {
...
@override
RenderParagraph createRenderObject(BuildContext context) {
...
}
@override
void updateRenderObject(BuildContext context, RenderParagraph renderObject) {
...
}
}
```
但 `Widget` 在什麼時候,被誰呼叫,去建立 `RenderObject`?
```dart=
abstract class RenderObjectElement extends Element {
@override
void mount(Element parent, dynamic newSlot) {
...
_renderObject = widget.createRenderObject(this);
...
_dirty = false;
}
}
```
- 什麼時候 -> `mount`。
- 被誰呼叫 -> `Element`。
## Element
Widget: XML/xib
RenderObject: View/UIView
**Element: Life cycle**

### 但 `Element` 在什麼時候,被誰產生?
`Widget` 的 `createElement`,並在下面兩個地方呼叫:
1. `RenderObjectToWidgetAdapter.attachToRenderTree`
2. `Element.inflateWidget`
#### 1. RenderObjectToWidgetAdapter
`RenderObjectToWidgetAdapter` 是整個 Flutter 中的第一個 `Widget`,也是由它來建立 Element Tree 中的第一個 `Element`。之後的 Child Element 都是由 Parent Element 來建立的。
```dart=
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
if (element == null) {
...
element = createElement(); // HERE
...
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}
```
#### 2. Element.inflateWidget
`Widget` 會建立其對應的 `Element`,並將自身傳入作為參數
```dart=
abstract class Element extends DiagnosticableTree implements BuildContext {
@protected
Element inflateWidget(Widget newWidget, dynamic newSlot) {
....
final Element newChild = newWidget.createElement(); // AND HERE
....
}
}
```
> `inflateWidget` 最主要的工作就是透過 `Widget` 建立 `Element`
## 各自的繼承關係圖,以及它們之間的對應關係

1. `Widget` 和 `Element` 之間有著明顯的一對一的關係。
2. 只有 `RenderObjectElement` 才會呼叫 `RenderObjectWidget` 去產生 `RenderObject`。
> 簡單來說,既然整個 Widget Tree 是一個由各種 Widget 複合出來的樹,我們要去渲染它時自然會遞迴地去呼叫 StatelessWidget 和 StatefulWidget 的 build 函數,直到我們遇到某個沒有 build 函數的 RenderObjectWidget,就由它產生其對應的 RenderObject。
### Example
```dart=
void main() {
runApp(Container(
alignment: Alignment.center,
child: Text(
'Hello, World!',
textDirection: TextDirection.ltr,
),
));
}
```

1. 複合的 `Widget`,例如 `Container`。
2. 只有在遇到 `RenderObjectWidget` `時才會去產生RenderObject`。
# 範例:Run App -> 建立渲染樹 -> 更新渲染樹
## Run App
這裡的 `RenderObjectToWidgetAdapter` 是整個 Flutter 中的第一個 `Widget`,也是由它來建立 Element Tree 中的第一個 `Element`。之後的Child Element 都是由 Parent Element 來建立的。
### Step 1:

### Step 2:

### Step 3:

### Step 4:

### Step 5:

### Step 6:

### Step 7:

### Step 8:

### Step 9:

### Step 10:

### Step 11:

## 更新 Widget Tree
`setState` -> `build` -> `updateChild`
```dart=
// In Element
@protected
@pragma('vm:prefer-inline')
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
....
}
```
| | newWidget == null | newWidget != null |
|---------------|---------------|------|
| child == null | Returns null. | Returns new Element. |
| child != null | Old child is removed, returns null. | Old child updated if possible, returns child or new Element. |
```dart=
// In Widget
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
```
# Ref
- https://ithelp.ithome.com.tw/articles/10234299
- https://juejin.cn/post/7103491448985059359
- https://medium.com/jastzeonic/flutter-render-%E7%9A%84%E9%82%A3%E4%B8%80%E5%85%A9%E4%BB%B6%E4%BA%8B%E6%83%85-d372ed9a9962
###### tags: `fugle study group`