前言
在做 Flutter
開發(fā)的時候所有的頁面以及頁面上的元素都變成了 Widget
,創(chuàng)建一個頁面或者視圖直接 new
一個新的 widget
就可以,相關(guān)的參數(shù)我們可以直接通過構(gòu)造函數(shù)直接傳遞。
我們做 Android
開發(fā)的人員都知道 Android
應(yīng)用程序在進行頁面跳轉(zhuǎn)的時候可以利用Intent進行參數(shù)傳遞,那么再開發(fā) Flutter
的時候有類似的方式可以進行參數(shù)傳遞么?答案當(dāng)然是有。
Flutter中文網(wǎng) 中有一段話,大多數(shù)應(yīng)用程序包含多個頁面。例如,我們可能有一個顯示產(chǎn)品的頁面,然后,用戶可以點擊產(chǎn)品,跳到該產(chǎn)品的詳情頁。
在Android中,頁面對應(yīng)的是Activity,在iOS中是ViewController。而在Flutter中,頁面只是一個widget!
在Flutter中,我們那么我們可以使用Navigator
在頁面之間跳轉(zhuǎn)。
所以我們下邊講述 widget
的參數(shù)傳遞,從簡單到簡便:
widget構(gòu)造參數(shù)傳遞
route參數(shù)傳遞
上面兩種方式進混合(onGenerateRoute)
widget構(gòu)造參數(shù)傳遞
class Page extends StatelessWidget{
Page({this.arguments});
final Map arguments;
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Text("this page name is ${arguments != null ? arguments['name'] : 'null'}"),
),
);
}
}
上面是一個簡單的 Flutter
的視圖組件,我們在使用參數(shù) arguments
的時候只需要將其傳入到 Page({this.arguments})
的構(gòu)造函數(shù)中。
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Page(arguments: {"name": 'Flutter Demo Home Page'}),
);
}
}
這種方式進行的參數(shù)傳遞只能單向往下一個頁面?zhèn)鬟f,不能像Android的 setResult
一樣往上一級頁面?zhèn)鬟f數(shù)據(jù)。
Route
在講 Route
傳參的時候,我們先講講 Flutter
中 Route
相關(guān)的知識點。
路由( Route
)在移動開發(fā)中通常指頁面( Page
),這跟 web
開發(fā)中單頁應(yīng)用的 Route
概念意義是相同的,Route
在Android
中通常指一個 Activity
,在 iOS
中指一個 ViewController
。所謂路由管理,就是管理頁面之間如何跳轉(zhuǎn),通常也可被稱為導(dǎo)航管理。這和原生開發(fā)類似,無論是 Android
還是 iOS
,導(dǎo)航管理都會維護一個路由棧,路由入棧( push
)操作對應(yīng)打開一個新頁面,路由出棧( pop
)操作對應(yīng)頁面關(guān)閉操作,而路由管理主要是指如何來管理路由棧。
MaterialPageRoute
MaterialPageRoute
是我們使用最為廣泛的路由類,它繼承自 PageRoute
類, PageRoute
類是一個抽象類繼承抽象類 ModalRoute
,下面我們介紹一下 MaterialPageRoute
構(gòu)造函數(shù)的各個參數(shù)的意義:
MaterialPageRoute({
@required this.builder,
RouteSettings settings,
this.maintainState = true,
bool fullscreenDialog = false,
}) : assert(builder != null),
assert(maintainState != null),
assert(fullscreenDialog != null),
assert(opaque),
super(settings: settings, fullscreenDialog: fullscreenDialog);
-
builder
是一個WidgetBuilder類型的回調(diào)函數(shù),它的作用是構(gòu)建路由頁面的具體內(nèi)容,返回值是一個widget。我們通常要實現(xiàn)此回調(diào),返回新路由的實例。 -
settings
包含路由的配置信息,如路由名稱、路由參數(shù)、是否初始路由(首頁)。 -
maintainState
:默認情況下,當(dāng)入棧一個新路由時,原來的路由仍然會被保存在內(nèi)存中,如果想在路由沒用的時候釋放其所占用的所有資源,可以設(shè)置maintainState
為false。 -
fullscreenDialog
表示新的路由頁面是否是一個全屏的模態(tài)對話框,在iOS中,如果fullscreenDialog
為true
,新頁面將會從屏幕底部滑入(而不是水平方向)。
如果想自定義路由切換動畫,可以自己繼承PageRoute來實現(xiàn),我們將在后面介紹動畫時,實現(xiàn)一個自定義的路由Widget。
命名路由
所謂命名路由(Named Route)即給路由起一個名字,然后可以通過路由名字直接打開新的路由。這為路由管理帶來了一種直觀、簡單的方式。和 Android
中的 ARrouter
頁面跳轉(zhuǎn)框架所定義的 path
非常的類似。
路由表
要想使用命名路由,我們必須先提供并注冊一個路由表(routing table),這樣應(yīng)用程序才知道哪個名稱與哪個路由Widget對應(yīng)。路由表的定義是一個 Map<String, WidgetBuilder>
結(jié)構(gòu)的 Map
, key 為路由的名稱,是個字符串;value是個builder回調(diào)函數(shù),用于生成相應(yīng)的路由Widget。我們在通過路由名稱入棧新路由時,應(yīng)用會根據(jù)路由名稱在路由表中找到對應(yīng)的WidgetBuilder回調(diào)函數(shù),然后調(diào)用該回調(diào)函數(shù)生成路由widget并返回。
我們在創(chuàng)建 MaterialApp
的時候就有一個 routes
構(gòu)造參數(shù):
const MaterialApp({
Key key,
this.navigatorKey,
this.home,
this.routes = const <String, WidgetBuilder>{},
this.initialRoute,
this.onGenerateRoute,
this.onUnknownRoute,
this.navigatorObservers = const <NavigatorObserver>[],
/***********/
})
Navigator
Navigator
是一個路由管理的widget,它通過一個棧來管理一個路由widget集合。通常當(dāng)前屏幕顯示的頁面就是棧頂?shù)穆酚伞?code>Navigator提供了一系列方法來管理路由棧,我們主要使用 push
和 pop
連個操作進行頁面的入棧和出棧。
push
將給定的路由入棧(即打開新的頁面),返回值是一個Future
對象,用以接收新路由出棧(即關(guān)閉)時的返回數(shù)據(jù)。
push
我們主要使用兩個方法一個是直接 push
一個路由,另外一個是 pushNamed
一個命名路由地址(PS:要想使用命名路由必須提供并注冊一個路由表,這后面會講到)。
push方法源碼
下邊是 Navigator.push
的源碼,入?yún)⒌?Route
對象中有一個 RouteSettings
成員變量,我們可以在構(gòu)造 Route
對象的時候?qū)⑿枰獋鬟f的參數(shù)放在 RouteSettings
中。
@optionalTypeArgs
static Future<T> push<T extends Object>(BuildContext context, Route<T> route) {
return Navigator.of(context).push(route);
}
push方法使用
我們可以將參數(shù)放在 SecondScreen
的構(gòu)造函數(shù)中,也可以放在構(gòu)造的 MaterialPageRoute
的 RouteSettings
中。
Navigator.push(
context,
new MaterialPageRoute(builder: (context) => new SecondScreen()),
).then((data){
//接受返回的參數(shù)
print(data.toString());
};
pushNamed方法源碼
第二種方式最終的實現(xiàn)也是調(diào)用的 push
方法,這中方法直接暴露了參數(shù) Object arguments
。
@optionalTypeArgs
static Future<T> pushNamed<T extends Object>(
BuildContext context,
String routeName, {
Object arguments,
}) {
return Navigator.of(context).pushNamed<T>(routeName, arguments: arguments);
}
@optionalTypeArgs
Future<T> pushNamed<T extends Object>(
String routeName, {
Object arguments,
}) {
return push<T>(_routeNamed<T>(routeName, arguments: arguments));
}
pushNamed方法使用
使用前提是 /route1
已經(jīng)被注冊到路由表中:
Navigator.of(context)
.pushNamed(
'/route1',
arguments: {
"name": 'hello'
}
).then((data){
//接受返回的參數(shù)
print(data.toString());
};
pop
將棧頂路由出棧,入?yún)橐粋€ object
類型的對象為當(dāng)前頁面關(guān)閉時返回給上一個頁面的數(shù)據(jù)。
@optionalTypeArgs
static bool pop<T extends Object>(BuildContext context, [ T result ]) {
return Navigator.of(context).pop<T>(result);
}
使用非常簡單:
Navigator.of(context).pop("ok~!");
頁面參數(shù)的傳輸、獲取以及結(jié)果返回
參數(shù)傳輸
Navigator.of(context).pushNamed('/route1', arguments: {"name": 'hello'});
參數(shù)獲取
class Page extends StatelessWidget{
String name;
@override
Widget build(BuildContext context) {
dynamic obj = ModalRoute.of(context).settings.arguments;
if (obj != null && isNotEmpty(obj["name"])) {
name = obj["name"];
}
return Material(
child: Center(
child: Text("this page name is ${name}"),
),
);
}
}
參數(shù)返回
//頁面返回參數(shù)
Navigator.of(context).pop("ok~!");
//上一個頁面接收參數(shù)
Navigator.of(context)
.pushNamed(
'/route1',
arguments: {
"name": 'hello'
}
).then((data){
//接受返回的參數(shù)
print(data.toString());
};
onGenerateRoute構(gòu)建路由
在說 onGenerateRoute
構(gòu)建路由之前,我們得先了解他。前面 MaterialApp
的的構(gòu)造函數(shù)中我們看到過它出現(xiàn), MaterialApp
有一個參數(shù)類型為 Function
類型的 onGenerateRoute
。
void main() => runApp(MyApp());
Map<String, WidgetBuilder> routers = {'/route1': (context, {arguments}) => Page(arguments: arguments)}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
// 處理Named頁面跳轉(zhuǎn) 傳遞參數(shù)
onGenerateRoute: (RouteSettings settings) {
// 統(tǒng)一處理
final String name = settings.name;
final Function pageContentBuilder = routers[name];
if (pageContentBuilder != null) {
final Route route =
MaterialPageRoute(
builder: (context) {
//將RouteSettings中的arguments參數(shù)取出來,通過構(gòu)造函數(shù)傳入
return pageContentBuilder(context, arguments: settings.arguments);
},
settings: settings,
);
return route;
}
},
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(name: 'Flutter Demo Home Page'),
//routes優(yōu)先執(zhí)行,所以必須注釋掉,否則onGenerateRoute方法不會調(diào)用
//routes: routers,
);
}
}
class Page extends StatelessWidget{
Page({this.arguments});
final Map arguments;
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Text("this page name is ${arguments != null ? arguments['name'] : 'null'}"),
),
);
}
}
- 這種方式統(tǒng)一處理了頁面的
arguments
參數(shù),所以必須保證Map<String, WidgetBuilder> routers
當(dāng)中注冊的所有Widget
的構(gòu)造函數(shù)中都有一個Map
類型并且名為arguments
的參數(shù)。- 這種方法同時也傳遞了
RouteSettings
,所以在下一個頁面我們也可以通過ModalRoute.of(context).settings.arguments
方式獲取參數(shù)。
- 這種方式可以自定義
PageRoute
的類型,比如自帶IOS
側(cè)滑返回效果的CupertinoPageRoute
等。之前通過在WidgetsApp
注冊routes
的方式默認生成的PageRoute
類型為MaterialPageRoute
。
源碼分析傳送門:Flutter路由管理和頁面參數(shù)的傳遞(源碼分析)
文章到這里就全部講述完啦,若有其他需要交流的可以留言哦!!