0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ひとりアドカレ2024 byもんすんAdvent Calendar 2024

Day 21

Roadmap.shで学ぶFlutter - State Management -

Last updated at Posted at 2024-12-20

導入

こんにちは、もんすんです。

roadmap.shのFlutterの「StateManagement」の章です。
状態管理に関する内容の学習になります。
日本では、かなり主力になっているRiverpodに関しては、今回の記事では割愛します。
代わりにChangeNotifierValueNotifierをメインで進めていきます。

状態管理

Flutterにおける状態管理とは、Flutterアプリケーションのデータや状態を管理・更新するプロセスを指します。
Flutterでは、ユーザーがアプリケーションとやり取りをする際などに、ウィジェットの状態が動的に変わることがあります。
F状態管理の手法には次のようなものがあります。

  • ScopedModel: 状態を集中管理するためのモデルを使用するサードパーティ製の状態管理ソリューションです。
  • Provider: 最小限のボイラープレートコードでウィジェットが状態にアクセスできるようにする軽量なソリューションです。
  • BLoC (Business Logic Component): ストリームとリアクティブプログラミングを用いて状態を管理する手法です。
  • Redux: ReactのReduxライブラリに触発された状態管理ソリューションです。
  • InheritedWidget: 状態をウィジェットツリーに渡すことができる組み込みのウィジェットです。

どの状態管理手法を選ぶかは、プロジェクトの複雑さや規模によって異なります。
小規模なプロジェクトでは、ProviderやInheritedWidgetで十分かもしれませんが、大規模なプロジェクトでは、ScopedModelやReduxのようなより強力なソリューションが必要になることがあります。

ChangeNotifierクラス

FlutterのChangeNotifierは、Flutterにおける状態管理の基本クラスです。
これを使用することで、開発者はデータの変更を監視し、リスナーに通知することができ、ユーザーインターフェイスを効率的に更新することが可能になります。

ChangeNotifierを拡張することで、開発者は特定の状態やデータモデルを表すカスタムクラスを作成できます。
また、ChangeNotifierで状態を管理し、UIを動的に更新することで、ユーザー体験が向上します。
これにより、状態管理が簡素化され、インタラクティブなFlutterアプリケーションの作成が可能になります。

参考
https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html

ChangeNotifierは、VoidCallbackを使って通知するための変更通知APIを提供するクラスで、拡張またはミックスインとして使用できます。
リスナーの追加はO(1)、リスナーの削除と通知の送信はO(N)です。
ここでNはリスナーの数です。

データモデルにChangeNotifierサブクラスを使用する

データ構造がChangeNotifierを拡張またはミックスインすることで、Listenableインターフェースを実装し、ListenableBuilderなどのListenablesの変更を監視するウィジェットで利用できるようになります。

以下は、ListenableBuilderを使って、カウントを含むTextウィジェットの再ビルドを制限するシンプルなカウンターを実装した例です。

現在のカウントはChangeNotifierサブクラスに格納され、値が変更されるとListenableBuilderの内容が再ビルドされます。

import 'package:flutter/material.dart';

void main() {
  runApp(const ListenableBuilderExample());
}

class CounterModel with ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count += 1;
    notifyListeners();
  }
}

class ListenableBuilderExample extends StatefulWidget {
  const ListenableBuilderExample({super.key});

  @override
  State<ListenableBuilderExample> createState() =>
      _ListenableBuilderExampleState();
}

class _ListenableBuilderExampleState extends State<ListenableBuilderExample> {
  final CounterModel _counter = CounterModel();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('ListenableBuilder Example')),
        body: CounterBody(counterNotifier: _counter),
        floatingActionButton: FloatingActionButton(
          onPressed: _counter.increment,
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

class CounterBody extends StatelessWidget {
  const CounterBody({super.key, required this.counterNotifier});

  final CounterModel counterNotifier;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          const Text('Current counter value:'),
          ListenableBuilder(
            listenable: counterNotifier,
            builder: (BuildContext context, Widget? child) {
              return Text('${counterNotifier.count}');
            },
          ),
        ],
      ),
    );
  }
}

この場合、ChangeNotifierのサブクラスはリストをカプセル化しており、リストにアイテムが追加されるたびにクライアントに通知します。

