Flutterのページ遷移には、go_routerを使ってるけど、
「ページが変わったらなにかしたい」みたいなのができないかなと、
いろいろ調べてみたときの備忘録(*´ω`*)
以下のIssueのコメントによいワークアラウンドがのっていた
試したときのバージョンは以下の通り
- go_router: 12.1.3
- flutter_riverpod: 2.5.1
- hooks_riverpod: 2.5.1
やったこと
現在のパスが変更したら保存する
router.routerDelegate.addListener()
を使うのが、
一番よいっぽい動きをするので、こんな感じに。
Riverpodとflutter_hooksを使った書き方。
import 'dart:async'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:go_router/go_router.dart'; FutureOr<void> main() async { runApp(ProviderScope(child: const App())); } class App extends HookConsumerWidget { const App({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final router = GoRouter(/* ... */); // GoRouterのcurrent routeが変わったときの処理 void handleRouteChange() { // UI描画中だとエラーになるので、描画後に処理 WidgetsBinding.instance.addPostFrameCallback((_) { // 現在のRouteMatchListを取得 final config = router.routerDelegate.currentConfiguration; // RouteMatchListから各種情報を取得 final path = config.last.matchedLocation; // stack最後=現在のパス final pathParams = config.pathParameters; final queryParams = config.uri.queryParameters; // TODO: 取得した情報を保存する }); } useEffect(() { // 初期化時にリスナーへ追加 router.routerDelegate.addListener(handleRouteChange); return () { // dispose時にリスナーから削除 router.routerDelegate.removeListener(handleRouteChange); }; }, const []); return MaterialApp.router( // ... restorationScopeId: 'router', routerDelegate: router.routerDelegate, routeInformationProvider: router.routeInformationProvider, routeInformationParser: router.routeInformationParser, ); } }
pathは正確だけど、pathParams
やqueryParams
は微妙な感じっぽい。。
うまく取得できていなかったり、空だったりする。。
現在のパスを保持するProvider
取得した情報を保持するProviderはこんな感じ。
// current_route.dart import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'current_route.freezed.dart'; part 'current_route.g.dart'; typedef RouteParams = Map<String, String>; @freezed class RouteInfo with _$RouteInfo { const RouteInfo._(); const factory RouteInfo({ required String path, Map<String, String>? pathParams, Map<String, String>? queryParams, }) = _RouteInfo; } @Riverpod(keepAlive: true) class CurrentRoute extends _$CurrentRoute { @override RouteInfo build() { return RouteInfo(path: "/"); } void setCurrent( String path, { Map<String, String>? params, Map<String, String>? query, }) { state = state.copyWith( path: path, pathParams: params, queryParams: query, ); } }
CurrentRouteをwatch/listenしてなにかする
あとは、currentRouteProvider
を使えばOK
ref.listen(currentRouteProvider, (previous, next) { // TODO: pathに応じて好きな処理をする });
ほかに試したこと
よく見るこの辺りを試したけど、いろいろissueが上がっていたり、
ほしい情報がうまくとれなかったりで、だめだった。。
- GoRouterRedirect内で取得
- push/goのみで、popだと反応しない
- NavigatorObserver
- go_router周りのページ遷移が発火しない? ダイアログのpopなどのみ動作
以上!! これでだいぶ楽になるぞ。。。(*´ω`*)