👻

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

2020/10/01に公開

[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 {
  
  void didChangeDependencies() {
    super.didChangeDependencies();
    routeObserver.subscribe(this, ModalRoute.of(context));
  }

  
  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するようにするとよさそうです。

この記事はQiitaの記事をエクスポートしたものです

Discussion