torikatsu923.dev

サイト内検索

    riverpodでStateNotifierの取得方法が変わった件について

    私はStateNotifier+Riverpod+Freezedを使用してMVVMライクにFlutterアプリを開発しています。

    はじめに

    こんにちは、とりかつ(@torikatsu923)です。

    私はStateNotifier+Riverpod+Freezedを使用してMVVMライクにFlutterアプリを開発しています。 4月の頭にriverpodが0.13.1+1から0.14.0にアップデートされたのですが、このアップデートでは破壊的変更が入っていました。

    この破壊的変更に気づかず、いつものようにRiverpodのproviderからStateNotifierを取得しようとしたところ、うまく取得できずに沼にハマりました。

    この記事を執筆したのは4/19日ですが、どうやら公式docはこの破壊的変更に対応していないようです。 なので、今回はバージョンアップに伴う仕様変更の一部について解説をしたいと思います。

    手っ取り早く確認したい方はpub devのchangelogを参照してください。

    はまったこと

    ここではカウンターアプリを例として取り上げます。

    初めに以下のようなCounterControllerというコントローラがあるとします。 このコントローラはカウンターのカウント数の状態と、カウントを一個増やすincrementというメソッドを持っています。

    counter_controller.dart

    class CounterController extends StateNotifier<int> {
      Counter(): super(0);
    
      /// カウントを一個増やす
      void increment() => state++;
    }
    
    final counterProvider = StateNotifierProvider((ref) => Counter());

    UI側でボタンのコールバックにCounterControllerのincrementを紐付けようと以下のコードを書きました。

    app.dart

    class App extends StatelessWidget {
      @override
      Widget build(BuildContext context, ScopedReader watch) {
        final CounterController counter = context.read(counterProvider);
      
        return RaisedButton(
          onPressed: counter.increment,
          child: Text('increment'),
        );
      }
    }

    一見問題なさそうなコードに見えます。 いままでならこれで問題なく動いたのですが、v0.14.0の破壊的変更によってこの方法は使えなくなりました。

    まず、context.readの戻り値がdynamicになってしまうのでapp.dartの以下の行がコンパイルエラーになってしまいます。

        final CounterController counter = context.read(counterProvider);// return dynamic

    これを解決するためにStateNotifierProviderに型パラメータを設定する必要があります。 1個目の型パラメータにはStateNotifierの型を、2個目の型パラメータにはStateNotifierが保持する型を持たせる必要があります。 修正後のCounterControllerは以下のようになります。

    counter_controller.dart

    class CounterController extends StateNotifier<int> {
      Counter(): super(0);
    
      /// カウントを一個増やす
      void increment() => state++;
    }
    
    /// 型パラメータの追加
    final counterProvider = StateNotifierProvider<CounterController, int>((ref) => Counter());

    型パラメータを追加することでcontext.readの戻り値がdynamicではなくなりました。 しかし、まだコンパイルエラーは消えません。 app.dartの以下のコードではCounterControllerを取得したいのですが、context.readの戻り値はintになってしまいます。

        final CounterController counter = context.read(counterProvider);// return int type

    これではincrementへアクセスすることができません。 これも破壊的変更の影響です。 stateではなくCounterController(StateNotifier)へアクセスしたい場合はnotifierを追加する必要があります。 以下は修正後のコードになります。

        final CounterController counter = context.read(counterProvider.notifier);// return CounterController type

    counterProviderのすぐ後ろにnotifierがついていることがわかると思います。

    これで、無事にボタンとCounterController.incrementを紐づけることができました!

    おわりに

    今回はriverpodの破壊的変更について紹介しました。 もし間違っている部分があればご連絡いただければ幸いです。

    flutterはとても便利なフレームワークですが、新しい分枯れたライブラリが少ないです。 そのため日々、変更を追っていかないと一瞬で置いてけぼりにされてしまうので、注意をしなければと改めて思いました。

    それでは、よい開発ライフを!

    -––