ValueNotifier

FlutterのValueNotifierも、Flutterでの状態管理によく使われます。
ValueNotifierは、ChangeNotifierを拡張したクラスで、単一の値を効率的に管理し、その変更をリスナーに通知します。

ValueNotifierを使用することで、開発者はカウンターやユーザー入力などの特定のデータを簡単に追跡し、更新することができます。
これにより、状態管理が簡略化され、動的なFlutterインターフェースの実現が可能になります。

参考

ValueNotifierは、ValueListenableBuilderを使用してそれをリッスンしているウィジェットだけを再ビルドするため、パフォーマンス効率が高いらしいです。

ValueNotifierの作成

ValueNotifierは簡単に作成できます。新しいファイルvalue_notifiers.dartを作成し、すべてのValueNotifierを一つのファイルにまとめます。ValueNotifierは、intStringdoubleboolean型のデータを保持できますし、自分のデータ型(クラスを使って)も使用できます。

import 'package:flutter/material.dart';
ValueNotifier<int> buttonClickedTimes = ValueNotifier(0);

アプリのシナリオ: ボタンがクリックされた回数をカウントするアプリを作成しています。ValueNotifierの使い方を理解するために、ボタンのための独立したStatelessウィジェットクラスを作成し、カウンターを表示するためにStatelessウィジェットを親ウィジェットとして使います。

ValueNotifierの値を変更する

ボタンウィジェット内で、ValueNotifierの値をインクリメントします。

// value_notifiers.dartをインポートし忘れないでください。さもないとエラーが発生します。
GestureDetector(
  onTap: () => buttonClickedTimes.value = buttonClickedTimes.value + 1,
  child: Text("Button", style: Theme.of(context).textTheme.button),
);

ValueListenableBuilder

ValueListenableBuilderは、ValueNotifierにアクセスし、ValueNotifierの値に依存するUIの部分を再ビルドするために使用されるウィジェットです。

親ウィジェット内で、TextウィジェットをValueListenableBuilderで囲みます。

ValueListenableBuilder(
  valueListenable: buttonClickedTimes,
  builder: (BuildContext context, int counterValue, Widget child) {
    return Text("Counter: $counterValue");
  },
)

builder:(@required BuildContext context, @required int counterValue, Widget child)は、ValueNotifierの値が変更されるたびにUIを再ビルドします。

ネストされたValueListenableBuilder

ウィジェットが複数のValueNotifierに依存している場合、ネストされたValueListenableBuilderを使用します。

ValueListenableBuilder(
  valueListenable: buttonClickedTimes,
  builder: (BuildContext context, int counterValue, Widget child) {
    return ValueListenableBuilder(
      valueListenable: textColor,
      builder: (context, textColorValue, child) {
        return Text(
          "Counter: $counterValue",
          style: TextStyle(color: textColorValue),
        );
      },
    );
  },
)

ValueNotifiersの破棄

使用されなくなったValueNotifierを破棄することは、メモリの損失を防ぐための良い習慣です。

@override
void dispose() {
  buttonClickedTimes.dispose();
  textColor.dispose();
  super.dispose();
}

Optional Parameter Child

childパラメータはオプションですが、ValueNotifierValueListenableBuilderをさらにパフォーマンス効率よく使用するために役立ちます。

ValueListenableBuilder(
  valueListenable: ColorNotifier,
  builder: (BuildContext context, int colorValue, Widget child) {
    return Container(
      height: 100,
      width: 100,
      color: colorValue,
      child: child,
    );
  },
  child: Text("button", textAlign: TextAlign.center),
)

上記のコードでは、ContainercolorNotifierを使用して色を変更していますが、TextウィジェットはcolorNotifierの値に関係なく同じ値を持っています。しかし、Containerの子であるため、Containerが再ビルドされるとTextウィジェットも再ビルドされます。この問題に対処するために、オプションのchildパラメータが役立ちます。

終わりに

今回は、Flutterにおける状態管理の基本概念と、ChangeNotifierおよびValueNotifierの使用方法について詳しく解説しました。

状態管理は、アプリケーションの規模や要件に応じて適切な方法を選択することが重要です。

この記事を参考に、プロジェクトに最適な方法を見つけてみてください!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?