Flutter 之Router 頁面跳轉
頁面跳轉在移動開發中是很常見的事情,在Android中打開另外一個頁面主要是用startActivity這個方法,在Flutter中也是提供這種能力,主要的使用方式就是通過Navigator 去打開一個頁面
1.跳轉到另外一個頁面
構建FirstScreen和SecondScreen 頁面
import 'package:flutter/material.dart'; class FirstScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("First Screen"), ), body: Center( child: RaisedButton( onPressed: () { Navigator.push(context, MaterialPageRoute(builder: (context) { return SecondScreen(); })); }, child: Text("next screen"), ), ), ); } } class SecondScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Second Screen"), ), body: Center( child: RaisedButton( onPressed: () { Navigator.pop(context); }, child: Text("back"), ), ), ); } }
這里就是跳轉的主要代碼
Navigator.push(context, MaterialPageRoute(builder: (context) { return SecondScreen(); }));
push方式詳解
static Future<T> push<T extends Object>(BuildContext context, Route<T> route)
第一個參數就是上下問信息,類似Android中的Context,第二個參數就是路由信息,也就是要打開的主要頁面是哪個,MaterialPageRoute 就是Route其中的一個子類,用於在Material Desgin 模式下打開頁面的
Navigator.pop(context);
是用來返回上一個頁面的


2.通過routes路徑方式跳轉到下一個頁面
先定義Routes路由表,實際上就是一個Map結構,key是路徑,value就是對應的頁面
import 'package:flutter/material.dart'; import 'navigation/navigation_demo.dart'; void main() { runApp(MaterialApp( initialRoute: "/", routes: { "/": (context) => FirstScreen(), '/second': (context) => SecondScreen(), }, )); }
routes 就是一個map結構,根目錄/對應的頁面就是FirstScreen,/second路徑對應的頁面就是ScendScreen,在FirstScreen中打開SecondScreen的方式我們換一下,要通過Navigator.pushNamed方式打開一個在路由表中已經存在的頁面
class FirstScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("First Screen"), ), body: Center( child: RaisedButton( onPressed: () { Navigator.pushNamed(context, "/second"); // Navigator.push(context, MaterialPageRoute(builder: (context) { // return SecondScreen(); // })); }, child: Text("next screen"), ), ), ); } }
3.傳遞數據到下一個頁面
傳遞數據到下一個頁面也是比較常見的情況,例如說在一個相冊應用中,有一個列表頁面,單擊列表中某一個item,應該跳轉到照片的詳情頁面,其實這種情況就應該把照片的信息傳遞給另外一個頁面
傳遞的方式有兩種:
- 在構造方法中傳遞數據
- 在Route中傳遞數據給下一個頁面
在第一個頁面構造要傳遞的數據
class Photo { String title; String message; Photo({this.title, this.message}); } class FirstScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("First Screen"), ), body: Center( child: RaisedButton( onPressed: () { Navigator.pushNamed(context, "/second", arguments: Photo(title: "pass title",message: "pass message")); // Navigator.push(context, MaterialPageRoute(builder: (context) { // return SecondScreen(); // })); }, child: Text("next screen"), ), ), ); } }
在第二個頁面獲取數據
class SecondScreen extends StatelessWidget { @override Widget build(BuildContext context) { final Photo photo=ModalRoute.of(context).settings.arguments; return Scaffold( appBar: AppBar( title: Text("Second Screen ${photo.title}"), ), body: Center( child: RaisedButton( onPressed: () { Navigator.pop(context); }, child: Text("back ${photo.message}"), ), ), ); } }
這種方式有種不太好的地方就是需要在下一個頁面通過ModalRoute.of(context).settings.arguments; 方式獲取傳遞的數據,其實Flutter中已經提供了這種方式簡便處理方式
import 'package:flutter/material.dart'; import 'navigation/navigation_demo.dart'; void main() { runApp(MaterialApp( home: FirstScreen(), onGenerateRoute: (settings) { if (settings.name == ThreeScreen.routeName) { final Photo args = settings.arguments; return MaterialPageRoute(builder: (context) { return ThreeScreen( title: args.title, message: args.message, ); }); } }, )); }
onGenerateRoute 是用來統一攔截傳遞參數的方法,我們可以在這個地方獲取傳遞的數據,然后在構造頁面的時候把參數傳遞給目標頁面,這樣在目標頁面也就是不用考慮如何解析傳遞過來的數據了
class ThreeScreen extends StatelessWidget { static const routeName = '/extractArguments'; final String title; final String message; ThreeScreen({this.title, this.message}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("second"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text(title), Text(message), ], ), ), ); } }
在這個頁面,數據都是通過構造方法中傳遞了,減少了在頁面獲取傳遞數據的代碼
class FirstScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("First Screen"), ), body: Center( child: RaisedButton( onPressed: () { Navigator.pushNamed(context, ThreeScreen.routeName, arguments: Photo(title: "args title", message: "args message")); }, child: Text("next screen"), ), ), ); } }
發送方式的代碼沒有改變

4.接收頁面返回值
有的時候我們希望在前一個頁面接收另外一個頁面的數據,這個怎么處理呢
class FirstScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("First Screen"), ), body: FirstButton(), ); } } class FirstButton extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: RaisedButton( onPressed: () { Navigator.pushNamed(context, "/second", arguments: Photo(title: "pass title", message: "pass message")) .then((vale) { final snackBar = SnackBar( content: Text('Yay! A SnackBar!'), action: SnackBarAction( label: 'Undo', onPressed: () { }, ), ); Scaffold.of(context).showSnackBar(snackBar); }); }, child: Text("next screen"), ), ); } }
關鍵的代碼是這段,then 方法用來處理接收數據后的處理邏輯,這個例子中主要通過SnackBar 展示一下接收的信息
Navigator.pushNamed(context, "/second", arguments: Photo(title: "pass title", message: "pass message")) .then((vale) { final snackBar = SnackBar( content: Text('Yay! A SnackBar!'), action: SnackBarAction( label: 'Undo', onPressed: () { }, ), ); Scaffold.of(context).showSnackBar(snackBar); });
為什么要單獨抽取出FirstButton組件?
是因為SnackBar只能在Scaffold 組件代碼中使用會報錯
下面代碼是用於在推出當前頁面的時候,處理了ok 給前一個頁面
Navigator.pop(context, "ok");

總結
使用上跟Android 的使用方式類似,有點經驗的人掌握這個不是很難
https://docs.flutter.io/flutter/widgets/Navigator-class.html
https://www.raywenderlich.com/110-flutter-navigation-tutorial
作者:飢餓的大灰狼 來源:簡書