Flutterの設計で一番悩ましいのが、ViewとModelの連携。状態遷移だと思われる。
2020年の初頭からFlutterやりはじめて、「Provider + ChangeNotifier」でやってた。これでも全然動く。
ただ、Providerは必ずcontext
を経由する必要があるので、ウィジェットの階層構造に気をつけないといけないとか、データを変更した後にnotifyListeners
を自分で呼ばないといけないとか、そういう面倒さがあった。
値を再代入するだけで、当該UIのウィジェットが再ビルドされたらそれで良いのだけども、GUI(デスクトップアプリやスマホアプリ)を作る場合にそこが一番面倒。
RIverpodの便利なところは、提供されるProviderがimmutableに設計されているため、グローバルに定義しても良い点。BuildContextに依存しない点の2点がでかい。
riverpod版
import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:state_notifier/state_notifier.dart'; final counterProvider = StateNotifierProvider((_) => CounterNotifier()); class CounterNotifier extends StateNotifier<int> { CounterNotifier(): super(0); void increment () => state++; } class CounterPage extends HookWidget { Widget build(BuildContext context) { final v = useProvider(counterProvider.state); final counter = useProvider(counterProvider); return Scaffold( body: Center( child: Text(v.toString(), style: TextStyle(fontSize: 24)), ), floatingActionButton: FloatingActionButton( onPressed: () => counter.increment(), child: Icon(Icons.add), ), ); } }
StatefulWidget + State + setState
import 'package:flutter/material.dart'; class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return new Scaffold( body: Center( child: Text('$_counter', style: TextStyle(fontSize: 24)) ), ), floatingActionButton: new FloatingActionButton( onPressed: _incrementCounter, child: new Icon(Icons.add), ), ); } }
この2つを見比べると、パッと見るだけでも以下のことがわかる。
- ユーザーの操作に伴い
setState
を書きまくるコードはだいぶ辛い。 - StateNotifierでは
state
を再代入するだけに徹する。公開される値がstate
しか無いのがミソ。- 再代入するとはいえ、State
の T
の持ってる色んなプロパティの状態をキープしつつイミュータブルにしたいので、freezedパッケージを使うのよね、と。
- 再代入するとはいえ、State
個人的には、notifyListener()
を意識しなくて良いのが好きなので、StateNotifier
を推していきたい。
- スクロールの管理
- テキストボックスの値の代入
- 引数ありの画面遷移
この辺がクリア(自分の中で噛み砕ければ)できれば、Riverpodいける。がんばろう。