Flutter路由导航Flutter路由导航

简介: 我们通常会用屏(Screen)来称呼一个页面(Page),一个完整的App应该是有多个Page组成的。在之前的案例(豆瓣)中,我们通过IndexedStack来管理了首页中的Page切换:首页-书影音-小组-市集-我的通过点击BottomNavigationBarItem来设置IndexedStack的index属性来切换除了上面这种管理页面的方式,我们还需要实现其它功能的页面跳转:比如点击一个商品跳转到详情页,某个按钮跳转到发送朋友圈、微博的编辑页面。这种页面的管理和导航,我们通常会使用路由进行统一管理。

一. 路由管理


1.1. 认识Flutter路由


路由的概念由来已久,包括网络路由后端路由,到现在广为流行的前端路由

  • 无论路由的概念如何应用,它的核心是一个路由映射表
  • 比如:名字 detail 映射到 DetailPage 页面等
  • 有了这个映射表之后,我们就可以方便的根据名字来完成路由的转发(在前端表现出来的就是页面跳转)

在Flutter中,路由管理主要有两个类:Route和Navigator


1.2. Route


Route:一个页面要想被路由统一管理,必须包装为一个Route

  • 官方的说法很清晰:An abstraction for an entry managed by a Navigator.

但是Route是一个抽象类,所以它是不能实例化的

  • 在上面有一段注释,让我们查看MaterialPageRoute来使用
/// See [MaterialPageRoute] for a route that replaces the
/// entire screen with a platform-adaptive transition.
abstractclass Route<T> {
}

事实上MaterialPageRoute并不是Route的直接子类:

  • MaterialPageRoute在不同的平台有不同的表现
  • 对Android平台,打开一个页面会从屏幕底部滑动到屏幕的顶部,关闭页面时从顶部滑动到底部消失
  • 对iOS平台,打开一个页面会从屏幕右侧滑动到屏幕的左侧,关闭页面时从左侧滑动到右侧消失
  • 当然,iOS平台我们也可以使用CupertinoPageRoute
MaterialPageRoute -> PageRoute -> ModalRoute -> TransitionRoute -> OverlayRoute -> Route


1.3. Navigator


Navigator:管理所有的Route的Widget,通过一个Stack来进行管理的

  • 官方的说法也很清晰:A widget that manages a set of child widgets with a stack discipline.

那么我们开发中需要手动去创建一个Navigator吗?

  • 并不需要,我们开发中使用的MaterialApp、CupertinoApp、WidgetsApp它们默认是有插入Navigator的
  • 所以,我们在需要的时候,只需要直接使用即可
Navigator.of(context)

Navigator有几个最常见的方法:

// 路由跳转:传入一个路由对象
Future<T> push<T extendsObject>(Route<T> route)
// 路由跳转:传入一个名称(命名路由)
Future<T> pushNamed<T extendsObject>(
  String routeName, {
    Object arguments,
  })
// 路由返回:可以传入一个参数
bool pop<T extendsObject>([ T result ])


二. 路由基本使用


2.1. 基本跳转


我们来实现一个最基本跳转:

  • 创建首页页面,中间添加一个按钮,点击按钮跳转到详情页面
  • 创建详情页面,中间添加一个按钮,点击按钮返回到首页页面

image.png                                                 页面基本跳转

核心的跳转代码如下(首页中代码):

// RaisedButton代码(只贴出核心代码)
RaisedButton(
  child: Text("打开详情页"),
  onPressed: () => _onPushTap(context),
),
// 按钮点击执行的代码
_onPushTap(BuildContext context) {
  Navigator.of(context).push(MaterialPageRoute(
    builder: (ctx) {
      return DetailPage();
    }
  ));
}

核心的返回代码如下(详情页中代码):

// RaisedButton代码(只贴出核心代码)
RaisedButton(
  child: Text("返回首页"),
  onPressed: () => _onBackTap(context),
)
// 按钮点击执行的代码
_onBackTap(BuildContext context) {
  Navigator.of(context).pop();
}


2.2. 参数传递


在跳转过程中,我们通常可能会携带一些参数,比如

  • 首页跳到详情页,携带一条信息:a home message
  • 详情页返回首页,携带一条信息:a detail message

image.png                                                     参数传递

首页跳转核心代码:

  • 在页面跳转时,会返回一个Future
  • 该Future会在详情页面调用pop时,回调对应的then函数,并且会携带结果
_onPushTap(BuildContext context) {
  // 1.跳转代码
  final future = Navigator.of(context).push(MaterialPageRoute(
    builder: (ctx) {
      return DetailPage("a home message");
    }
  ));
  // 2.获取结果
  future.then((res) {
    setState(() {
      _message = res;
    });
  });
}

