FlutterのNavigatorイケてないなと思って探していたらroutemasterという良さげなライブラリ見つけたのでメモも兼ねて紹介します。
routemasterの特徴
routemasterはFlutterのNavigator2.0をラッピングしたライブラリで以下の特徴があります。
- URLとページのマッピングをシンプルに定義できる
- 使いやすくシンプルなAPI
(例:routemaster.push('/page') - ネストされたタブにも、非常に簡単なやり方で対応できる
- 複数のルートマッピングを定義できる
(ログイン済みとログアウト済みのユーザを分けたり - Observerを設定し、ルートの変化を簡単に聞き取ることができる
- 160個以上の Unit/Widget/Integrationテストで動作検証済み
インストール
flutter pub add routemaster
セットアップ
MaterialAppのrouterDelegateに設定する
- contextありの設定
MaterialApp.router(
routerDelegate: RoutemasterDelegate(
routesBuilder: (context) => RouteMap(routes: {
'/': (routeData) => MaterialPage(child: PageOne()),
'/two': (routeData) => MaterialPage(child: PageTwo()),
}),
),
routeInformationParser: RoutemasterParser(),
)
- contextなしの設定
final routeMap = RouteMap(
routes: {
'/': (route) => MaterialPage(child: PageOne()),
'/two': (route) => MaterialPage(child: PageTwo()),
},
);
final routemaster = RoutemasterDelegate(
routesBuilder: (context) => routeMap,
);
MaterialApp.router(
routerDelegate: routemaster,
routeInformationParser: RoutemasterParser(),
)
遷移させる
あとは簡単遷移させるだけ
/// 特定のパスに遷移する
Routemaster.of(context).push('/two')
/// 前のパスに戻る
Routemaster.of(context).pop
Routeにパラメータを持たせる
Path Parameterの場合
// Routemaster.of(context).push('products/123')で123がパラメータとして渡る
RouteMap(routes: {
'/products/:id': (route) => MaterialPage(
child: ProductPage(id: route.pathParameters['id']),
),
'/products/myPage': (route) => MaterialPage(MyPage()),
})
Query Parameterの場合
// Routemaster.of(context).push('/search?query=hello')でhelloがパラメータとして渡る
RouteMap(routes: {
'/search': (route) => MaterialPage(
child: SearchPage(query: route.queryParameters['query']),
),
})
現在のパス情報の取得
// フルパスの'/product/123?query=param'が取得できる
RouteData.of(context).path;
// Path ParameterのKeyとValueがMapで取得できる → Map: {'id': '123'}
RouteData.of(context).pathParameters;
// Query ParameterのKeyとValueがMapで取得できる → Map: {'query': 'param'}
RouteData.of(context).queryParameters;
Navigationの監視
RoutemasterObserverを継承したクラスを作り、RoutemasterDelegateに設定するだけ
class MyObserver extends RoutemasterObserver {
@override
void didPop(Route route, Route? previousRoute) {
print('ルートが戻ったよ');
}
@override
void didChangeRoute(RouteData routeData, Page page) {
print('新しいルートだよ: ${routeData.path}');
}
}
MaterialApp.router(
routerDelegate: RoutemasterDelegate(
observers: [MyObserver()],
routesBuilder: (_) => routeMap,
),
routeInformationParser: RoutemasterParser(),
);
Routeガード
条件によってRouteの出し分けが可能です。
条件に一致しない場合、デフォルトのnot foundページを表示する。
'/protected-route': (route) =>
canUserAccessPage()
? MaterialPage(child: ProtectedPage())
: NotFound()
条件に一致しない場合別のURLにリダイレクトさせる
'/protected-route': (route) =>
canUserAccessPage()
? MaterialPage(child: ProtectedPage())
: Redirect('/no-access'),
条件に一致しない場合別のURLにリダイレクトさせる(URLは変えない)
'/protected-route': (route) =>
canUserAccessPage()
? MaterialPage(child: ProtectedPage())
: MaterialPage(child: CustomNoAccessPage())
404ページ
定義されていないURLの場合にエラーページに遷移させるようにする
RouteMap(
onUnknownRoute: (route, context) {
return MaterialPage(child: NotFoundPage());
},
routes: {
'/': (_) => MaterialPage(child: HomePage()),
},
)
リダイレクト
404以外にもリダイレクトができます。
あるルートを別のルートにリダイレクトさせる
RouteMap(routes: {
'/one': (routeData) => MaterialPage(child: PageOne()),
'/two': (routeData) => Redirect('/one'),
})
定義されていないRouteは全てTopに遷移させる
RouteMap(
onUnknownRoute: (_) => Redirect('/'),
routes: {
'/': (_) => MaterialPage(child: LoginPage()),
},
)
リダイレクト先にパラメータを引き渡す
RouteMap(routes: {
'/user/:id': (routeData) => MaterialPage(child: UserPage(id: id)),
'/profile/:uid': (routeData) => Redirect('/user/:uid'),
})
RouteMapの再構築
アプリ起動後の処理によってRouteMap自体を置き換えることができます。
ログイン前とログイン後でRouteが全くことなる場合など
final loggedOutMap = RouteMap(
onUnknownRoute: (route, context) => Redirect('/'),
routes: {
'/': (_) => MaterialPage(child: LoginPage()),
},
);
final loggedInMap = RouteMap(
routes: {
'/': (_) => MaterialPage(child: HomePage()),
},
);
MaterialApp.router(
routerDelegate: RoutemasterDelegate(
routesBuilder: (context) {
// AppStateの状態によってRouteMapを切り替える
final appState = Provider.of<AppState>(context);
return appState.isLoggedIn ? loggedInMap : loggedOutMap;
},
),
routeInformationParser: RoutemasterParser(),
);
最後に
READMEを記載しただけになってしまいましたが、いかがでしょうか。
必要な機能はすべて揃っているかなと個人的に思います。
全て実装して試したわけではないので、また必要があれば更新します。
ディープリンクに関しても、対応しているようなので別の記事でまとめてみようと思います。