flutterの最もしっくりくるState管理法 - Provider + ChangeNotifierの使い方
February 23, 2020
flutterが推奨していたstate管理手法Blocの学習や実装で、アプリ開発に何度も挫折してきた。 しかし、気づいたらprovider + changeNotifierというものがflutterの推奨手法になっていた。
On this page, we are going to be using the provider package. If you are new to Flutter and you don’t have a strong reason to choose another approach (Redux, Rx, hooks, etc.), this is probably the approach you should start with.
Provider + ChangeNotifierを使ったstate管理法はBlocと比べて学習コストも100分の1(大げさかもしれないけどBlocむずすぎる。結局よくわからなかった。)。このstate管理手法は感覚的にもしっくりきた。flutter officialも言っている様に特に理由がなければこの手法を使ってstate管理しておけば間違えないと思った。flutterのプロジェクトにprovider + ChangeNotiferを導入したので、やり方を紹介。
完成イメージ
1. プラグイン
changeNotifierはproviderと合わせて使うので、providerプラグインをまずインストール。
dependencies:
flutter:
sdk: flutter
+ provider: ^4.0.4
2. モデルを作成
ChangeNotifierを拡張したmodelを作る。 このモデル内で何か変化が起きたとき(アイテムリストに新しいアイテムを追加・変更・削除など)、notifyListeners()を使ってその変化をUIに知らせる。 今回はリストに新しいアイテムを追加する処理のみを追加。新しいアイテムが追加されたら、その変更を知らせるnofityListeners()を実行。
+ import 'package:flutter/material.dart';
+
+ class itemModel extends ChangeNotifier {
+ List<Item> _items = <Item>[
+ Item("test1"),
+ Item("test2"),
+ Item("test3"),
+ ];
+
+ List<Item> get items => _Items;
+
+ //新しいItemを追加
+ void add(Item Item) {
+ _Items.add(Item);
+ // 変更があったことを通知する。
+ notifyListeners();
+ }
+ }
class Item {
const Item(this.title);
final String title;
}
3. プロバイダーにモデルを追加
2で作成したモデルをプロジェクト全体で使用できるようにするために、モデルをプロバイダーに追加。
import 'package:flutter/material.dart';
+ import 'package:Project/Models/item_model.dart';
+ import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ return MultiProvider(
+ child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Hello World',
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: HomeScreen(),
),
+ providers: [
+ ChangeNotifierProvider(
+ create: (context) => ItemModel(),
+ )
+ ],
);
}
}
4.リストを画面に表示させる。
notifyListeners()での変更の知らせを受け取るために、Consumerを使用します。
新しいアイテムがリストに追加され、notifyListeners()が実行されると、Consumer
class HomeScreen extends StatefulWidget {
HomeScreen({Key key}) : super(key: key);
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Hello World'),
),
+ body: Consumer<ItemModel>(
+ builder: (BuildContext context, ItemModel value, Widget child) {
+ return ListView.builder(
+ itemBuilder: (BuildContext context, int index) {
+ return Card(
+ child: ListTile(
+ title: Text(value.items[index].title),
+ ),
+ );
+ },
+ itemCount: value.items.length,
+ );
+ }
+ ),
);
}
}
変更をUIに反映させたくはないけど、modelのメソッドやプロパティなどを使用したい場合は、Provider.ofを使用します。 listen: falseをpropoertiesに入れると、UIはrebuildされません。
Provider.of<ItemModel>(context, listen: false).add(_item);
5. リストに新しい項目を追加し、画面に反映させる。
リストに新しいアイテムを追加するための処理を追加します。 これでフローティングボタンを押したら、リストに新しいアイテムが追加され、リストがリビルドされ、UIに新しく追加したアイテムが表示されます。
class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
var _ItemModel = Provider.of<ItemModel>(context);
return Scaffold(
appBar: ・・・,
body: ・・・,
+ floatingActionButton: FloatingActionButton(
+ onPressed: () {
+ var _item = Item("test4");
+ _ItemModel.add(_item);
+ },
+ child: Icon(Icons.add),
+ backgroundColor: Colors.indigo,
+ ),
);
}
}
6. まとめ
Blocで何度も挫折したが、ChangeNotifierなら大丈夫そう。なんか感覚的にしっくりくる。ChangeNotifierがあってほんとよかった。✌️