📘

【Flutter】Widget(ウィジェット)とは

2025/02/02に公開

はじめに

公式ドキュメントを簡単にまとめた備忘録的な内容です
初学者の方の参考になれば幸いです

Widget(ウィジェット)とは

FlutterはWdgetを使用してUIを構成します。(公式ドキュメントには"everything is a widget"と書いてありました)

WidgetはFlutterアプリのUIの構成要素であり、UIの一部を不変的に宣言したものです。Widgetは、テキストやボタンなどの物理的な側面から、パディングや整列などのレイアウト効果を含む、UIのあらゆる側面を記述するために使われます。

各Wdgetには親子関係が存在し、親ウィジェットからコンテキストを受け取ることができます。
以下のサンプルコードでインスタンスかされたクラスは全てウィジェットです。
MaterialApp, Scaffold, AppBar, Text, Center, Builder, Column, SizedBox, ElevatedButton

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp( // Root Widget
      home: Scaffold( // MaterialAppの子Widget
        appBar: AppBar( // Scaffoldの子Widget
          title: const Text('My Home Page'),
        ),
        body: Center( // Scaffoldの子Widget
          child: Builder( // Centerの子Widget
            builder: (context) {
              return Column(
                children: [
                  const Text('Hello, World!'),
                  const SizedBox(height: 20),
                  ElevatedButton( // ElevatedButtonの子Widget
                    onPressed: () {
                      print('Click!');
                    },
                    child: const Text('A button'),// ElevatedButtonの子Widget
                  ),
                ],
              );
            },
          ),
        ),
      ),
    );
  }
}

実行結果

Widgetの構成

Flutterでは多くのWdgetは他の多くの小さなWidgetを組み合わせることで画面を構成します。

画面のレイアウトを作成するためのレイアウトWidgetというWigetがあります。(Padding, Alignment, Row, Column,Gridなど)

レイアウトWidgetを記述しただけでは何も表示されませんが、他のWigetのレイアウトを制御することができます。先の例のコードを実行すると、Hello, World!というテキストとA buttonというテキストが書かれたボタンとを画面の中央に縦に表示します。これらの要素を配置するために、利用可能なスペースの中央に子要素を配置するCenterと、子要素を縦に次々と配置するColumnがあります。


(公式ドキュメントから引用)

Widgetのビルド

FlutterでUIを作るには、Widgetオブジェクトのbuildメソッドをオーバーライドします。全てのWidgetはbuildメソッドを持っていて、別のWidgetを返さなければなりません。

例えば、画面にテキストをパディング付きで追加したい場合、次のように書きます。

class PaddedText extends StatelessWidget {
  const PaddedText({super.key});

  
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: const Text('Hello, World!'),
    );
  }
}

フレームワークは、このWidgetが作成されるときと、このウィジェットの依存関係(Widgetに渡されるStateなど)が変更されるときに、buildメソッドを呼び出します。このメソッドはすべてのフレームで呼び出される可能性があります。
FlutterがどのようにWidgetをレンダリングするかについては、Flutterのアーキテクチャ概要を参照してください。

WidgetのState

FlutterではWidgetにはStatelessWidgetStatefulWgetが存在します。

変更可能な状態を持たない(時間経過によって変化するクラス・プロパティを持たない)Widgetは、StatelessWidgetのサブクラスです。PaddingTextIconなど、多くの組み込みウィジェットはstatelessです。独自のウィジェットを作成する場合、ほとんどの場合、StatelessWidgetを作成します。

Widgetがユーザの操作やその他の要因に基づいて変更される必要がある場合、そのウィジェットはstatefulです。例えば、Widgetにカウンタがあり、ユーザがボタンをタップするたびにカウンタが増加する場合、カウンタの値がWidgetのstateになります。この値が変更されると、UIの一部を更新するためにWidgetをリビルドする必要があります。これらのWidgetは、StatefulWidgetのサブクラスで、変更された値をStateのサブクラスである別のクラスに格納します。StatefulWidgetにはビルドbuildメソッドがありません。代わりに、下の例に示すように、UIはStateオブジェクトを通してビルドされます。

class CounterWidget extends StatefulWidget {
  
  State<CounterWidget> createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
  
  
  Widget build(BuildContext context) {
    return Text('$_counter');
  }
}

Stateオブジェクトを変更する(たとえば、カウンターをインクリメントする)ときは常に、setState()を呼び出して、Stateのbuildメソッドを再度呼び出してUIを更新するようにフレームワークにシグナルを送る必要があります。

StateをWidgetオブジェクトから分離することで、他のWidgetは状態を保持する必要なく、StatelessWidgetStatefluWidgetを同じように扱えるようになります。親Widgetは子Widgetのインスタンスを再生成しても、子の状態を失うことなく、状態を保持できます。この仕組みは、フレームワークが適切に既存の状態オブジェクトを見つけて再利用することで実現されています。

参考

公式ドキュメント

Discussion