LoginSignup
26

More than 3 years have passed since last update.

[Flutter] NavigatorObserver/RouteObserverを利用した画面遷移の検出

Last updated at Posted at 2020-05-28

1. はじめに

複数画面を遷移するアプリの場合、画面遷移時のアクション (画面が最初に表示された時、別の画面を表示するためにその画面から離れるなど) を定義したい時があります。本記事は、この場合の対応方法をまとめた記事です。

2. そもそもFlutterで画面管理を行っているのは?

Flutterでは、Navigatorが画面遷移 (Pop, Push) をスタックのようなイメージで管理しています。そして、Routeと呼ばれるWidgetを画面に表示しており、NavigatorがこのRouteを切り替えることによって画面遷移しています。

Route関連のソースコードはここにありますので、興味があれば覗いてみてください。

3. 画面遷移の検出方法

やり方は2通りあります。

一つは、これから説明するNatigatorObserverを継承した画面遷移検出用のWidgetを自分用に作成する方法です。もう一つは、各画面 (ただしStatefulWidget) ごとに検出するためのRouteObserverを利用する方法です。

前者は少し面倒なので、StatefulWidgetの場合であれば後者を使ったほうが楽です。

[方法1] NatigatorObserverを利用した画面遷移の検出

Navigatorは、引数としてNavigatorObserverというObserverのリスト (List observers: const []) を指定することができます。このNavigatorObserverを利用することで、各画面の遷移を検出することが可能です。

MaterialAppでの対応
Widget build(BuildContext context) {
  ...
  return MaterialApp(
    title: 'sample',
    home: Page1(),
    routes: {
      '/page1' : (_) => Page1(),
      '/page2' : (_) => Page2(),
      '/page3' : (_) => Page3(),
    },
    navigatorObservers: [
      MyNavigatorObserver(), // <= ここ!
    ],
  );
}

以下のように、NavigatorObserverクラスを継承した自前クラスを定義することで、画面変化イベントを検出出来ます。

MyNavigatorObserverクラス
import 'package:flutter/material.dart';

class MyNavigatorObserver extends NavigatorObserver {
  @override
  void didPush(Route route, Route previousRoute) {
    super.didPush(route, previousRoute);

    var previous = '';
    if (previousRoute == null) {
      previous = 'null';
    }else {
      previous = previousRoute.settings.name;
    }
    print('Current:' + route.settings.name + '  Previous:' + previous);
  }

  @override
  void didPop(Route route, Route previousRoute) {
    super.didPop(route, previousRoute);

    var previous = '';
    if (previousRoute == null) {
      previous = 'null';
    }else {
      previous = previousRoute.settings.name;
    }
    print('Current:' + route.settings.name + '  Previous:' + previous);
  }
}

[方法2] RouteObserverを利用した画面遷移の検出

RouteObserverを利用した方法を紹介します。RouteObserverはRouteのStateの変化を検出し、通知してくれる、NavigatorObserverを継承したクラスです。

まず、RouteObserverのインスタンスをNavigatorObserverのリストに渡します。

MaterialAppでの対応
final RouteObserver<PageRoute> _routeObserver = RouteObserver<PageRoute>(); // <= ここ!

Widget build(BuildContext context) {
  ...
  return MaterialApp(
    title: 'sample',
    home: Page1(),
    routes: {
      '/page1' : (_) => Page1(_routeObserver), // <= ここ! (画面遷移を検出したい対象のWidget)
      '/page2' : (_) => Page2(),
      '/page3' : (_) => Page3(),
    },
    navigatorObservers: [
      _routeObserver, // <= ここ!
    ],
  );
}

次に、RouteAwareクラスを継承しつつ、以下のサンプルコードのように対応していきます。

class Page1 extends StatefulWidget {
  final RouteObserver<PageRoute> routeObserver;
  const Page1(this.routeObserver);

  @override
  _Page1State createState() => _Page1State();
}

class _Page1State extends State<Page1> with RouteAware {
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    widget.routeObserver.subscribe(this, ModalRoute.of(context));
  }

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

  @override
  void didPush() {
    // 画面が初めて表示 (Push) される時にコールされます。
  }

  @override
  void didPop() {
    // この画面から別の画面に遷移する (Pop) 場合にコールされます。
  }

  @override
  void didPushNext() {
    // この画面から別の画面をPushする場合にコールされます (この画面はPopされずにそのまま残る場合)。
  }

  @override
  void didPopNext() {
    // 一度、別の画面に遷移したあとで、再度この画面に戻ってきた時にコールされます。
  }
...
}

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26