Flutterの基礎である2つのウィジェット(StatefulWidgetとStatelessWidget)を理解しよう!
Flutterの基本的な構造
まず初めに、FlutterのUIはすべてウィジェットで構成されています。 ウィジェットは、Flutterで画面に表示される要素や画面の構造であるレイアウトを表現するために使用されます。
つまり、画面上のボタン、テキスト、画像など、表示される全ての要素はウィジェットとして扱われます。また、これらのウィジェットを組み合わせることによって、さまざまなUIが表現できます。
ウィジェットとツリー構造
Flutterのウィジェットは、木のような構造をしています。イメージとしては、木の根から枝が広がっていく感じです。この構造を「ツリー構造」と呼び、ウィジェット同士の階層的な関係を表現する方法です。
ツリー構造を使うと、ウィジェットの配置や動作を整理しやすくできます。
レシピでイメージしてみましょう。まず、レシピ(親)が一番上に位置し、その下には材料や手順(子)が続きます。さらに、材料や手順の(子)下には、材料の分量や手順の詳細(別の子)を追加できます。
例えば、画面全体を表す親ウィジェットの下に、ボタンやテキストフィールドなどの子ウィジェットを配置できます。そして、ボタン内部にはアイコンやラベルなどの別の子ウィジェットを配置することも可能です。
このように、ツリー構造を使うことで、ウィジェット同士の関係が明確になります。親ウィジェットが子ウィジェットを管理し、操作や情報の伝達がスムーズに行われます。
ツリー構造は重要
ツリー構造を理解することはとても重要です。なぜなら、ウィジェットの関係や階層が把握できるようになると、アプリのデザインや動作を簡単に作成できるようになるからです。
次の画像は、ツリー構造を表現したものです。 この画像は、ウィジェットの階層関係を視覚的に理解するためのものです。画像を見ることで、ウィジェットの親子関係や階層構造を把握ができます。

