Page に route() メソッド用意する設計について
以下の DartPad のリンクでサンプルコード動かせます。
チームでも話題になってたこともあり、ashdik(朝日) さんの「【Flutter】もうnamedRouteは使わない!僕が全力で勧めたいルーティング方法をサンプル付きで解説してみた」を読んだ。
ページを追加するたびに、routeNameを定義して、
app.dartに移動して、routesに追加して…とやるのが大変億劫でした。
理に適ってるなと思った。
ただ、個人的には namedRoute で嫌なのは、Flutter の Navigator の仕組み上 arguments を Object 型で渡さないといけないことだったから、そこだけいい感じに変えたいと思った。
arguments で渡すような「ページの引数」にあたる情報は、route メソッドの引数として受けて直接ページクラスのコンストラクタに渡せば良さそう。
class NextPage extends StatelessWidget {
NextPage._({
this.index,
Key key,
}) : super(key: key);
// フィールドとして持っとく。
final int index;
static PageRoute<void> route({
int index,
}) {
return MaterialPageRoute(
builder: (ctx) => NextPage._(
index: index,
),
);
}
Widget build(BuildContext context) {
...(省略)
}
}
呼び出し側はこう
Navigator.of(context).push(
NextPage.route(index: 0),
);
課題感としては、記事中で言うと以下のコードの someId
が Object 型になっちゃうところでした。
final someId = ModalRoute.of(context).settings.arguments;
(おぉ、スクラップと言う使い方が!)
この課題感めっちゃ分かります、
それもこの書き方を辞めたかった理由の一つなので👍
そして、メンバ変数として持つの全く問題ないと思ってますし
なんだったらこれ全く同じこと僕もやってます👍
良かったです👍
(スクラップの使い方わかってないですけど、意見もらいたい時スクラップいいかも)
あとは、firebase_analytics の FirebaseAnalyticsObserver にスクリーン名をログ取ってもらうために、RouteSettings も定義してあげればいいかな。
class NextPage extends StatelessWidget {
static PageRoute<void> route({
int index,
}) {
return MaterialPageRoute(
builder: (ctx) => NextPage._(
index: index,
),
settings: RouteSettings(name: '/next'), // <= It's for Navigator#observers
);
}
Widget build(BuildContext context) {
...(省略)
}
}
フルコードはこんな感じ。
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
/// Using [Builder] for sample code.
home: Builder(
builder: (context) {
return Scaffold(
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.of(context).push(
NextPage.route(index: 0),
);
},
child: Text('Navigate to next page'),
),
),
);
},
),
);
}
}
class NextPage extends StatelessWidget {
NextPage._({
this.index,
Key key,
}) : super(key: key);
final int index;
static PageRoute<void> route({
int index,
}) {
return MaterialPageRoute(
builder: (ctx) => NextPage._(
index: index,
),
settings: RouteSettings(name: '/next'),
);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('No.$index'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('No.$index'),
RaisedButton(
onPressed: () {
Navigator.of(context).push(
NextPage.route(index: index + 1),
);
},
child: Text('Navigate to next page'),
),
],
),
),
);
}
}
kikuchy さんにフィードバックもらった。
確かに、運用上 screenName 忘れて Firebase Analytics の不整合が起きるの面倒そう。
ただ、同ページに複数の route メソッド用意したいケースもある気がするので、自動生成で対応するの面倒かも。
今思いつくのは、PageRoute 作成用の factory を用意して、screenName を required パラメータにすることくらいかな。
class PageRouteBuilder {
static MaterialPageRoute<T> buildMaterialRoute<T>({
WidgetBuilder builder,
String name,
bool fullscreenDialog = false,
}) {
return MaterialPageRoute<T>(
builder: builder,
settings: RouteSettings(name: name),
fullscreenDialog: fullscreenDialog,
);
}
}