[flutter]Riverpodで画面遷移時にStateNotifierを初期化する

公開:2020/10/12
更新:2020/10/13
2 min読了の目安(約2500字TECH技術記事

はじめに

人生初の技術記事です!
現在StateNotifier + ProviderからRiverpodへの移行作業をしております。自分は画面ごとにStateNotifierを用意するタイプの状態管理をしていたので、すんなりRiverpodに移行できませんでした。(理由は後述します)
画面遷移時に前の画面から受け取ったデータを使って遷移先のStateNotifierの初期化をするみたいな処理をしている人は割と多いと思いますが、これをRiverpodではどうやるのか紹介します。

hooksは使用していない前提です(hooksの文法がDartのクラスベースの文法と合わないなと感じています)。
ただhooksを使っていても参考にはなるかと思います

環境

バージョン
flutter 1.22.1(MacOS Catalina 10.15.4)
state_notifier 0.6.0
freezed 0.12.1
freezed_annotation 0.12.0
flutter_riverpod 0.11.1

問題点

Riverpodは基本的にグローバル変数としてStateNotifierProviderを初期化するのでStateNotifierのコンストラクタが走るタイミングで渡す値を決定する必要があります。これで困るのは前の画面から何らかのデータを次の画面のStateNotifierのコンストラクタに渡すときです。画面遷移が終わってからStateNotifierを初期化するみたいなことはできません
解決方法はいくつかありそうですが今回は1つだけ紹介します

onGenerateRouteを使う

flutterの画面遷移のやり方は複数あると思いますが、riverpodを使っている場合はonGenerateRouteを使った方法が一番いいと思われます。画面遷移の直前にStateNotifierの処理呼び出せるからです

ModalRoute.ofを廃止する

自分は今までModalRoute.ofを使っていたのですが、これは画面遷移時のargumentsがbuildメソッドの中でないと取得できないという問題があります。そのargumentを使った初期化をしたい場合build内でwatch(provider).初期化処理を呼ぶ必要がありエラーが出ます

// HogeScreen.dart
Widget build(BuildContext context, ScopedReader watch) {
  final arg = ModalRoute.of(context).settings.arguments as String;
  // error
  watch(provider).init(arg)
}

ModalRouteを使った方法からonGenerateRouteに使った方法に変えるのはそこまで変更点が多くないのですんなり移行できると思います

詳しくはこちら

onGenerateRouteの書き方

例えば上の例のように引数を一つ取るような場合は以下のように書き換えられます

// HogeScreenを生成する関数
// argumentsはここで取り出せます
Route<HogeScreen> hogeScreenGenerator(RouteSettings settings) {
  //後ほど使います
  final arg = settings.arguments as String;
  return MaterialPageRoute(builder: (context) => HogeScreen());
}

// ...
Route<dynamic> onGenerateRoute(RouteSettings settings) {
  switch(settings.name) {
    case HogeScreen.id:
      hogeScreenGenerator(settings);
      break;
    // ...
    // 他のページの生成関数
    // ...
  }
}

// ...
MaterialApp(
  onGenerateRoute: onGenerateRoute
)

(本題)初期化処理をonGenerateRouteでする

上の処理にそのままproviderの初期化処理を挟めば今までの画面ごとのStateNotifierの初期化と同じ感覚で使うことができると思います。

Route<HogeScreen> hogeScreenGenerator(RouteSettings settings) {
  final arg = settings.arguments as String;
  return MaterialPageRoute(builder: (context) {
    context.read(hogeProvider).init(arg);
    return HogeScreen();
  });
}

この方法を使った場合は画面遷移(画面生成)時に必ずfirestoreと通信するなどの初期化処理を好きなようにすることができると思います。

まとめ

Riverpodへの移行コストはそこまで高くはなさそうですが、微妙にProviderのときとは構成を変える必要があるのかなと思います(Riverpodに即したやり方がありそう)
またなんらかの発見があれば共有したいと思います

間違い、アドバイスなどあれば是非コメントお願いします!