TypedShellRouteでスタックごとに、observerを適応する
この記事では、Flutter(Dart) で TypedShellRoute
を使用する際に、GoRouter
のobserver
がうまく追跡できなくなる問題を解決する方法を紹介します。特に、タブごとに異なるNavigator
を使用する場合に発生する問題について詳しく解説します。
こんな人におすすめ
・@TypedShellRouteを使っている
・NavigationKeyが変わった瞬間 Observerが追跡されなくなった
⭐️ NavigationKeyがかわってもObserverの追跡が可能になります。
初めに
一般的な gorouterの observerは以下のような感じでしょうか。
しかし、今回紹介するTypedShellRoute で同じようにobserversを適応すると、navigationkeyが切り替わると、observerの追跡が切れてしまいます。
理由は、TypedShellRouteのように複数のNavigatorスタックをサポートする場合、それぞれのスタックに対応するNavigatorが別々のnavigatorKeyを持つことになります。このため、GoRouterの内部でnavigatorKeyが切り替わると、別のNavigatorがアクティブになり、最初に設定されたobserverが新しいNavigatorを追跡しなくなるという問題が発生するのです。
これを防ぐためには、
💡navigatorKeyが切り替わる際に、observerを新しいNavigatorインスタンスに関連付ける必要があります。
return GoRouter(
navigatorKey: navigationKey,
initialLocation: SplashPage.pagePath,
observers: [transitionObserver],
手順
具体的には以下の手順で実装できます。
また、issueはこちらにあります。
TransitionObserver
クラスを定義する
1. ページ遷移を追跡する enum AppTransitionType {
push,
pop,
}
class TransitionObserver extends NavigatorObserver {
/// ページ遷移を追跡する
static FirebaseAnalytics analytics = FirebaseAnalytics.instance;
// 新しいルートがナビゲータースタックにプッシュされたとき
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
super.didPush(route, previousRoute);
_onTransition(route, previousRoute, AppTransitionType.push);
}
// 現在のルートがナビゲータースタックからポップされたとき
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
super.didPop(route, previousRoute);
_onTransition(route, previousRoute, AppTransitionType.pop);
}
// .... (お好みで didRemoveなど追加)
void _onTransition(
Route<dynamic> route,
Route<dynamic>? previousRoute,
AppTransitionType transitionType,
) async {
/// 表示されたページ名を取得
final pageName = route.settings.name ?? 'unknown';
logger.i('pageName: $pageName, ${transitionType.name}');
await analytics
..logEvent(name: 'page_transition', parameters: {
'page_name': pageName,
'transition_type': transitionType.name,
});
}
}
2. ブランチごとに Observerを仕込む
/// bottom tab1 branch
class FirstBranch extends StatefulShellBranchData {
const FirstBranch();
static final List<NavigatorObserver> $observers = [
TransitionObserver(), → これ
];
static final GlobalKey<NavigatorState> $navigatorKey = firstNavigatorKey;
}
/// bottom tab2 branch
class SecoundBranch extends StatefulShellBranchData {
const SecoundBranch();
static final List<NavigatorObserver> $observers = [
TransitionObserver(), → これ
];
static final GlobalKey<NavigatorState> $navigatorKey = secoundNavigatorKey;
}
上記、issue通りにやったら、きちんとタブが切り替わっても Observerで追跡できました!
まとめ
マーケ側から、見られているページが多いのはどこか情報収集したいといった要望があった場合、対応できるようなりました。これはかなり重要です。
最近は、TypedShellRoute がとっても気に入ってます。
タイプセーフに実装できるのと、タブが切り替わっても前の状態がのこり続けるのが最高ですよね⭐️
ぜひ使ってみてください!
Discussion
最近同じ問題にぶつかったのですが、
StatefullShellBranchを使用している場合もう一つ問題がありまして、
ブランチ間を移動したときに、すでに一度ブランチが生成(インスタンス化)されている場合はobserverがdidPushなどが発火されません。
(ブランチ間の移動なので、pushじゃないのは当たり前ですが、、)
※ブランチ内の移動は記録されます!
analyticsで画面移動を記録したい場合は考慮が必要になるかなと思います。。
コメントありがとうございます😊!
今回は ブランチ間の移動は対象外としてます。