详情页返回核心代码:

_onBackTap(BuildContext context) {
  Navigator.of(context).pop("a detail message");
}


2.3. 返回细节


但是这里有一个问题,如果用户是点击右上角的返回按钮,如何监听呢?

方法一:自定义返回的按钮(在详情页中修改Scaffold的appBar)

appBar: AppBar(
  title: Text("详情页"),
  leading: IconButton(
    icon: Icon(Icons.arrow_back),
    onPressed: () {
      Navigator.of(context).pop("a back detail message");
    },
  ),
),

方法二:监听返回按钮的点击(给Scaffold包裹一个WillPopScope)

  • WillPopScope有一个onWillPop的回调函数,当我们点击返回按钮时会执行
  • 这个函数要求有一个Future的返回值:
  • true:那么系统会自动帮我们执行pop操作
  • false:系统不再执行pop操作,需要我们自己来执行
return WillPopScope(
  onWillPop: () {
    Navigator.of(context).pop("a back detail message");
    return Future.value(false);
  },
  child: Scaffold(
    appBar: AppBar(
      title: Text("详情页"),
    ),
    body: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          RaisedButton(
            child: Text("返回首页"),
            onPressed: () => _onBackTap(context),
          ),
          Text(_message, style: TextStyle(fontSize: 20, color: Colors.red),)
        ],
      ),
    ),
  ),
);


三. 命名路由使用


3.1. 基本跳转


我们可以通过创建一个新的Route,使用Navigator来导航到一个新的页面,但是如果在应用中很多地方都需要导航到同一个页面(比如在开发中,首页、推荐、分类页都可能会跳到详情页),那么就会存在很多重复的代码。

在这种情况下,我们可以使用命名路由(named route)

  • 命名路由是将名字和路由的映射关系,在一个地方进行统一的管理
  • 有了命名路由,我们可以通过Navigator.pushNamed() 方法来跳转到新的页面

命名路由在哪里管理呢?可以放在MaterialApp的 initialRouteroutes

  • initialRoute:设置应用程序从哪一个路由开始启动,设置了该属性,就不需要再设置home属性了
  • routes:定义名称和路由之间的映射关系,类型为Map<String, WidgetBuilder>

修改MaterialApp中的代码:

return MaterialApp(
  title: 'Flutter Demo',
  theme: ThemeData(
    primarySwatch: Colors.blue, splashColor: Colors.transparent
  ),
  initialRoute: "/",
  routes: {
    "/home": (ctx) => HYHomePage(),
    "/detail": (ctx) => HYDetailPage()
  },
);

修改跳转的代码:

_onPushTap(BuildContext context) {
  Navigator.of(context).pushNamed("/detail");
}

在开发中,为了让每个页面对应的routeName统一,我们通常会在每个页面中定义一个路由的常量来使用:

class HYHomePage extends StatefulWidget {
  staticconstString routeName = "/home";
}
class HYDetailPage extends StatelessWidget {
  staticconstString routeName = "/detail";
}

修改MaterialApp中routes的key

initialRoute: HYHomePage.routeName,
routes: {
  HYHomePage.routeName: (ctx) => HYHomePage(),
  HYDetailPage.routeName: (ctx) => HYDetailPage()
},


3.2. 参数传递


因为通常命名路由,我们会在定义路由时,直接创建好对象,比如HYDetailPage()

那么,命名路由如果有参数需要传递呢?

pushNamed时,如何传递参数:

_onPushTap(BuildContext context) {
  Navigator.of(context).pushNamed(HYDetailPage.routeName, arguments: "a home message of naned route");
}

在HYDetailPage中,如何获取到参数呢?

  • 在build方法中ModalRoute.of(context)可以获取到传递的参数
Widget build(BuildContext context) {
    // 1.获取数据
    final message = ModalRoute.of(context).settings.arguments;
  }


3.3. 路由钩子


3.3.1. onGenerateRoute


假如我们有一个HYAboutPage,也希望在跳转时,传入对应的参数message,并且已经有一个对应的构造方法

在HYHomePage中添加跳转的代码:

RaisedButton(
  child: Text("打开关于页"),
  onPressed: () {
    Navigator.of(context).pushNamed(HYAboutPage.routeName, arguments: "a home message");
  },
)

HYAboutPage的代码:

class HYAboutPage extends StatelessWidget {
  staticconstString routeName = "/about";
  finalString message;
  HYAboutPage(this.message);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("关于页面"),
      ),
      body: Center(
        child: Text(message, style: TextStyle(fontSize: 30, color: Colors.red),),
      ),
    );
  }
}

