前言
在做 Flutter
開發(fā)的時候所有的頁面以及頁面上的元素都變成了 Widget
五续,創(chuàng)建一個頁面或者視圖直接 new
一個新的 widget
就可以洞难,相關的參數(shù)我們可以直接通過構(gòu)造函數(shù)直接傳遞赘那。
我們做 Android
開發(fā)的人員都知道 Android
應用程序在進行頁面跳轉(zhuǎn)的時候可以利用Intent進行參數(shù)傳遞,那么再開發(fā) Flutter
的時候有類似的方式可以進行參數(shù)傳遞么翔曲?答案當然是有抖韩。
Flutter中文網(wǎng) 中有一段話,大多數(shù)應用程序包含多個頁面敌土。例如镜硕,我們可能有一個顯示產(chǎn)品的頁面,然后返干,用戶可以點擊產(chǎn)品兴枯,跳到該產(chǎn)品的詳情頁。
在Android中矩欠,頁面對應的是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
相關的知識點音榜。
路由( Route
)在移動開發(fā)中通常指頁面( Page
),這跟 web
開發(fā)中單頁應用的 Route
概念意義是相同的捧弃,Route
在Android
中通常指一個 Activity
赠叼,在 iOS
中指一個 ViewController
。所謂路由管理违霞,就是管理頁面之間如何跳轉(zhuǎn)嘴办,通常也可被稱為導航管理。這和原生開發(fā)類似买鸽,無論是 Android
還是 iOS
涧郊,導航管理都會維護一個路由棧,路由入棧( push
)操作對應打開一個新頁面眼五,路由出棧( pop
)操作對應頁面關閉操作妆艘,而路由管理主要是指如何來管理路由棧彤灶。
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
:默認情況下,當入棧一個新路由時失暴,原來的路由仍然會被保存在內(nèi)存中坯门,如果想在路由沒用的時候釋放其所占用的所有資源,可以設置maintainState
為false逗扒。 -
fullscreenDialog
表示新的路由頁面是否是一個全屏的模態(tài)對話框古戴,在iOS中,如果fullscreenDialog
為true
矩肩,新頁面將會從屏幕底部滑入(而不是水平方向)现恼。
如果想自定義路由切換動畫,可以自己繼承PageRoute來實現(xiàn)黍檩,我們將在后面介紹動畫時叉袍,實現(xiàn)一個自定義的路由Widget。
命名路由
所謂命名路由(Named Route)即給路由起一個名字刽酱,然后可以通過路由名字直接打開新的路由喳逛。這為路由管理帶來了一種直觀、簡單的方式棵里。和 Android
中的 ARrouter
頁面跳轉(zhuǎn)框架所定義的 path
非常的類似润文。
路由表
要想使用命名路由,我們必須先提供并注冊一個路由表(routing table)殿怜,這樣應用程序才知道哪個名稱與哪個路由Widget對應典蝌。路由表的定義是一個 Map<String, WidgetBuilder>
結(jié)構(gòu)的 Map
, key 為路由的名稱头谜,是個字符串骏掀;value是個builder回調(diào)函數(shù),用于生成相應的路由Widget。我們在通過路由名稱入棧新路由時截驮,應用會根據(jù)路由名稱在路由表中找到對應的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集合。通常當前屏幕顯示的頁面就是棧頂?shù)穆酚伞?code>Navigator提供了一系列方法來管理路由棧甲脏,我們主要使用 push
和 pop
連個操作進行頁面的入棧和出棧眶熬。
push
將給定的路由入棧(即打開新的頁面),返回值是一個Future
對象块请,用以接收新路由出棧(即關閉)時的返回數(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
類型的對象為當前頁面關閉時返回給上一個頁面的數(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
當中注冊的所有Widget
的構(gòu)造函數(shù)中都有一個Map
類型并且名為arguments
的參數(shù)。- 這種方法同時也傳遞了
RouteSettings
囚衔,所以在下一個頁面我們也可以通過ModalRoute.of(context).settings.arguments
方式獲取參數(shù)挖腰。
- 這種方式可以自定義
PageRoute
的類型,比如自帶IOS
側(cè)滑返回效果的CupertinoPageRoute
等练湿。之前通過在WidgetsApp
注冊routes
的方式默認生成的PageRoute
類型為MaterialPageRoute
猴仑。
源碼分析傳送門:Flutter路由管理和頁面參數(shù)的傳遞(源碼分析)
文章到這里就全部講述完啦,若有其他需要交流的可以留言哦!辽俗!