【Flutter】Widget(ウィジェット)とは
はじめに
公式ドキュメントを簡単にまとめた備忘録的な内容です
初学者の方の参考になれば幸いです
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にはStatelessWidget
とStatefulWget
が存在します。
変更可能な状態を持たない(時間経過によって変化するクラス・プロパティを持たない)Widgetは、StatelessWidget
のサブクラスです。Padding
、Text
、Icon
など、多くの組み込みウィジェットは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は状態を保持する必要なく、StatelessWidget
とStatefluWidget
を同じように扱えるようになります。親Widgetは子Widgetのインスタンスを再生成しても、子の状態を失うことなく、状態を保持できます。この仕組みは、フレームワークが適切に既存の状態オブジェクトを見つけて再利用することで実現されています。
Discussion