但是我们继续使用routes中的映射关系,就不好进行配置了,因为HYAboutPage必须要求传入一个参数;

这个时候我们可以使用onGenerateRoute的钩子函数:

  • 当我们通过pushNamed进行跳转,但是对应的name没有在routes中有映射关系,那么就会执行onGenerateRoute钩子函数;
  • 我们可以在该函数中,手动创建对应的Route进行返回;
  • 该函数有一个参数RouteSettings,该类有两个常用的属性:
  • name: 跳转的路径名称
  • arguments:跳转时携带的参数
onGenerateRoute: (settings) {
  if (settings.name == "/about") {
    return MaterialPageRoute(
      builder: (ctx) {
        return HYAboutPage(settings.arguments);
      }
    );
  }
  returnnull;
},

image.png                                                关于页面跳转


3.3.2. onUnknownRoute


如果我们打开的一个路由名称是根本不存在,这个时候我们希望跳转到一个统一的错误页面。

比如下面的abc是不存在有对应的页面的

  • 如果没有进行特殊的处理,那么Flutter会报错。
RaisedButton(
  child: Text("打开未知页面"),
  onPressed: () {
    Navigator.of(context).pushNamed("/abc");
  },
)

我们可以创建一个错误的页面:

class UnknownPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("错误页面"),
      ),
      body: Container(
        child: Center(
          child: Text("页面跳转错误"),
        ),
      ),
    );
  }
}

并且设置onUnknownRoute

onUnknownRoute: (settings) {
  return MaterialPageRoute(
    builder: (ctx) {
      return UnknownPage();
    }
  );
},
相关文章
Flutter Getx 路由 until 方法帮助你跳转指定路由
不少同学都会问我,这样一个场景,当我点击商品列表,进入商品页,点击购买,支付成功后,想返回商品页,或者我的中心的订单列表。怎么做,这中间跨度了 n 个路由。 我不只一次的推荐 GetX 的 until 方法,和 offNamedUntil 方法。 我写了个 demo 今天我们就一起来看下这两个方法如何使用。
2085 0
Flutter Getx 路由 until 方法帮助你跳转指定路由
|
Dart Android开发 UED
带你读《深入浅出Dart》二十七、Flutter路由管理
带你读《深入浅出Dart》二十七、Flutter路由管理
267 0
|
8月前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
525 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
10月前
|
API 网络架构
一文带你了解 Flutter 路由
一文带你了解 Flutter 路由
278 5
|
安全 Go 数据安全/隐私保护
Flutter开发笔记:Flutter路由技术
Flutter开发笔记:Flutter路由技术
1327 0
|
11月前
|
Go 网络架构 开发者
Flutter &鸿蒙next中的路由使用详解【基础使用】
本文介绍了 Flutter 路由系统的使用方法,包括基本路由、命名路由、参数传递、返回参数和动态路由。通过 `Navigator` 类实现页面跳转,支持简单和复杂参数的传递,并可通过 `onGenerateRoute` 实现更灵活的动态路由管理。示例代码展示了如何在实际项目中应用这些技术,帮助开发者构建清晰、易于维护的导航结构。
301 1
|
UED 开发者
flutter:获取对象&路由管理 (四)
本文介绍了Flutter中如何通过Context获取状态对象、使用GlobalKey获取状态对象、基本的路由管理、路由传值、命名路由、返回根路由以及点击图标跳转的方法。示例代码展示了如何在应用中实现这些功能,包括页面跳转、传递参数和返回上一页等操作。
158 0
flutter 导航组件 AppBar (含顶部选项卡TabBar,抽屉菜单 drawer ,自定义导航图标)
flutter 导航组件 AppBar (含顶部选项卡TabBar,抽屉菜单 drawer ,自定义导航图标)
330 1
|
Android开发
Flutter路由跳转参数处理小技巧
Flutter路由跳转参数处理小技巧
153 0
|
前端开发 开发者 iOS开发
【Flutter前端技术开发专栏】Flutter中的路由管理与页面跳转
【4月更文挑战第30天】本文介绍了Flutter的路由管理与页面跳转,包括基本和命名路由管理。基本路由使用`Navigator`的`push`和`pop`方法,如`MaterialPageRoute`和`CupertinoPageRoute`。命名路由则通过路由表注册名称进行跳转,如`Navigator.pushNamed`。此外,还展示了如何通过构造函数、`arguments`和`PageRouteBuilder`进行路由传值。掌握这些知识能提升Flutter开发效率。
373 0
【Flutter前端技术开发专栏】Flutter中的路由管理与页面跳转

热门文章

最新文章