到目前為止,範例都只有一個頁面,對於有著一定功能性的 App 來說,往往不會只有一個頁面,如果你希望有多個頁面,就目前學過的東西來說,一個直覺的想法就是,將 Widget
樹中代表各頁面的 child
置換掉,或者是用 Stack
之類的排版來實現。
基本上這行得通,只不過會有許多細節必須得親自處理,像是被換掉的頁面該怎麼管理?頁面出現的順序?頁面狀態的維護?頁面與頁面之間的訊息溝通等。
在 Flutter 中,涉及頁面間轉換的職責,是由 Navigator
處理,它使用堆疊來管理 Route
,你可以將 Route
實例置入(push)Navigator
的堆疊中,或者將堆疊中的 Route
彈出(pop)。
Route
是什麼呢?如方才談到,技術上來說,Route
是 Navigator
可在堆疊中管理的實例,抽象層面上而言,會讓人聯想到許多 Web 框架中路由表之類的東西,就這方面而言,「路由代表某個資源的銜接」的概念是類似的。
雖然〈Navigate to a new screen and back〉中談到:
In Flutter, screens and pages are called routes. The remainder of this recipe refers to routes.
確實地,在 Flutter 上 Route
銜接的資源,通常是某個頁面,不過 Route
可不是 Widget
,舉個例子好了,稍後會用到的 MaterialPageRoute
,被置入 Navigator
的堆疊後,最後雖然會使用 builder
指定的 Widget
來 build
出可全螢幕呈現的畫面元件,不過 MaterialPageRoute
銜接的資源除了畫面元件之外,還包含了原生平台相應的轉場動畫的效果。
來看個實例,如果想透過 Navigator
與 MaterialPageRoute
,在 Android 呈現出底下的換頁效果:
圖片使用的部份,可參考〈Assets 管理〉,先來看範例程式碼:
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(
title: 'Openhome',
home: MainPage(),
));
class MainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('主畫面'),
),
body: GestureDetector(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (_) => DetailPage())
);
},
child: Image.asset('images/caterpillar.png'),
),
);
}
}
class DetailPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () => Navigator.pop(context),
child: Center(
child: Image.asset('images/caterpillar.png'),
),
),
);
}
}
範例中使用了 GestureDetector
,這是個可用來處理子元件發生觸控手勢相關事件,tap
事件就是用手指點一下(類似按鈕的 click
),在主頁面圖片的 tap
事件發生時,Navigator.push
將指定的 Route
置入堆疊,也可以使用 Navigator.of(context).push(MaterialPageRoute(builder: (_) => DetailPage()))
,因為 push
的原始碼是:
static Future<T> push<T extends Object>(BuildContext context, Route<T> route) {
return Navigator.of(context).push(route);
}
簡單來說,一開始堆疊是這樣的:
Navigator
的堆疊中最底部的路由,代表的資源是 MaterialApp
的 home
指定的 Widget
,在 tap
事件發生時,MaterialPageRoute
被置入堆疊:
在堆疊頂端的路由,代表著會用於螢幕顯示的相關資源,就 MaterialPageRoute
來說,builder
指定的函式,會用來建立最後要的顯示的畫面,在範例中指定的是 DetailPage
,因此在轉場動畫之後,看到的就是只有置中顯示的圖片了,這時若又按下圖片,Navigator.pop
會將 MaterialPageRoute
從堆疊中彈出,你也可以寫 Navigator.of(context).pop()
,因為原始碼 Navigator.pop
是:
static void pop<T extends Object>(BuildContext context, [ T result ]) {
Navigator.of(context).pop<T>(result);
}
其中 result
是用來傳遞頁面結果時使用,之後會談到。在彈出 MaterialPageRoute
後,堆疊中就只剩代表 MainPage
的 Route
實例,也就相當於回到主畫面了。