📖参考
[Flutter | Introduction to widgets]
(https://flutter.dev/docs/development/ui/widgets-intro)
[KotlinとJavaができる人向けDart速習]
(https://qiita.com/kikuchy/items/2cce118d38fc15324b2b)
[Dart言語の3つのコンストラクタ]
(https://makicamel.hatenablog.com/entry/2019/03/14/2139330)
[Language tour | Dart]
(https://dart.dev/guides/language/language-tour)
[Flutter:StatelessWidgetとStatefulWidgetの違い]
(https://qiita.com/rindarinda5/items/496c70e54784921a31f0)
Flutter WidgetにKeyが必要な理由, 仕組みについて
[FlutterのUIウィジェットを理解するための基本を知ろう!]
(https://codezine.jp/article/detail/13329)
[Flutter 全部俺 Advent Calendar 2019]
(https://adventar.org/calendars/4140)
[FlutterにおけるWidgetとElementとRenderObjectの基本]
(https://engineer.recruit-lifestyle.co.jp/techblog/2019-12-24-flutter-rendering/)
🧐途中で見つけた面白そうな記事
[Dartのconstコンストラクタとは]
(https://zenn.dev/razokulover/articles/61380323a73e00572789)
[Flutter の Widget ツリーの裏側で起こっていること]
(https://medium.com/flutter-jp/dive-into-flutter-4add38741d07)
Widget vs. Element vs. RenderObject
クラス | 説明 |
---|---|
Widget |
自身のUIの構成情報と子Widgetをfinalなフィールドとして保持するImmutableなクラス。createElement() メソッドによってElementを生成し、createRenderObject() によってRenderObjectを生成する。 |
Element | **UIの状態(State)**を保持するMutableなクラス。Element同士を集約するだけの機能をもつComponentElementと、RenderObjectを生成するRenderObjectElementに分類される。mount() メソッド内のattachRenderObject() メソッドによって、Widgetから受け取ったRenderObjectをRenderObjectツリーに挿入する。 |
RenderObject | UIの描画を行うMutableなクラス。RenderObjectツリーの情報をもとに、スクリーンにUIを描画する。 |
StatelessWidget vs. StatefulWidget
StatelessWidgetは、状態(State)を持たない静的Widgetであり、フィールドは全てfinalで保持される。
StatelessWidget
を継承するWidgetでは、定数コンストラクタ(※後述)と、Widgetを生成するbuild()
メソッドをオーバーライドする。
class StatelessWidgetSample extends StatelessWidget {
// 定数コンストラクタ
const StatelessWidgetSample({Key? key,}) : super(key: key);
// build()
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: StatelessWidgetSample2(),
);
}
}
StatefulWidgetは、状態を持つ動的Widgetであり、ある瞬間(状態)における画面を構成するWidgetオブジェクトと、次にbuild()
が呼び出されるまで半永続的に情報を保持するStateオブジェクトに分類される。
StatefulWidget
を継承するWidgetでは、定数コンストラクタと、Stateを生成するcreateState()
メソッドをオーバーライドする。
// StatefulWidget
class StatefulWidgetSample extends StatefulWidget {
// 定数コンストラクタ
const StatefulWidgetSample({Key? key}) : super(key: key);
// createState()
@override
_StateSample createState() => _StateSample();
}
State<T where StatefulWidget>
を継承するStateには、Stateの値を変化させるsetState()
メソッドを記述し、Widgetを生成するbuild()
メソッドをオーバーライドする。
// State
class _StateSample extends State<StatefulWidgetSample> {
// 変数フィールド
int _counter = 0;
// フィールドの値を変化させるメソッド → Stateの内部状態が変化
void _increment() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
ElevatedButton(
onPressed: _increment,
child: const Text('Increment'),
),
Text('Count: $_counter'),
]
);
}
定数コンストラクタ(Constant Constructor)
定数コンストラクタは、インスタンスをコンパイル時定数(compile-time constant)として生成したいクラスに対して採用する。
コンパイル時定数にする以上、フィールドは定数であることが保証されている必要があるため、全てのフィールドがfinalである場合にのみ定数コンストラクタを利用することができる。
class Point {
final int x;
final int y;
// 定数コンストラクタ
const Point(this.x, this.y);
}
// 定数コンストラクタを採用したクラスは、constキーワードを付けて「コンパイル時定数」として宣言できる
var p = const Point(1, 2);
var q = const Point(1, 2);
var r = Point(1, 2);
identical(p, q); // true ← コンパイル時定数は、等価であるインスタンスを使い回すため等値になる
identical(q, r); // false ← (実質的に)実行時定数は、等価であってもインスタンスを新たに生成するため等値にならない
Widgetのコンストラクタについて
Flutter公式のWidgetのコンストラクタの記法が見慣れないため、ここで解説する。
const StatelessWidgetSample({Key? key,}) : super(key: key);
const
キーワードが使われていることから、インスタンス生成時もconstキーワードを付けて定数オブジェクトとして生成しなければならない定数コンストラクタであることが分かる。
次に、コンストラクタ引数{Key? key,}
の部分は、const StatelessWidgetSample(key: KeySample())
のように、インスタンス生成時にオプションで引数ラベルkeyにOptional<Key>型の値を設定できる、**名前付き任意パラメータ(Optional Named Parameter)**である。
最後に、コロン:
の部分はブロック{}
を省略する代わりに用いられ、親クラスの(デフォルトコンストラクタでない)コンストラクタを明示的に呼び出す特殊な記法で記述されている。
By default, a constructor in a subclass calls the superclass’s unnamed, no-argument constructor. The superclass’s constructor is called at the beginning of the constructor body.
(中略)
If the superclass doesn’t have an unnamed, no-argument constructor, then you must manually call one of the constructors in the superclass. Specify the superclass constructor after a colon (:), just before the constructor body (if any).
Invoking a non-default superclass constructor - Language tour | Dart より引用
Widgetのコンストラクタに関する補足①(クラス継承)
子クラスは親クラスのコンストラクタを必ず呼び出さなければならないという決まりがあるため、親クラスのコンストラクタを明示的に呼び出さない場合、子クラスは親クラスのデフォルトコンストラクタを呼び出す。1
ここで、StatelessWidgetもStatefulWidgetもコンストラクタは以下のように定義されており、名前付きパラメータkeyは任意パラメータである(=引数がなくてもよい)ため、子クラスで暗黙的にsuper()
23が呼び出せる。
// StatelessWidget
const StatelessWidget({Key? key}) : super(key: key);
// StatefulWidget
const StatefulWidget({Key? key}) : super(key: key);
なお、Javaと同様、Dartにおける全てのクラスはObjectクラスのサブクラスとして定義される。
Widgetのコンストラクタに関する補足②(Key)
Widgetのコンストラクタに任意パラメータとして定義されているKeyは、ウィジェットツリー内のWidgetを一意に識別する際に利用され、既定値はnull
である。