7
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

FlutterのStatelessWidgetとStatefulWidgetの違い

Posted at

📖参考

[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()メソッドをオーバーライドする。

StatelessWidget
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
// 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
// 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である場合にのみ定数コンストラクタを利用することができる。

Constant_Constructor
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

ここで、StatelessWidgetStatefulWidgetもコンストラクタは以下のように定義されており、名前付きパラメータkeyは任意パラメータである(=引数がなくてもよい)ため、子クラスで暗黙的にsuper()23が呼び出せる。

Constructor
// 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である。

  1. ただし、親クラスで他の(引数が必要な)コンストラクタが記述されている場合は(引数の不要な)デフォルトコンストラクタが自動的に生成されていないため、子クラスで親クラスのコンストラクタを指定して呼び出す必要がある。

  2. コンストラクタ引数の{}ブロックは**任意パラメータ(Optional Parameter)**を表すため、super()のように引数がなくてもよい。

  3. StatelessWidget / StatefulWidgetはどちらもWidgetクラスを親クラスとしている。

7
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?