はじめに
前回はDart言語の基礎文法を学びました。

今回は状態管理を学びます。
アプリケーション開発において「状態管理」はとても重要なテーマです。特にユーザーの入力や操作によってUIが変わるアプリケーションでは、状態管理が欠かせません。Flutterでも、状態(State)を適切に管理することがアプリケーションの品質に大きく影響します。
この記事では、Flutterの状態管理の基本となるStatefulWidget
とsetState
を使って、状態がどのように変わり、それに伴ってUIが更新されるのかを初心者向けにわかりやすく解説していきます。状態管理の基本を理解すれば、より複雑なアプリケーションの開発が楽になります。
1. StatelessWidget vs StatefulWidget
まず、Flutterには2つの主要なウィジェットがあります。それはStatelessWidgetとStatefulWidgetです。
StatelessWidget
StatelessWidgetは、その名前の通り状態を持たないウィジェットです。状態が変わらない画面や静的な要素を作成する際に使います。Text
やIcon
などは、StatelessWidgetに分類されます。
StatelessWidgetの例
import 'package:flutter/material.dart';
class MyStatelessWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('StatelessWidget Example')),
body: Center(child: Text('This is a stateless widget')),
);
}
}
void main() {
runApp(MaterialApp(home: MyStatelessWidget()));
}

StatefulWidget
一方、StatefulWidgetは状態を持つウィジェットです。状態が変わることで、UIもそれに応じて更新されるような場面で使います。ユーザーの入力や操作に応じて、画面が動的に変化する場合に使うのがStatefulWidget
です。
StatefulWidgetの基本的な構造
下記コードのポイントは、StatefulWidget
が状態を持ち、状態を管理するためにStateクラス(_MyStatefulWidgetState
)を定義しているところです。このクラス内で状態を変更し、それに応じてUIを更新します。
import 'package:flutter/material.dart';
class MyStatefulWidget extends StatefulWidget {
@override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('StatefulWidget Example')),
body: Center(child: Text('This is a stateful widget')),
);
}
}
void main() {
runApp(MaterialApp(home: MyStatefulWidget()));
}

setStateを使った状態管理
StatefulWidget
で状態を管理する際に使うのが、setStateメソッドです。setState
を呼び出すことで、状態を変更し、それに伴ってUIが再レンダリングされます。
状態の変更とUIの更新
setState
を使うことで、状態を更新し、その状態に基づいてUIを再描画する仕組みを実現できます。以下は、ボタンを押すたびにカウントが増えるシンプルなカウンターアプリの例です。
例:カウンターアプリ
- 状態の定義:
_counter
という整数変数でカウントの状態を管理しています。初期値は0
です。 - 状態の変更:
_incrementCounter
メソッドでsetState
を呼び出し、_counter
の値を1つ増やします。このメソッドが呼ばれるたびに、_counter
が更新され、UIも自動的に再レンダリングされます。 - UIの更新:
Text
ウィジェットの中で_counter
の値を表示しているため、ボタンを押すたびに表示される数字が更新されます。
import 'package:flutter/material.dart';
class CounterApp extends StatefulWidget {
@override
_CounterAppState createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
int _counter = 0; // 状態を管理する変数
// カウントを増やすメソッド
void _incrementCounter() {
setState(() {
_counter++; // 状態の変更
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter App')),
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.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter, // ボタンを押すとカウントを増やす
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
void main() {
runApp(MaterialApp(home: CounterApp()));
}

ColumnやRowを使ったレイアウトの状態管理
状態管理は、単に数値を増減させるだけでなく、複雑なUIレイアウトでも必要です。例えば、Column
やRow
を使って、複数のウィジェットを縦や横に並べ、その状態を動的に変更することも可能です。
以下は、Column
内に複数のボタンを並べ、それぞれがクリックされるたびにカウントが増加する例です。
例:複数ボタンで状態を管理する
- 2つのボタンとそれぞれのカウントが独立して状態を管理しています。
Column
を使って縦に配置し、_incrementCount1
と_incrementCount2
でそれぞれのボタンの状態を変更しています。- それぞれのボタンを押すと、対応するカウントが増え、UIに反映されます。
import 'package:flutter/material.dart';
class MultiButtonApp extends StatefulWidget {
@override
_MultiButtonAppState createState() => _MultiButtonAppState();
}
class _MultiButtonAppState extends State<MultiButtonApp> {
int _count1 = 0;
int _count2 = 0;
// ボタン1のカウントを増やす
void _incrementCount1() {
setState(() {
_count1++;
});
}
// ボタン2のカウントを増やす
void _incrementCount2() {
setState(() {
_count2++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Multi Button App')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Button 1 count: $_count1'),
ElevatedButton(
onPressed: _incrementCount1,
child: Text('Increment Button 1'),
),
SizedBox(height: 20), // ボタン間にスペースを入れる
Text('Button 2 count: $_count2'),
ElevatedButton(
onPressed: _incrementCount2,
child: Text('Increment Button 2'),
),
],
),
);
}
}
void main() {
runApp(MaterialApp(home: MultiButtonApp()));
}

setStateを使う上での注意点
setState
は非常に便利ですが、使い方によってはパフォーマンスに悪影響を与える可能性もあります。以下のポイントを押さえておくと、効率的な状態管理が可能です。
setStateの呼び出しは必要最小限に
setState
を呼び出すたびにUI全体が再描画されるため、不要な場所での呼び出しを避けましょう。特に、大規模なウィジェットツリーでの頻繁な再描画は、パフォーマンスの低下につながります。
状態を局所的に管理する
- アプリ全体で状態を管理するのではなく、状態が影響を与える部分だけで管理するのが理想です。これにより、再レンダリングの範囲を狭め、効率的な動作が実現できます。
まとめ
今回の記事では、Flutterの基本的な状態管理について学びました。StatefulWidgetを使うことで、状態の変化に応じてUIを更新することが可能になります。特に、setState
を使って状態を管理する方法は、Flutterアプリの開発において基本中の基本です。
この記事のポイント
- StatefulWidgetとStatelessWidgetの違いを理解
- setStateを使って状態を変更し、UIを更新する方法を学習
- 複数のウィジェットに対して状態管理を行う方法
状態管理の基本を理解すれば、より複雑なアプリケーションの構築がスムーズに進みます。
次回は、実際にTodoアプリを作成してみます。
第5回はこちら

コメント