GoRouterで画面遷移。Riverpodのstateに値をセットする方法

こんにちは、株式会社Pentagonの山本です。

GoRouterとRiverpodを使ってアプリ開発をしていましたが、GoRouterでどのようにデータを次の画面に渡すのか、そして渡したデータをどうのようにstateにセットすれば良いのかわからなかったので、その方法をまとめました。

【こんな人に読んで欲しい】

  • 画面遷移時にデータを次の画面に渡したい人

【この記事を読むメリット】

この記事を読めば、GoRouterで画面遷移する際にデータを次の画面に渡す方法、そのデータをstateに格納し扱う方法がわかります。

【結論】

結論から言うと、GoRouterでは、extraを使ってデータを渡し、ConsumerStatefulWidgetのinitStateで、データをstateに格納します。

目次

GoRouterで画面遷移時にデータを渡す

以下では、VideoListScreenからVideoPlayingScreenに画面遷移する場合を想定します。VideoListScreenからVideoPlayingScreenに遷移するときに、再生をするVideoのデータを送るものとします。

まずは、VideoPlayingScreenに画面遷移する時、必要なデータを管理するクラスを作成します。これをVideoPlayingScreenArgsとします。VideoPlayingScreenを初期化する際に、をVideoPlayingScreenArgsを渡せるようにコンストラクタを記述します。

class VideoPlayingScreenArgs {
 VideoPlayingScreenArgs({
   required this.playingVideo,
 });
 final Video playingVideo;
}

class VideoPlayingScreen extends ConsumerStatefulWidget {
 const VideoPlayingScreen({Key? key, this.args}) : super(key: key);

 final VideoPlayingScreenArgs? args;

 @override
 VideoPlayingScreenState createState() => VideoPlayingScreenState();
}

アプリのルーターを定義している箇所で、extraを経由してVideoPlayingScreenArgsを渡せるようにします。

GoRoute(
       path: videoPlaying,
       builder: (context, state) {
         return VideoPlayingScreen(
           args: state.extra as VideoPlayingScreenArgs?,
         );
       },
     ),

次のコードでvideoのインスタンスを画面遷移時にextraとして渡すことができます。

context.push(
AppRouter.videoPlaying,
      extra: VideoPlayingScreenArgs(playingVideo: video)
);

RiverpodでProvider, State, Notifierを用意

状態管理にRiverpodを使うため、Provider, State, Notifierを用意します。

part 'video_playing_screen_model.freezed.dart';

// -----------------------------------------------------------------------------
// Provider
// -----------------------------------------------------------------------------
final videoPlayingScreenModelProvider = NotifierProvider.autoDispose<
   VideoPlayingScreenModelNotifier, VideoPlayingScreenModelState>(() {
 return VideoPlayingScreenModelNotifier();
});

// -----------------------------------------------------------------------------
// State
// -----------------------------------------------------------------------------
@freezed
class VideoPlayingScreenModelState with _$VideoPlayingScreenModelState {
 const factory VideoPlayingScreenModelState({
   Video? playingVideo,
 }) = _VideoPlayingScreenModelState;
}

// -----------------------------------------------------------------------------
// Notifier
// -----------------------------------------------------------------------------
class VideoPlayingScreenModelNotifier
   extends AutoDisposeNotifier<VideoPlayingScreenModelState> {

 @override
 VideoPlayingScreenModelState build() {
   return const VideoPlayingScreenModelState();
 }

 void setInitialState(
   Video? playingVideo
 ) {
   state = state.copyWith(
     playingVideo: playingVideo
   );
 }
}

ConsumerStatefulWidgetを継承して画面を構築

ConsumerStatefulWidgetを継承し、initState()内で、画面遷移時に受け取ったデータをstateの中に投入していきます。


class VideoPlayingScreenArgs {
 VideoPlayingScreenArgs({
   required this.playingVideo,
 });
 final Video playingVideo;
}

class VideoPlayingScreen extends ConsumerStatefulWidget {
 const VideoPlayingScreen({Key? key, this.args}) : super(key: key);

 final VideoPlayingScreenArgs? args;

 @override
 VideoPlayingScreenState createState() => VideoPlayingScreenState();
}

class VideoPlayingScreenState extends ConsumerState<VideoPlayingScreen> {
 @override
 void initState() {
   super.initState();
   Future(
     () {
       ref.read(videoPlayingScreenModelProvider.notifier).setInitialState(
             widget.args?.playingVideo
           );
     },
   );
 }

 @override
 Widget build(BuildContext context) {
   return Container();
 }
}

stateにデータを反映する際に、Futureで囲まないと、次のようなエラーが発生するので、気をつけてください。

まとめ

以上、GoRouterでデータを次の画面に渡し、Riverpodを使ってstateに投入し管理する方法を紹介しました。GoRouterでintを渡す例などはありましたが、extraを使って複雑なデータを渡すサンプルが少なかったので、本記事を書きました。Flutterでの開発のヒントになれば幸いです。

採用情報はこちら
目次