路由导航及传参"/>
一篇文章说完Flutter页面路由导航及传参
目录
前言
动态路由
静态路由
静态路由传参
Fluro 实现路由导航与传参
前言
在 Flutter 中,App 多个页面之间的跳转是由 Navigator(导航器)来管理的,如常见的 Navigator.push 跳转到下一页,Navigator.pop 回到上一页,同时也会涉及到页面之间的参数传递。本文主要介绍一下动态路由、静态路由及第三方路由插件 Fluro,它们在页面跳转、参数传递的区别和各自的优缺点,日常开发选择合适的即可。
动态路由
动态路由是不需要显示声明的,通过代码来实现,如下面这样的:
class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(title: "FlutterToDo",theme: ThemeData(brightness: Brightness.dark,),// 直接将 HomePage() 赋值给 MaterialApp 的 homehome: HomePage(),);}
}
页面跳转通过 Navigator.push,在跳转到 SecondPage.dart 页面时,如果有参数时需要通过构造函数将参数传递给 SecondPage.dart,如这里的参数是 pageTitle。
class HomePage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("FlutterToDo"),),body: Center(child: RaisedButton(child: Text("这是第一个界面,点击进入第二个界面"),onPressed: () {// 点击跳转页面,无参数// Navigator.push// context,// MaterialPageRoute(// builder: (context) => SecondPage(),// ));// 带参数的页面跳转Navigator.push(context,MaterialPageRoute(// 参数:pageTitlebuilder: (context) => SecondPage(pageTitle: "SecondPage"),// 调用 then 等待接收 SecondPage 返回的数据)).then((value) => print(value));}, },),),);}
}
SecondPage.dart页面代码,参数 pageTitle依赖接收上个页面传递过来的数据并直接作为当前页面的标题。
import 'package:flutter/material.dart';class SecondPage extends StatelessWidget {final String pageTitle;SecondPage({Key key, this.pageTitle = "第二个界面"});@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text(pageTitle),),body: Center(child: RaisedButton(child: Text("这是第二个界面,点击进入第一个界面"),onPressed: () {// 返回上一个界面,// Navigator.pop(context);// 携带参数上一个界面,"SecondPage pop" 为传递给上一个页面的参数。Navigator.pop(context, "SecondPage pop");},),),);}
}
动态路由简单直接,传参时参数名称定义明确,调用者通过构造函数就能提示具体参数,使用起来比较方便,不用特地的去声明路由,缺点是缺乏统一管理,当前页面需要导入要跳转的页面,页面之间有强依赖,项目后期业务逻辑增多、结构变复杂,维护成本也就增加,不利于业务模块组件化。
静态路由
静态路由又称命名路由,其实现方式是在跳转之前,需要通过在MaterialApp内的routes属性内显式声明路由的名称,代码实现如下:
import 'package:flutter/material.dart';
import 'pages/HomePage.dart';
import 'pages/SecondPage.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(title: "FlutterToDo",// 默认加载的页面initialRoute: '/',// 显式声明路由列表routes: {'/': (context) => HomePage(),'/secondPage': (context) => SecondPage(),},);}
}
此时首页的代码实现及点击按钮的跳转逻辑如下:
// 跳转到第二个界面,
Navigator.pushNamed(context, '/secondPage');
这里的 /secondPage 就是上面 MyApp - > MaterialApp-> routes 中定义的路由,名称必须保持一致。SecondPage 页面点击按钮返回:
// 返回上一个界面
Navigator.pop(context);
静态路由传参
静态路由是通过 MyApp - > MaterialApp-> onGenerateRoute 属性来处理。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(title: "FlutterToDo",initialRoute: '/', // 默认界面// 当页面跳转时进行参数处理onGenerateRoute: (RouteSettings settings) {// ......},);}
}
查看 onGenerateRoute 的源码定义可以看到,根据所给的 route settings 来返回一个 Route<dynamic>。
而打开 RouteSettings 类的实现看到其构造函数有 name 和 arguments,其中 name 为路由名称,arguments 就是传递的参数,其类型为 Object。
将静态路由的代码优化一下之后:
import 'package:flutter/material.dart';
import 'pages/HomePage.dart';
import 'pages/SecondPage.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget {// routes中声明所有的页面final routes = {'/': (context, {arguments}) => HomePage(),'/secondPage': (context, {arguments}) => SecondPage(arguments: arguments),};@overrideWidget build(BuildContext context) {return MaterialApp(title: "FlutterToDo",initialRoute: '/', // 默认界面// 当页面跳转时进行参数处理onGenerateRoute: (RouteSettings settings) {// 1. 根据路由名称从上面声明的 routes 取出类似于 (context, {arguments}) => HomePage()的 pageBuilder函数体var pageBuilder = routes[settings.name];if (pageBuilder != null) {// 2. 判断是否有携带的参数if (settings.arguments != null) {// 创建路由页面并携带参数// 3. 传递 context 和 settings.arguments给 pageBuilder 函数体执行,// 并返回一个Widget页面给到 MaterialPageRoute 的 builderreturn MaterialPageRoute(builder: (context) =>pageBuilder(context, arguments: settings.arguments));} else {// 3. 无参数的情况return MaterialPageRoute(builder: (context) => pageBuilder(context));}}// 默认返回 HomePage页面return MaterialPageRoute(builder: (context) => HomePage());},);}
}
在 HomePage 点击按钮的跳转和传参:
// 通过arguments指定参数
Navigator.pushNamed(context, "/secondPage", arguments: {'title': "SecondPage title"}).then((value) {// 退出 SecondPage 时返回的参数print(value) // 返回传递数据 SecondPage pop
});
此时 SecondPage 页面获取参数代码如下:
import 'package:flutter/material.dart';class SecondPage extends StatelessWidget {final Map arguments;SecondPage({Key key, this.arguments});@overrideWidget build(BuildContext context) {String title = arguments != null ? arguments['title'] : "SecondPage";return Scaffold(appBar: AppBar(title: Text(title),),body: Center(child: RaisedButton(child: Text("这是第二个界面,点击进入第一个界面"),onPressed: () {// 返回上一个界面Navigator.pop(context, "返回传递数据 SecondPage pop");},),),);}
}
从上面可以看出,静态路由及传参优点也很明显,声明路由统一在一个位置定义,方便管理,当然也可以创建一个管理类(如:RouteManager)将 routes 中的路由 path 声明为静态的变量。
class RouteManager {static String homePage = "/";static String secondPage = "/secondPage";static routes = {homePage: (context, {arguments}) => HomePage(),secondPage: (context, {arguments}) => SecondPage(arguments: arguments),};
}
此时直接使用 RouteManager.secondPage 就可以了,相比于动态路由,跳转时不再需要导入 SecondPage,页面直接的耦合度降低。缺点通过 arguments 不够直接准确,同时在 SecondPage也需要定义一个 Map 来接收,这里的Map结构就需要调用者和使用者都能明确且准确无误填入参数字段,根据参数字段取出数据,对调用者和使用者都有一定的要求,同时也非常容易出错。
Fluro 实现路由导航与传参
第三方插件 fluro 在 pub地址,接下来,我们通过代码来看看 fluro 是如何解决 动态路由带来的页面耦合度过高和静态路由传参不方便的问题。
在pubspec.yaml中导入插件:
fluro: ^2.0.3
此时 MyApp 代码:
import 'package:flutter/material.dart';class MyApp extends StatelessWidget {MyApp({Key? key}) : super(key: key) {MyRoutes.configureRoutes();}@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter 技术实践',theme: ThemeData(primarySwatch: Colors.blue,),onGenerateRoute: MyRoutes.router.generator,initialRoute: MyRoutes.root,);}
}
MyRoutes 的实现实际上就是封装的 FluroRouter,路由的定义也统一在这个类里面完成,其实现如下:
import 'package:fluro/fluro.dart';class MyRoutes {static FluroRouter router = FluroRouter();static String testRoutePage = "/testRoutePage";static String secondPage = "/testRoutePage/secondPage";static void configureRoutes() {router.notFoundHandler = Handler(handlerFunc: (BuildContext? context, Map<String, List<String>> params) {return const Text("ROUTE WAS NOT FOUND !!!");});router.define(testRoutePage,handler: Handler(handlerFunc: (_, __) => const TestRoutePage()));router.define(secondPage,handler: Handler(handlerFunc: (_, params) {String title = params['title']?.first ?? "";return SecondPage(title: title,);}));}
}
TestRoutePage 跳转到 SecondPage 并传递 title 参数。
class TestRoutePage extends StatelessWidget {const TestRoutePage({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return Scaffold(body: Center(child: RaisedButton(child: const Text("TestRoutePage"),onPressed: () {// 跳转到 SecondPage,传参方式和get请求很像MyRoutes.router.navigateTo(context, MyRoutes.secondPage + "?title=hello").then((value) => print(value));},),),);}
}
SecondPage 页面实现
class SecondPage extends StatelessWidget {String? title;SecondPage({Key? key, this.title = "第二个页面"}) : super(key: key);@overrideWidget build(BuildContext context) {return Scaffold(body: Center(child: RaisedButton(child: Text(title ?? ""),onPressed: () {Navigator.pop(context, "SecondPage pop 参数");},),),);}
}
相比较于动态路由和静态路由,Fluro 传参方式更加简洁,运行在Web上时,而且通用性更高,当然还有更多好用的功能,这里只是简单的介绍 Fluro 的基本使用,详细用法请移步查看 Fluro 相关文档。
小结:日常开发中,路由导航和传参使用频率非常之高,选择合适的路由管理方式不仅有利于项目的维护和扩展,后期如果需要兼容Web等平台时,也能胜任。
本文代码可直接打开 flutter_todo 查看效果,同步更新到同名微信公众号(公众号搜索:flutter_todo)。
更多推荐
一篇文章说完Flutter页面路由导航及传参
发布评论