Flutterの画面遷移について考えたのでメモ。
現状このようにして遷移している。
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => HogeScreen(keyword: "..."),
));
このやり方だと settings.name
が設定されていないので、FirebaseAnalyticsにログを送るときになんの画面なのか判別できないためログが送られない
settings.name
を設定する
FirebaseAnalyticsObserverを利用して画面遷移のログを送信したい場合、settings.name
がnullだとログが送られない。
FirebaseAnalyticsObserver class - observer library - Dart API
ログを送るためには以下のようにsettings.name
を設定するか、pushNamed を使う.
Navigator.of(context).push(MaterialPageRoute(
settings: RouteSettings(name: "HogeScreen"),
builder: (_) => HogeScreen(keyword: "..."),
))
pushNamed を使う
pushNamed は以下のように使い、画面遷移のコードを何度も書く必要がないというメリットもある。
pushNamed method - Navigator class - widgets library - Dart API
Navigator.of(context).pushNamed("HogeScreen", arguments: "...")
しかし、pushNamedには以下のような問題もあると思った。
- route name を String で渡しそれを利用してRoute(またはWidget)を作るので、うっかりミスが起きやすい
settings.arguments
経由で値を渡すことになるので、型がObjectになり利用するときにcastする必要がありうっかりミスが起きやすい
- route nameだけでは arguments が必要かどうかわからないので、うっかりミスが起きやすい
人間のうっかりミスが起きやすく、コンパイルエラーではなく実行時エラーになるため最悪気づかないことがある。
route メソッドをつくる
pushNamedは画面遷移のコードを何度も書く必要がないという点では便利だけど、うっかりミスも起きやすいと感じた。
それを防ぐためにはこのようにRouteを生成するメソッドを書いてあげればよいのではと考えた。
extension HogeScreenRoutes on HogeScreen {
static Route<dynamic> route({@required this.keyword})) {
return MaterialPageRoute(
settings: RouteSettings(name: "HogeScreen"),
builder: (_) => HogeScreen(keyword: "..."),
)
}
}
Navigator.of(context).push(HogeScreen.route());
こうすればうっかりタイポなどをしても実行したタイミングでエラーになるのではなくコンパイルのタイミングでわかるので、最高。
ただし、これにもうっかりミスが起きる場所がある。HogeScreenに渡す値が増えたときだ。
class HogeScreen {
final String keyword;
final String fuga;
HogeScreen({Key key, @required this.keyword, @reuqired this.fuga});
}
extension HogeScreenRoutes on HogeScreen {
static Route<dynamic> route({@required String keyword})) {
return MaterialPageRoute(
settings: RouteSettings(name: "HogeScreen"),
builder: (_) => HogeScreen(keyword: keyword),
)
}
}
@required
をしていてもコンパイルエラーにならない。lintを動かせばエラーにすることはできる。
複数箇所変更するのが嫌ならコンストラクタからrouteメソッドを自動生成すればいいかなと思ったけどそこまでやらなくていいかなと思ってここで考えるのをやめた。
もっといい方法があるなら知りたい。
追記:
こうやれば一応一箇所の変更ですむねって話を同僚としたけど、やりすぎ感あるのでやめておいた。
class HogeScreenArguments {
final String keyword;
final String fuga;
HogeScreenArguments({Key key, @required this.keyword, @required this.fuga});
}
class HogeScreen {
final HogeScreenArguments arguments;
HogeScreen({Key key, @required this.arguments});
}
extension HogeScreenRoutes on HogeScreen {
static Route<dynamic> route({@required HogeScreenArguments arguments})) {
return MaterialPageRoute(
settings: RouteSettings(name: "HogeScreen"),
builder: (_) => HogeScreen(arguments: arguments),
)
}
}