-
概述
普通路由傳遞參數(shù)采用的是硬傳遞的方式:
Navigator.push( context, MaterialPageRoute( builder: (context) { return TipRoute( // 路由參數(shù) text: "我是提示xxxx", ); }, ), );k
可以看到解寝,參數(shù)通過新頁面組件的構造方法直接傳入的报咳。
但是標準的做法通常是建立一個路由表讯檐,通過字符串名字映射路由信息甘萧,因此Flutter提供了一種命名路由方式:
Future pushNamed(BuildContext context, String routeName,{Object arguments})
我們本文就是整理分析關于它傳值后獲取它的兩種途徑缤弦。
-
路由映射表
不管哪種方式,首先我們都需要定義路由映射表锉试,比如:
static final routes = { HOME_PAGE: (context) => MyHomeWidget('Flutter Demo Home Page'), MIX_DEMO: (context,{argument}) => MixDemo(title: argument), };
-
方式一:通過onGenerateRoute函數(shù)
這種方式首先在MaterialApp中不能指定routes屬性玻募,因為onGenerateRoute函數(shù)只有在routes不存在或者在其中找不到路由的時候才會調用,具體原理之前有博文介紹過,此處不再贅述拖云。
這種方式要求映射表不指定routes的類型,雖然按照MaterialApp的routes類型來看靶庙,我們應該定義成:
Map<String, WidgetBuilder>? routes; typedef WidgetBuilder = Widget Function(BuildContext context);
但是如果使用onGenerateRoute方式的話,定義成這樣是不行的娃属,為什么六荒,接著往下看。
下面我們看onGenerateRoute中該如何寫:
///針對命名路由矾端,跳轉前攔截操作,找不到路由的時候才會走這里 Route<dynamic>? _myGenerateRouteLogic(RouteSettings settings) { Function? pageContentBuilder = RouteManager.routes[settings.name]; if (pageContentBuilder != null) { if (settings.arguments != null && settings.arguments is String) { var route = MaterialPageRoute( builder: (context) => pageContentBuilder(context, argument: settings.arguments as String)); return route; } else { var route = MaterialPageRoute( builder: (context) => pageContentBuilder(context)); return route; } } }
可見恬吕,這里會從映射表中按照路由的名字找到對應的pageContentBuilder,這里會用Function接收须床,這里也能理解,如果定義成WidgetBuilder的話渐裂,這里的實際類型就會是WidgetBuilder類型豺旬,而我們這里會根據(jù)arguments是否為空來調用不同參數(shù)的pageContentBuilder方法,使用WidgetBuilder接收是編譯不過去的柒凉。
現(xiàn)在獲取的pageContentBuilder是Widget Function(dynamic)類型族阅,它可以同時應對帶參數(shù)的和不帶參數(shù)的Function,所以可以通過編譯膝捞。
這種方式的映射表需要根據(jù)需求添加可選參數(shù)坦刀,必須是可選參數(shù),因為實際上函數(shù)類型還是限制成和WidgetBuilder一樣的蔬咬,只不過是參數(shù)類型用dynamic適配的鲤遥。
現(xiàn)在我們來梳理一下邏輯,MaterialApp中不設置routes屬性林艘,或者routes指定的路由表中不配置傳參路由頁面(最好不要這么做盖奈,顯得怪怪的),這樣以來路由跳轉時就會走到onGenerateRoute函數(shù)中狐援,在這里通過RouteSettings的name去路由表(這個路由表中必須得有獲取參數(shù)的路由頁面了)中找到生成該路由的pageContentBuilder钢坦,判斷RouteSettings的arguments,從而適配帶參數(shù)和不帶參數(shù)的pageContentBuilder調用啥酱,在pageContentBuilder中把可選參數(shù)通過路由頁面的構造方法傳入爹凹。
可見,這種方式最終還是通過路由頁面的構造函數(shù)直接傳入的镶殷。
-
方式二:通過ModalRoute
第一種方式可以說是另辟蹊徑禾酱,因為onGenerateRoute的初衷是為了處理一些路由表中未找到路由的情況的,用它來實現(xiàn)傳遞參數(shù)有一些驢唇不對馬嘴了,相比于第一種方式宇植,第二種方式就優(yōu)雅很多了得封。
在路由頁面的build中,我們直接調用ModalRoute.of(context)?.settings.arguments就可以拿到命名路由傳過來的參數(shù)指郁,代碼越少忙上,原理越繞...那它的原理是什么呢?
@optionalTypeArgs static ModalRoute<T>? of<T extends Object?>(BuildContext context) { final _ModalScopeStatus? widget = context.dependOnInheritedWidgetOfExactType<_ModalScopeStatus>(); return widget?.route as ModalRoute<T>?; }
可以看到闲坎,這里會在組件樹中通過找到父級中最近的一個_ModalScopeStatus疫粥,然后拿到它的route,我們來找一下 _ModalScopeStatus是什么腰懂。
最終找到的是:
@override Iterable<OverlayEntry> createOverlayEntries() sync* { yield _modalBarrier = OverlayEntry(builder: _buildModalBarrier); yield _modalScope = OverlayEntry(builder: _buildModalScope, maintainState: maintainState); }
_buildModalScope函數(shù)中最終構造的子樹中含有 _ModalScopeStatus梗逮,createOverlayEntries這個方法是在NavigatorState的 _push流程中會調用的方法,可以理解成建立新路由的面板绣溜。
Widget _buildModalScope(BuildContext context) { // To be sorted before the _modalBarrier. return _modalScopeCache ??= Semantics( sortKey: const OrdinalSortKey(0.0), child: _ModalScope<T>( key: _scopeKey, route: this, // _ModalScope calls buildTransitions() and buildChild(), defined above ), ); }
_ModalScope設置了一個route屬性慷彤,指定為當前類, _buildModalScope所在的類正是ModalRoute怖喻,它的RouteSettings屬性正是前面pushNamed方法一步步傳入的底哗,我們來看看RouteSettings在怎么生成的。
@optionalTypeArgs Future<T?> pushNamed<T extends Object?>( String routeName, { Object? arguments, }) { return push<T>(_routeNamed<T>(routeName, arguments: arguments)!); }
很明顯锚沸,Route由_routeNamed生成跋选,它的核心代碼如下:
Route<T>? _routeNamed<T>(String name, { required Object? arguments, bool allowNull = false }) { if (allowNull && widget.onGenerateRoute == null) return null; final RouteSettings settings = RouteSettings( name: name, arguments: arguments, ); Route<T>? route = widget.onGenerateRoute!(settings) as Route<T>?; if (route == null && !allowNull) { //如果沒找到返回onUnknownRoute生成的404路由 route = widget.onUnknownRoute!(settings) as Route<T>?; } return route; }
可以看到,RouteSettings是在這里創(chuàng)建的哗蜈,它的arguments正是我們調用pushNamed方法時傳入的arguments屬性的值前标,而Route通過widget.onGenerateRoute生成,因為_routeNamed方法在NavigatorState中距潘,所以widget自然是Navigator炼列,Navigator的widget.onGenerateRoute也是構造時傳入的,我們這里是App內的跳轉绽昼,所以找到 _WidgetsAppState中的build中Navigator的構造處唯鸭,發(fā)現(xiàn)這里的onGenerateRoute屬性設置的是一個叫 _onGenerateRoute的函數(shù):
Route<dynamic>? _onGenerateRoute(RouteSettings settings) { final String? name = settings.name; final WidgetBuilder? pageContentBuilder = name == Navigator.defaultRouteName && widget.home != null ? (BuildContext context) => widget.home! : widget.routes![name]; if (pageContentBuilder != null) { assert( widget.pageRouteBuilder != null, 'The default onGenerateRoute handler for WidgetsApp must have a ' 'pageRouteBuilder set if the home or routes properties are set.', ); final Route<dynamic> route = widget.pageRouteBuilder!<dynamic>( settings, pageContentBuilder, ); assert(route != null, 'The pageRouteBuilder for WidgetsApp must return a valid non-null Route.'); return route; } if (widget.onGenerateRoute != null) return widget.onGenerateRoute!(settings); return null; }
可以看到,這里會通過name去App指定的routes映射表中取pageContentBuilder硅确,route又通過widget.pageRouteBuilder產生目溉,找到_MaterialAppState的 _buildWidgetApp中發(fā)現(xiàn)pageRouteBuilder是:
pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) { return MaterialPageRoute<T>(settings: settings, builder: builder); },
可以看到這里和我們第一種方式的onGenerateRoute中的構造route的方式差不多,只不過這里的MaterialPageRoute會把RouteSettings傳進去菱农,而我們第一種方式不用傳是因為我們那時是直接通過路由頁面的構造函數(shù)傳入?yún)?shù)的缭付,而這里會把settings傳入是因為之后是通過route找到settings后再取參數(shù)。
而MaterialPageRoute最終繼承自ModalRoute循未,所以dependOnInheritedWidgetOfExactType會取到route從而取到settings陷猫。
-
總結
關于路由參數(shù)的獲取我們這里整理分析了兩種方式秫舌,第一種屬于旁門招數(shù),而第二種也是官方推薦使用的方式绣檬,它通過把參數(shù)保存在組件樹的當前Route中足陨,然后通過dependOnInheritedWidgetOfExactType獲取最近的Route持有者的方式來獲取參數(shù)。
Flutter命名路由獲取傳參的兩種方式
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
- 文/潘曉璐 我一進店門忽妒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玩裙,“玉大人,你說我怎么就攤上這事段直∠仔铮” “怎么了?”我有些...
- 正文 為了忘掉前任甩苛,我火速辦了婚禮蹂楣,結果婚禮上,老公的妹妹穿的比我還像新娘讯蒲。我一直安慰自己痊土,他們只是感情好,可當我...
- 文/花漫 我一把揭開白布墨林。 她就那樣靜靜地躺著赁酝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪旭等。 梳的紋絲不亂的頭發(fā)上酌呆,一...
- 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼娜饵!你這毒婦竟也來了坡贺?” 一聲冷哼從身側響起,我...
- 正文 年R本政府宣布刽虹,位于F島的核電站,受9級特大地震影響呢诬,放射性物質發(fā)生泄漏涌哲。R本人自食惡果不足惜,卻給世界環(huán)境...
- 文/蒙蒙 一尚镰、第九天 我趴在偏房一處隱蔽的房頂上張望阀圾。 院中可真熱鬧,春花似錦狗唉、人聲如沸初烘。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽肾筐。三九已至,卻和暖如春缸剪,著一層夾襖步出監(jiān)牢的瞬間局齿,已是汗流浹背。 一陣腳步聲響...
推薦閱讀更多精彩內容
- 前言 在做 Flutter 開發(fā)的時候所有的頁面以及頁面上的元素都變成了 Widget 蹋半,創(chuàng)建一個頁面或者視圖直接...
- 概述get的官方文檔上介紹說他巨,它具有更快和更實際的路由管理,至于性能上是不是如他所說我暫時沒做比較减江,本文從初始化的...
- 概述我們知道染突,在配置FlutterEngine的時候,有兩種方式可以設置初始路由辈灼,這個初始路由就是flutter第...
- 命名路由如果在initstate中接收路由參數(shù)份企,需要通過重定向路由把參數(shù)轉發(fā)到接收參數(shù)的頁面 step1.在mai...