Flutterで画面の遷移のタイミングで処理を行う

[2019/05/13 追記] Issue#29596 は プルリク 30422 で修正されました。 [2019/04/01 追記] #29596 によると、iOSでスワイプで戻ると didPop, didPopNext が呼ばれないようです。


FlutterのWidgetには、画面の遷移に関するイベントを処理するためのハンドラ(メソッド)がありません。これは当然で、Widgetは部品であるため、画面の一部なのか全体なのか、使う側次第だからです。

さて、Flutterにおいて画面遷移に関する処理を行っているのはNavigatorというクラスです。このクラスに対してpushやpopという操作を行います。このNavigatorに対してはNavigatorObserverのリストを渡すことができます。これにより画面遷移に関するイベントを受け取ることができます。

NavigatorObserverを実装したクラスを自分で実装して処理してもよいのですが、それをStateで処理することが目的であれば、RouteObserverというものを使うと便利です。

以下のようにMaterialAppやWidgetsAppにオブザーバーを設定しておきます。

final RouteObserver<PageRoute> routeObserver = new RouteObserver<PageRoute>();

Widget build(BuildContext context) {

...
return new MaterialApp(
...
navigatorObservers: <NavigatorObserver>[routeObserver],
...
);
}

続いて、StateにRouteAwareをmixinして、routeObserverに対してsubscribe/unsubscribeします。

class YourPageWidgetState extends State<YourPageWidget> with RouteAware {

@override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(this, ModalRoute.of(context));
}

@override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}

// 上の画面がpopされて、この画面に戻ったときに呼ばれます
void didPopNext() {
debugPrint("didPopNext ${runtimeType}");
}

// この画面がpushされたときに呼ばれます
void didPush() {
debugPrint("didPush ${runtimeType}");
}

// この画面がpopされたときに呼ばれます
void didPop() {
debugPrint("didPop ${runtimeType}");
}

// この画面から新しい画面をpushしたときに呼ばれます
void didPushNext() {
debugPrint("didPushNext ${runtimeType}");
}
}

新しい画面が呼ばれるときに:


  • 呼び出し元で didPushNext

  • 呼び出し先で disPush

画面を戻るときに:


  • 戻る元で disPop

  • 戻る先で disPopNext

が呼ばれます。

どの画面でも共通の処理があるのであれば、基底クラスとしてRouteAwareStateのようなものを作り、各画面のStateはそれをextendsするようにするとよさそうです。