FlutterのHello Worldを超深掘った話

1. はじめに

Flutter初学者の ryuichi111std です。
以前はXamarin派だったのですが、最近はFlutterに面白さを感じてチョロチョロと学習しています(別にXamarinが嫌いになったわけではないです)。
なので、「Flutterとはこうだ!」みたいな知識はまだ持っていないのですが、その中で理解した知識をまとめておこうと思います。

ということで、まずは画面(ページ)作成の概念についてまとめようと思ったのですが・・・Hello World実装に対して「なぜ?なぜ?病」を発症してしまったので、それについて記録しようかと。。。

(注意) なぜ?なぜ?を深掘った内容なので、とにかく動くものを作る知識が欲しいんだ、という方にはおすすめしない記事ですm(_ _)m

使用環境:Mac Book Pro(High Sierra) / Flutter 0.7.3 / Visual Studio Code

2. Hello World

公式サイトにも載っていますが、まずは「Hello World」を作ります。
以下のコマンドで hello_world プロジェクトを生成。

$ flutter create hello_world

Visual Studio Codeで hello_world を開きます。(Android Studioでもお好みで)
「lib/main.dart」に、結構、はじめに見るには重めのテンプレサンプルが生成されるので、実装をざっくり削除して以下の実装に書き換えます。

// lib/main.dart

import 'package:flutter/widgets.dart';

void main() {
  Text text = new Text(
    "Hello World", 
    textDirection: TextDirection.ltr,);
  Center center = new Center(child: text);
  runApp(center);
}

実行画面は以下のようになります。

f:id:daigo-knowlbo:20180915172241p:plain

2.1. main()関数

多くの開発言語・環境と同じく main()関数 がエントリーポイントになります。

2.2. Text / Centerの作成

Text と Center が「Hello Flutter」と画面に表示するためのUI要素の定義になります。
Flutterでは、これらは共に「Widget」と呼ばれます。

Textオブジェクトの生成には、第1引数で表示する文字列を、オプション引数 textDirection にはテキストの表示方向を指定します(ltr=左から右)。
サンプルとしてはtextDirectionが余分に感じますが、Textオブジェクトでは指定必須のオプションパラメータになります(指定しないと実行時エラーが発生)。

Centerオブジェクトは、名前の通り子要素をセンターに配置する役割を持ちます。コンストラクタで、childプロパティにTextオブジェクトを指定します。

※ 最小実装の Hello World であれば、Textオブジェクトのみで実装することが出来ますが、その場合、画面左上にちょろっとテキストが表示される残念な状態になります(iPhone Xのような角が丸いタイプの画面だと角に文字がはみ出た状態にもなります)。

2.3. runApp()関数

runApp()関数は、引数に Widget オブジェクトを取り、そのWidgetをルートWidgetとして、それを画面いっぱいに満たすようにしてFlutterアプリケーションを実行します。

※ runApp()の定義は https://docs.flutter.io/flutter/widgets/runApp.html で確認することが出来ます。

2.4. import 'package:flutter/widgets.dart'

外部で定義されたオブジェクトを利用するためのインポート文です。
公式の HelloWorld だと「import 'package:flutter/material.dart'」が行われています。
ただ、最低限のimportでは本サンプルで使用した「import 'package:flutter/widgets.dart'」になります。
Text / Center / TextDirection / runApp を利用するために必要なimportなのですが・・・

2.4.1. Textクラス

textは、Flutterの実装では「flutter/packages/flutter/lib/src/widgets/text.dart」で実装されています。

https://github.com/flutter/flutter/blob/1ad538e454c77496fbd068b9e8b5f8b61c2f6d96/packages/flutter/lib/src/widgets/text.dart#L214

Hello Worldソースから辿ると・・・
main.dart から import した flutter/widget.dart は「export 'src/widgets/text.dart';」という実装を持ちます。

https://github.com/flutter/flutter/blob/master/packages/flutter/lib/widgets.dart

Text.dartは、「class Text extends StatelessWidget { }」という形で Textクラス を実装しています。

https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/text.dart

2.4.2. Centerクラス

Centerは、Flutterの実装では「Futter/packages/flutter/lib/src/widgets/basic.dart」に実装されています。

https://github.com/flutter/flutter/blob/1ad538e454c77496fbd068b9e8b5f8b61c2f6d96/packages/flutter/lib/src/widgets/basic.dart#L1541

Hello Worldソースから辿ると・・・
main.dart から import した flutter/widget.dart は「export 'src/widgets/basic.dart';」という実装を持ちます。

https://github.com/flutter/flutter/blob/master/packages/flutter/lib/widgets.dart

Basic.dartは、「class Center extends Align { }」という形で Centerクラス を実装しています。

https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/basic.dart

ちなみに Centerクラスの基本クラス Align は、同じくbasic.dartファイル内で「class Align extends SingleChildRenderObjectWidget { }」と定義されています。
(更に深掘ると Center <- Align <- SingleChildRenderObjectWidget(framework.dart) <- RenderObjectWidget(framework.dart) <- Widget(framework.dart) というクラス継承構造になっています。)

2.4.3. TextDirection列挙

TextDirectionは、Flutterの実装ではFlutter.engine側の engine/lib/ui/text.dart で実装されています(enum TextDirection { })。

https://github.com/flutter/engine/blob/master/lib/ui/text.dart

Hello Worldソースから辿ると・・・
main.dart から import した flutter/widget.dart は「export 'src/widgets/basic.dart';」という実装を持ちます。

https://github.com/flutter/flutter/blob/1ad538e454c77496fbd068b9e8b5f8b61c2f6d96/packages/flutter/lib/src/widgets/basic.dart#L1541

basic.dartは「export 'package:flutter/painting.dart';」という実装を持ち、painting.dartは「export 'src/painting/basic_types.dart';」という実装を持ちます。更にbasic_types.dartは「import 'dart:ui' show TextDirection;」という実装を持ちます。
おお、繋がった。dart:ui → engine/lib/ui/text.dart

2.4.4. runApp()

runApp()は、Flutterの実装では「Futter/packages/flutter/lib/src/widgets/binding.dart」に実装されています。

https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/binding.dart

Hello Worldソースから辿ると・・・
main.dart から import した flutter/widget.dart は「export 'src/widgets/binding.dart';」という実装を持ちます。

https://github.com/flutter/flutter/blob/master/packages/flutter/lib/widgets.dart

3. まとめ

ということで・・・Text / Center / TextDirection / runApp を使うために必要な import は package:flutter/material.dart であり、1行のimport文から各種必要なクラスの読み込み(export)が行われているFlutter内の実装を明確に理解することが出来ました。
5分で流し読んでしまう Hello World ですが、ちょっとした なぜ?なぜ? からえらい深掘りになったというお話です。。。
でも、本質部分を理解することで、もっと大きな問題に出会ったときに、その解決方法を自力で導き出せると思うので、まあ知っていて損はないかと^^

Flutterはリファレンスもしっかりしていて、

https://docs.flutter.io/index.html

オープンソースなので、何か疑問点やうまく動作しない部分が合ったとき、ある程度内部動作を調べることも出来て、

flutter:
https://github.com/flutter/flutter

engine:
https://github.com/flutter/engine

今後もいろいろリサーチしていこうかと思います。