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を利用することで、各画面の遷移を検出することが可能です。
Widget build(BuildContext context) {
...
return MaterialApp(
title: 'sample',
home: Page1(),
routes: {
'/page1' : (_) => Page1(),
'/page2' : (_) => Page2(),
'/page3' : (_) => Page3(),
},
navigatorObservers: [
MyNavigatorObserver(), // <= ここ!
],
);
}
以下のように、NavigatorObserverクラスを継承した自前クラスを定義することで、画面変化イベントを検出出来ます。
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のリストに渡します。
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() {
// 一度、別の画面に遷移したあとで、再度この画面に戻ってきた時にコールされます。
}
...
}