初心者と学ぶFlutter第4回:State管理の基本

初心者と学ぶFlutter

はじめに

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

初心者と学ぶFlutter第3回:Dartの基本文法
Flutter開発に必要なDartの基本文法を初心者向けに解説。データ型、制御構文、関数、クラス、非同期処理まで丁寧に学べる入門ガイド。

今回は状態管理を学びます。

アプリケーション開発において「状態管理」はとても重要なテーマです。特にユーザーの入力や操作によってUIが変わるアプリケーションでは、状態管理が欠かせません。Flutterでも、状態(State)を適切に管理することがアプリケーションの品質に大きく影響します。

この記事では、Flutterの状態管理の基本となるStatefulWidgetsetStateを使って、状態がどのように変わり、それに伴ってUIが更新されるのかを初心者向けにわかりやすく解説していきます。状態管理の基本を理解すれば、より複雑なアプリケーションの開発が楽になります。

1. StatelessWidget vs StatefulWidget

まず、Flutterには2つの主要なウィジェットがあります。それはStatelessWidgetStatefulWidgetです。

StatelessWidget

StatelessWidgetは、その名前の通り状態を持たないウィジェットです。状態が変わらない画面や静的な要素を作成する際に使います。TextIconなどは、StatelessWidgetに分類されます。

StatelessWidgetの例

Dart
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を更新します。

Dart
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の値を表示しているため、ボタンを押すたびに表示される数字が更新されます。
Dart
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レイアウトでも必要です。例えば、ColumnRowを使って、複数のウィジェットを縦や横に並べ、その状態を動的に変更することも可能です。

以下は、Column内に複数のボタンを並べ、それぞれがクリックされるたびにカウントが増加する例です。

例:複数ボタンで状態を管理する

  • 2つのボタンとそれぞれのカウントが独立して状態を管理しています。
  • Columnを使って縦に配置し、_incrementCount1_incrementCount2でそれぞれのボタンの状態を変更しています。
  • それぞれのボタンを押すと、対応するカウントが増え、UIに反映されます。
Dart
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アプリの開発において基本中の基本です。

この記事のポイント

  • StatefulWidgetStatelessWidgetの違いを理解
  • setStateを使って状態を変更し、UIを更新する方法を学習
  • 複数のウィジェットに対して状態管理を行う方法

状態管理の基本を理解すれば、より複雑なアプリケーションの構築がスムーズに進みます。
次回は、実際にTodoアプリを作成してみます。

第5回はこちら

初心者と学ぶFlutter第5回:Todoアプリの設計と実装
FlutterでシンプルなTodoアプリを作成。ScaffoldやListTileなどのUI要素を使い、CRUD機能を実装してタスク管理を学びます。

コメント

タイトルとURLをコピーしました