1
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?

More than 1 year has passed since last update.

output_tarouAdvent Calendar 2023

Day 3

Flutter初期アプリをproviderの状態管理に変えてみる

Posted at

こんにちは、アウトプット太郎です。

今回はタイトルの通り、初期アプリ(カウンターアプリ)
の内容をproviderを使った状態管理に変更し、setStateとはどんな点が違うのか、
学んでみました。

初期アプリ解説記事はこちら

以降手順としては下記になります。

1.providerパッケージの追加

ターミナルで下記を実行
$ flutter pub add provider

上記によって、pubspec.yamlにproviderパッケージが入ります。
こうすることでローカルキャッシュにソースコードが保存され、build時に使用されるようになります。

2.counter_modelの作成
別ファイルとしてcounter_model.dartを作成します。

import 'package:flutter/material.dart';

class CounterModel extends ChangeNotifier {
  int _counter = 0;

  int get counter => _counter;

  void incrementCounter() {
    _counter++;
    notifyListeners();
  }
}

ChangeNotifierクラスを継承したCounterModelを定義しています。

ChangeNotifierクラスとは
状態変更を通知する機能を持ったクラスです。

FlutterではしばしばProviderと一緒に使われ、
アプリケーションのトップレベルで状態モデルを宣言した後、
各widgetでその状態モデルを使用すると、そのwidgetはリスナーとして
ChangeNotifierに保持されます。

そして、ChangeNotifierクラスが持つメソッド内でnotifyListeners();が実行されると、状態の変更としてリスナーへ通知が行われ、リスナーの再描画が行われます。

要するに、ChangeNotifireは事前にリスナーとして登録しておいたら、変更があった時に通知がきて、リロードがかかる仕組みを提供しています。

それではステップ3としてproviderの宣言を行います。

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CounterModel(),
      child: const MyApp(),
    ),
  );
}

そして利用したいwidget内で状態モデルを定義し、
ボタン内でクリックイベントが起きた時にcounterModelの持つ、incrementCounterを呼び出しています。

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    final counterModel = Provider.of<CounterModel>(context, listen: false);

    return Scaffold(
      // ...
      floatingActionButton: FloatingActionButton(
        onPressed: () => counterModel.incrementCounter(),
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

final counterModel = Provider.of<CounterModel>(context, listen: false);
この書き方について解説すると、
Providerパッケージのofメソッドで、CounterModelの状態モデルを指定しています。
また、listenパラメータにfalseを渡すことでこちらはリスナー登録していないことが分かります。

逆に実際にカウンターの数字を示している下記では

      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '${Provider.of<CounterModel>(context).counter}',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),

'${Provider.of<CounterModel>(context).counter}',
listenは定義されず、defaultでtrueになっています。

以上、一連の流れでsetStateからproviderを使った状態管理に移行することが出来ました。

setStateの時はStatefulWidgetとStateが1対1でしたが、
providerにすると、一度宣言すれば色々な画面でCounterModelを使えるのは便利だなと思いました。

ただ、トップレベルで宣言するとなると、数が増えてきたときにmain関数が長くなる恐れがあり、その場合は別ファイルに書いて、その関数を呼び出すといいのではと思いました。

1
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
1
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?