上記の例では、親としてMyApp
を指定し、その直下には、MaterialApp
が来ます。マテリアルデザインのアプリを開発する場合は、このウィジェットを使用します。 MaterialApp
にはhome
というプロパティがあり、アプリ起動時にどの画面(ウィジェット)を表示するかを設定します。今回指定しているのはMyHomePage
です。
さらにその直下にはScaffold
が来ています。
Scaffold
とは、レイアウト用のウィジェットです。Scaffold
は「白紙のキャンバス」であり、他のウィジェット「絵の具」のような役割を果たします。
Scaffold
というキャンバスにボタンやテキスト等のレイヤーをどんどん置いていって進めるのがFlutterでUI構築する際の基礎になります。
Scaffold
の各プロパティにはそれぞれ、AppBar
、Center
、FloatingActionButton
というウィジェットを設定しています。
また、AppBar
にはText
、Center
にはColumn
、FloatingActionButton
にはIcon
が来ています。さらに、Column
の直下には、Text
が来ています。
このように、Flutterはウィジェットをツリー構造として組み合わせていくということが理解できました。
基本的なウィジェットの紹介
それでは、2種類のウィジェットについて解説します。 Flutterには、よく使われる基本的なウィジェットとして2つのタイプがあります。
- StatelessWidget
- StatefulWidget
StatelessWidget とは
StatelessWidgetは、状態(state)を持たないウィジェットです。 そのため、上位のウィジェットのステートが更新されたときは、基本的にリビルドが走ります。
例えば、テキスト、アイコン、イメージなどのウィジェットがあります。
基本的なコードの書き方
こちらは、StatelessWidgetの基本的なコードの書き方です。
class クラス名 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
);
}
}
コードの詳細
さらにコードについて詳しく見ていきましょう。
クラス名を指定し、StatelessWidgetを継承しています。StatelessWidgetは、状態(state)を持たないウィジェットを作るための基本的なクラスです。
class クラス名 extends StatelessWidget {
ウィジェットのビルド(構築)を行うためのメソッドの定義です。BuildContext context
という引数を受け取ります。
@override
Widget build(BuildContext context) {
build()
メソッドから返される値で、コンテナウィジェットを返します。コンテナは他のウィジェットを包むための箱です。
コンテナの中には、さらにテキストやボタンなど他のウィジェットを追加することができます。
return Container()
StatefulWidget とは
StatefulWidgetは、状態(state)を持つウィジェットです。 これにより、ウィジェットの状態を変更することで画面の再描画が行われます。ユーザーの入力やアプリの状態変化に応じて、動的なUIを作成するのに適しています。
例えば、チェックボックス、ラジオボタン、リストなどがあります。
基本的なコードの書き方
こちらは、StatefulWidgetの基本的なコードの書き方です。
class クラス名 extends StatefulWidget {
@override
_(クラス名)State createState() => _(クラス名)State();
}
class _(クラス名)State extends State<クラス名> {
@override
Widget build(BuildContext context) {
return Container(
);
}
}
コードの詳細
さらにコードについて詳しく見ていきましょう。
クラス名を指定し、StatefulWidgetを継承しています。StatefulWidgetは、状態(state)を持つウィジェットを作るための基本的なクラスです。
class クラス名 extends StatefulWidget {
新しいステートオブジェクトを作成するためのメソッドです。
@override
_(クラス名)State createState() => _(クラス名)State();
}
新しいステートオブジェクトのクラスを作成しています。
class _(クラス名)State extends State<クラス名> {
ウィジェットのビルド(構築)を行うためのメソッドの定義です。BuildContext context
という引数を受け取ります。
@override
Widget build(BuildContext context) {
build()
メソッドから返される値で、コンテナウィジェットを返します。コンテナは他のウィジェットを包むための箱です。
コンテナの中には、さらにテキストやボタンなど他のウィジェットを追加することができます。
return Container()
まとめ
Flutterでは、さまざまなウィジェット(テキスト、アイコン、チェックボックスなど)を組み合わせてアプリを作り、ウィジェットツリーを構築してアプリの見た目や動作を決めます。そして、状態を持つウィジェットを使ってアプリのデザインや動作を変化させることができます。
ここからは、Flutterのサンプルアプリで使われているカウンターについて詳しく見ていきましょう。
サンプル画面
このサンプル画面は、Flutterプロジェクトを作成して「flutter run」と実行すると表示されるデフォルトのカウンターアプリです。 画面中央に数字が表示され、右下の+ボタンをタップすると数字が増えていきます。とてもシンプルな構造ですが、数字の変化を管理するために状態を持つウィジェットが使われています。

サンプルコード
以下は、上記のサンプル画面を構成するためのコードです(コメントは省略しています)。 コードを詳しく説明する前に、基本的な構造を見てみましょう。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
さらにコードについて詳しく見ていきましょう。
main関数
はじめに、main関数を見てみましょう。
void main() {
runApp(MyApp());
}
void main()
関数は、アプリケーションのエントリーポイントです。runApp()
関数を使用して、MyApp
クラスを実行します。
MyAppクラス
次に、MyAppクラスを見てみましょう。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
MyApp
クラスは、Flutterアプリケーションのルートとなるウィジェットです。build()
メソッドでは、MaterialApp
ウィジェットを返しています。MaterialApp
は、アプリケーションのテーマやタイトル、そしてMyHomePage
クラスをホーム画面として設定しています。
MyHomePageクラス
次に、MyHomePageクラスを見てみましょう。
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
MyHomePage
クラスは、カウンターアプリのホーム画面を定義しています。StatefulWidgetを継承しており、状態を持つことができます。title
パラメーターは、ホーム画面のタイトルを表しています。
_MyHomePageStateクラス
次に、_MyHomePageStateクラスを見てみましょう。
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
_MyHomePageState
クラスは、MyHomePage
クラスの状態を表しています。_counter
変数はカウンターの値を保持し、_incrementCounter()
メソッドはボタンが押されたときにカウンターを増やします。
_MyHomePageクラス の build
次に、_MyHomePageState の buildを見てみましょう。
@override
Widget build(BuildContext context) {
return Scaffold(
//以下の内容は後ほど説明
appBar: ...,
body: ...,
floatingActionButton: ...,
);
}
build()
メソッドでは、Scaffold
ウィジェットを返しています。Scaffold
は、アプリケーションの基本的なビジュアルレイアウト用のウィジェットです。
appBar
次に、appBarを見てみましょう。
appBar: AppBar(
title: Text(widget.title),
),
appBar
は、アプリケーションの上部に表示されるバーを作成します。属性として、title
が指定されており、その内容が表示されます。ここでは、文字列要素のText
ウィジェットが使われており、widget.title
が指定されています。
widget.title
は、StatefulWidgetであるMyHomePage
に渡されたtitle
を取得しています。Stateの内部であっても、このようにして呼び出したウィジェットの情報を取得できます。
body
次に、bodyを見てみましょう。
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
Center
ウィジェットで要素を中央に配置しています。コンテンツを中央寄せしたい場合に使用されます。この中にはchild
という属性があります。Center
は単なるコンテンツのコンテナであるため、子要素が必要であり、child
またはchildren
属性を持つ必要があります。この場合、子要素はColumn
です。これにより、複数の要素を縦に並べることができます。
Column
は、リストや表のような要素を作成する際に便利なウィジェットです。mainAxisAlignment
属性は、子要素を縦方向に並べる方法を指定するために使用されます。ここでは、Text
ウィジェットを使用して文字を設定しています。
style
次に、styleを見てみましょう。
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
Text
ウィジェットにはstyle
が設定されています。これは文字の大きさや色を指定するためのものです。
ここでは、Theme.of(context)
を使用して親のテーマ情報を取得しています。context
は「文脈」という意味で、親のテーマ情報も保持しています。これにより、テキストに関連するテーマであるtextTheme
のheadline4
スタイルを取得しています。
floatingActionButton
最後にfloatingActionButtonを見てみましょう。
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
画面右下に浮動するボタンを作成するためのFloatingActionButton
ウィジェットが含まれます。onPressed
コールバックは、ボタンが押されたときに呼び出されるメソッドを指定します。ここでは_incrementCounter
メソッドが指定されています。
これにより、Flutterアプリのホームページが作成され、ボタンが押されるたびにカウンターの値がインクリメントされ、画面が更新されるようになります。
まとめ
今回は、Flutterを使ったシンプルなカウンターアプリを解説しました。
基本的な部分を解説しましたが、Flutterの公式サイトにはアプリ開発に必要なドキュメントが数多くあります。新機能も定期的に追加されているため、常に最新情報をチェックすることが大切です。また、Flutterのコミュニティを活用すると、疑問点を解消することができるでしょう。
さらに、公式サイトでは多くのサンプルコードも掲載されていますので、実践的な学習に役立ててください。