Flutterが管理する各ツリーの正体

## 免責事項(a.k.a. 言い訳)
自分なりに正確に記したつもりですが間違ってたらごめんなさい&教えてください:bow_tone1: 

## モチベーション

Flutterのドキュメントなんかを読んでいると以下の三つのツリーが断片的に出てきます.

* Widgetツリー
* Elementツリー
* Renderツリー 

この記事ではそれぞれのツリーのルートがどうやって生成されるのかをソースコードから追跡してみようと思います.

それぞれのツリーの役割や更新方法などは以下を参照されることをお勧めします.

* [Flutter の Widget ツリーの裏側で起こっていること](https://medium.com/flutter-jp/dive-into-flutter-4add38741d07)

## runApp

runApp関数は受け取ったルート・ウィジットを"膨らませます". この際[WidgetsFlutterBinding](https://api.flutter.dev/flutter/widgets/WidgetsFlutterBinding-class.html)が色々やってくれるようです.

```dart
void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..attachRootWidget(app)
    ..scheduleWarmUpFrame();
}
```

カスケード構文を使ってattachRootWidgetやscheduleWarmUpFrameはWidgetBindingを呼び出しています. これらは[WidgetsBindingミックスイン](https://api.flutter.dev/flutter/widgets/WidgetsBinding-mixin.html)に定義されています.

### attachRootWidget

[attachRootWidget](https://api.flutter.dev/flutter/widgets/WidgetsBinding/attachRootWidget.html)はrootWidgetをrenderViewElement以下に取り付けます. ややこしいのはcontainerであるrenderViewに取り付けられるのはElementツリーらしいと言うことです.

> The given container is the RenderObject that the Element tree should be inserted into.

```dart
void attachRootWidget(Widget rootWidget) {
  _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
    container: renderView,
    debugShortDescription: '[root]',
    child: rootWidget,
  ).attachToRenderTree(buildOwner, renderViewElement);
}
```

### RenderObjectToWidgetAdapter

 [RenderObjectToWidgetAdapter](https://api.flutter.dev/flutter/widgets/RenderObjectToWidgetAdapter-class.html)と言うウィジットが初期化されています. 

また引数としてrenderViewを取ります. この変数はどこで初期化されたのか分かりませんが, 名前からレンダー・ツリーのルート・オブジェクトを指すようです. 実態解明はひとまず置いておいて, [attachToRenderTree](https://api.flutter.dev/flutter/widgets/RenderObjectToWidgetAdapter/attachToRenderTree.html)を見てみましょう.

```dart
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
  if (element == null) {
    owner.lockState(() {
      element = createElement();
      assert(element != null);
      element.assignOwner(owner);
    });
    owner.buildScope(element, () {
      element.mount(null, null);
    });
  } else {
    element._newWidget = this;
    element.markNeedsBuild();
  }
  return element;
}
```

### RenderObjectToWidgetElement

ここでようやくcreateElementと言う関数が出てきました. しかしElementをコンテナであるRenderObjectに付けると言うような処理はないです:no_good_tone1:

 [createElement](https://github.com/flutter/flutter/blob/2d2a1ffec9/packages/flutter/lib/src/widgets/binding.dart#L892)は[RenderObjectToWidgetElement](https://api.flutter.dev/flutter/widgets/RenderObjectToWidgetElement-class.html)のインスタンスを生成します.

```dart
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
```

> This element class is the instantiation of a RenderObjectToWidgetAdapter widget. It can be used only as the root of an Element tree.

~~うーん. コンテナがルートだと書いてあります. またRenderObjectToWidgetAdapterのインスタンスとも書いています. 引数もRenderObjectToWidgetElementインスタンスになってるようですが・・・全然分かりません.~~

ドキュメントの少し下の行に"RenderObjectToWidgetAdapter"のためにとあります. 実際attachToRenderTreeの処理はそのようになっています.

>> In typical usage, it will be instantiated for a RenderObjectToWidgetAdapter whose container is the RenderView...


次は[mount](https://github.com/flutter/flutter/blob/2d2a1ffec9/packages/flutter/lib/src/widgets/binding.dart#L966)をみてみましょう. シグネチャを見ると何をしているのかよく分かります.

```dart
void mount (Element parent, dynamic newSlot)
```

Elementツリーを構築するメソッドのようです. parentがnullなことからRenderObjectToWidgetElementがルート要素であることが分かります. 実際以下のような継承関係があります.

* [Element](https://api.flutter.dev/flutter/widgets/Element-class.html)
* [RenderObjectElement](https://api.flutter.dev/flutter/widgets/RenderObjectElement-class.html)
* [RootRenderObjectElement](https://api.flutter.dev/flutter/widgets/RootRenderObjectElement-class.html)
* [RenderObjectToWidgetElement](https://api.flutter.dev/flutter/widgets/RenderObjectToWidgetElement-class.html)

重要なのは上から2番目のRenderObjectElementの[mountメソッド](https://github.com/flutter/flutter/blob/2d2a1ffec9/packages/flutter/lib/src/widgets/framework.dart#L4747)です.

```dart
@override
void mount(Element parent, dynamic newSlot) {
  super.mount(parent, newSlot);
  _renderObject = widget.createRenderObject(this); // (*)
  assert(() {
    _debugUpdateRenderObjectOwner();
    return true;
  }());
  assert(_slot == newSlot);
  attachRenderObject(newSlot);
  _dirty = false;
}
```

ここでwidgetは何を指すでしょう. createElementはRenderObjectToWidgetElementをインスタンス化しているだけでした. そこで[RenderObjectToWidgetElementのコンストラクタ](https://github.com/flutter/flutter/blob/1aedbb1835/packages/flutter/lib/src/widgets/binding.dart#L944)を見るとwidgetが渡されています. これが継承を辿ってRenderObjectElementにセットされたわけです. つまりwidgetはRenderObjectToWidgetAdapterなのが分かります.

ではRenderObjectToWidgetAdapterはどんなウィジットなのでしょうか. 継承をたどるとRenderObjectWidgetと言うウィジットにたどり着きます.

* [Widget](https://api.flutter.dev/flutter/widgets/Widget-class.html)
* [RenderObjectWidget](https://api.flutter.dev/flutter/widgets/RenderObjectWidget-class.html)
* [RenderObjectToWidgetAdapter](https://api.flutter.dev/flutter/widgets/RenderObjectToWidgetAdapter-class.html)

 RenderObjectToWidgetAdapterの[createRenderObject](https://github.com/flutter/flutter/blob/2d2a1ffec9/packages/flutter/lib/src/widgets/binding.dart#L895)を見るとコンテナが返されています.

```dart
@override
  RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
```

これはRenderツリーのルートでした. つまりElementツリーのルートがマウントされるときにRenderツリーが生成されるわけです.

### ここまでの流れ

最初はルート・ウィジットであるRenderObjectToWidgetAdapterが生成されます. これがWidgetツリーのルートだと思われます. その生成プロセスでcreateElementが呼び出されてElementツリーのルート(RenderObjectToWidgetElement)が生成され, 更にそこからmountメソッド経由でcreateRenderObjectでRenderツリーのルートであるrenderObjectが生成されると言うのがここまでの流れです.

### renderViewインスタンスの生成場所

解決していない問題として[RenderView](https://api.flutter.dev/flutter/rendering/RenderView-class.html)インスタンスの生成場所です. これがcontainerの正体でした.

最初にrenderViewが出現するのはattachRootWidget内部です. RenderObjectToWidgetAdapterをインスタンス化するときにrenderViewが突然引数として現れるのでした. 正体を突き止めるために[WidgetsFlutterBinding](https://api.flutter.dev/flutter/widgets/WidgetsFlutterBinding-class.html)クラスをもう一度見直してみましょう.

 実はattachRootWidgetメソッドは[WidgetsBindingミックスイン](https://api.flutter.dev/flutter/widgets/WidgetsBinding-mixin.html)に定義されています. 更にこのミックスインの継承元の一つに[RendererBinding](https://api.flutter.dev/flutter/rendering/RendererBinding-mixin.html)と言うのがあります.

RendererBindingミックスインに[initRenderView](https://api.flutter.dev/flutter/rendering/RendererBinding/initRenderView.html)メソッドがあります. いかにもなメソッドが出てきました.

```dart
void initRenderView() {
  assert(renderView == null);
  renderView = RenderView(configuration: createViewConfiguration(), window: window);
  renderView.prepareInitialFrame();
}
```

WidgetBindingに現れるrenderViewはRenderBindingからもたらされるものでした:ok_hand_tone1:

### まとめ
　
Widgetツリー, Elementツリー, そしてRenderツリーのルートが作成されることが分かりました. それぞれのツリーとルートとなるクラスを整理しておきます.

|ツリー|ルート|
|:--:|:--:|
|Widgetツリー|RenderObjectToWidgetAdapter|
|Elementツリー|RenderObjectToWidgetElement|
|Renderツリー|RenderView|


### [WidgetsFlutterBinding](https://api.flutter.dev/flutter/widgets/WidgetsFlutterBinding-class.html)の謎

　runAppを実行すると最初に実行されるBindingが何をやってるのか分からなかったです.

#### そもそも何と何を結びつけるのか?

> This is the glue that binds the framework to the Flutter engine.

　フレームワークとFlutterエンジンを結びつける接着剤のようなものと言うことでしょうか. 以下はFlutterシステムのブロック・ダイアグラムです.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362766/906d8dee-8df5-ded9-5016-52c9049e584a.png)

 ドキュメントの言葉通りなら一番上と真ん中のレイヤーを結びつけるのがバインディングと言うことのようです. 

 しかしWidgetsFlutterBindingはいろんなバインディングのミックスインとして定義されています. 

```dart
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding { ... }
```

　いくつかのバインディングはフレームワーク・レイヤーに同じ名前のものが存在します. またAPI Docsのライブラリに共通の名前が並んでいます.

![Screenshot 2019-10-13 at 8.29.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362766/e47586d8-5eea-377d-ba8a-3d3596b61825.png)

 例えばGestureBindingを見てみましょう.

```dart
mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, HitTestTarget { ... }
```

　HitTestable, HitTestDispatcher, HitTestTargetは[gestureライブラリ](https://api.flutter.dev/flutter/gestures/gestures-library.html)に定義されています.

 これらを辿ってみてもFlutterエンジンと関係ありそうなコードは出てきません. それもそのはずでFlutterエンジンはフレームワークとは別のプロジェクトとして独立しているからです.

#### ネイティブ・コードはどこにあるのか? あるいはどうやって結合されるのか?

　分からないのでStackoverflowに質問しました. わかる人がいたら教えてください

[Where or when does the WidgetsFlutterBinding actually bind the flutter framework to the engine?](https://stackoverflow.com/questions/58359723/where-or-when-does-the-widgetsflutterbinding-actually-bind-the-flutter-framework)