こんにちは、アウトプット太郎です。
今回はタイトルの通り、初期アプリ(カウンターアプリ)
の内容を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関数が長くなる恐れがあり、その場合は別ファイルに書いて、その関数を呼び出すといいのではと思いました。