赞
踩
在 Flutter 中,Navigator
是一个管理应用视图(页面)的组件,它使用栈(Stack)的方式来控制页面的切换。每当你跳转到一个新页面时,Navigator
会将新页面的 Route
压栈(push),当你返回到之前的页面时,它会将当前页面的 Route
出栈(pop)。
为了使用 Navigator
进行页面跳转,我们需要使用 BuildContext
,它表示当前 widget 在 widget 树中的位置。BuildContext
是用于与 Navigator
进行交互的必要参数。
Navigator.push
方法用于将新的 Route
压入栈中,从而导航到新页面。
- Navigator.push(
- context,
- MaterialPageRoute(builder: (context) => NewPage()),
- );
或这种写法
- Navigator.push(context, MaterialPageRoute(
- builder: (context) {
- return NewPage();
- },
- ));
Navigator.pop
方法用于将栈顶的 Route
弹出,返回到前一个页面。
Navigator.pop(context);
MaterialPageRoute
是一种模态路由,它会根据目标平台的规范,为页面切换提供适当的动画。在 Android 上,它通常是一个从屏幕底部向上滑入的动画,而在 iOS 上,它通常是一个从屏幕右侧滑入的动画。
- import 'package:flutter/material.dart';
-
- void main() {
- runApp(MaterialApp(
- title: 'Navigation Basics',
- home: HomePage(),
- ));
- }
-
- class HomePage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('Home Page'),
- ),
- body: Center(
- child: ElevatedButton(
- child: Text('Open New Page'),
- onPressed: () {
- // 使用 Navigator.push 方法来跳转到新页面
- Navigator.push(
- context,
- MaterialPageRoute(builder: (context) => NewPage()),
- );
- },
- ),
- ),
- );
- }
- }
-
- class NewPage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('New Page'),
- ),
- body: Column(
- children: [
- Text('Welcome to the new page!'),
- TextButton(
- onPressed: () {
- Navigator.pop(context);
- },
- child: Text("pop"))
- ],
- ),
- );
- }
- }
命名路由是一种用于管理页面导航的技术,它允许你为每个页面分配一个唯一的名称,并通过这些名称在应用程序中进行页面之间的导航。命名路由,由一对字符串(路由名称)和对应的屏幕(或称为页面/视图)组成。
命名路由的好处
Navigator.push
和 MaterialPageRoute
。我们可以在 MaterialApp
的 routes
属性中定义所有的命名路由。routes
是一个 Map,它的键是字符串(路由的名称),而值是对应的构造器函数,返回相应的页面 Widget。
- MaterialApp(
- title: 'Navigation with Named Routes',
- // 初始路由,应用启动时加载的路由
- initialRoute: '/',
- // 定义命名路由
- routes: {
- '/': (context) => HomePage(),
- '/newPage': (context) => NewPage(),
- '/thirdPage': (context) => ThirdPage(),
- },
- )
Navigator.pushNamed
方法要使用命名路由进行页面跳转,可以调用 Navigator.pushNamed
方法,并传入对应的路由名称。
Navigator.pushNamed(context, '/newPage');
Navigator.pop
方法用于将栈顶的 Route
弹出,返回到前一个页面。
Navigator.pop(context);
Navigator.
popAndPushNamed 方法Navigator.popAndPushNamed
方法用于从当前页面返回到上一个页面,并立即导航到指定的命名路由。
该方法的作用是先执行 Navigator.pop
方法返回到上一个页面,然后立即执行 Navigator.pushNamed
方法导航到新的命名路由。
Navigator.popAndPushNamed(context, '/thirdPage');
- import 'package:flutter/material.dart';
-
- void main() {
- runApp(MaterialApp(
- title: 'Navigation with Named Routes',
- // 初始路由,应用启动时加载的路由
- initialRoute: '/',
- // 定义命名路由
- routes: {
- '/': (context) => HomePage(),
- '/newPage': (context) => NewPage(),
- '/thirdPage': (context) => ThirdPage(),
- },
- ));
- }
-
- class HomePage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('Home Page'),
- ),
- body: Center(
- child: ElevatedButton(
- child: Text('Open New Page'),
- // 使用命名路由进行页面跳转
- onPressed: () {
- Navigator.pushNamed(context, '/newPage');
- },
- ),
- ),
- );
- }
- }
-
- class NewPage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('New Page'),
- ),
- body: Column(
- children: [
- Text('Welcome to the new page!'),
- TextButton(
- onPressed: () {
- Navigator.popAndPushNamed(context, '/thirdPage');
- },
- child: Text("popAndPushNamed"))
- ],
- ),
- );
- }
- }
-
- class ThirdPage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('Third Page'),
- ),
- body: Column(
- children: [
- Text('Welcome to the Third page!'),
- TextButton(
- onPressed: () {
- Navigator.pop(context);
- },
- child: Text("pop"))
- ],
- ),
- );
- }
- }
最直接的方式是通过目标页面的构造函数直接传递数据。
- import 'package:flutter/material.dart';
-
- void main() {
- runApp(MaterialApp(
- home: HomePage(),
- ));
- }
-
- class HomePage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('Home Page'),
- ),
- body: Center(
- child: ElevatedButton(
- child: Text('Pass Data to New Page'),
- onPressed: () {
- // 通过构造函数直接传递数据
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) => NewPage(data: 'Hello from Home Page!'),
- ),
- );
- },
- ),
- ),
- );
- }
- }
-
- class NewPage extends StatelessWidget {
- final String data;
-
- // 接收数据的构造函数
- NewPage({required this.data});
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('New Page'),
- ),
- body: Center(
- child: Text(data), // 显示传递过来的数据
- ),
- );
- }
- }
MaterialPageRoute
的 arguments
属性另一种传递数据的方式是使用 MaterialPageRoute
的 arguments
属性,这在使用命名路由时尤其有用。
- import 'package:flutter/material.dart';
-
- void main() {
- runApp(MaterialApp(
- // 初始路由,应用启动时加载的路由
- initialRoute: '/',
- // 定义命名路由
- routes: {
- '/': (context) => HomePage(),
- '/newPage': (context) => NewPage(),
- },
- ));
- }
-
- class HomePage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('Home Page'),
- ),
- body: Center(
- child: ElevatedButton(
- child: Text('Pass Data to New Page'),
- onPressed: () {
- Navigator.pushNamed(
- context,
- '/newPage',
- arguments: 'Hello from Home Page!',
- );
- },
- ),
- ),
- );
- }
- }
-
- class NewPage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- // 获取传递过来的数据
- final String data = ModalRoute.of(context)!.settings.arguments as String;
-
- return Scaffold(
- appBar: AppBar(
- title: Text('New Page'),
- ),
- body: Center(
- child: Text(data), // 显示传递过来的数据
- ),
- );
- }
- }
Navigator.pop
返回结果当从一个页面返回到前一个页面时,可以通过 Navigator.pop
方法返回数据:
- // 假设这是 NewPage 中的一个按钮,当点击时返回数据给前一个页面
- ElevatedButton(
- onPressed: () {
- Navigator.pop(context, 'Result from New Page');
- },
- child: Text('Return Data to Home Page'),
- ),
Navigator.push
和 await
结合使用你可以使用 await
关键字等待一个页面返回结果:
- // ... HomePage 中的按钮点击事件
- onPressed: () async {
- final result = await Navigator.push(
- context,
- MaterialPageRoute(builder: (context) => NewPage()),
- );
-
- // 使用 ScaffoldMessenger 显示返回的结果
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(content: Text(result?.toString() ?? 'No result')),
- );
- },
或
- onPressed: () async {
- final result = await Navigator.pushNamed(
- context,
- '/newPage',
- arguments: 'Hello from Home Page!',
- );
-
- // 使用 ScaffoldMessenger 显示返回的结果
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(content: Text(result?.toString() ?? 'No result')),
- );
- },
- ),
如果你不显式的调用Navigator.pop(context, 'xxx'),就拿不到回传结果。比如你从系统导航上点击返回按钮,就没数据传递回去。
如果一定任何返回都回传值,就需要定义导航栏,或者通过使用 PopScope widget 来拦截系统返回按钮的行为,并执行自定义的操作。
- class NewPage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('New Page'),
- ),
- body: PopScope(
- canPop: false, // 使用canPop提前禁用pop
- onPopInvoked: (bool didPop) {
- // canPop被设置为false时。didPop参数表示返回导航是否成功。
- // `didPop`参数会告诉你路由是否成功地pop出
- if (!didPop) {
- // 在这里执行你的操作,比如返回数据. 前面不禁用pop的话,这里就会pop两次了。
- Navigator.pop(context, 'Custom back button result');
- }
- },
- child: Column(
- children: [
- TextButton(
- onPressed: () {
- Navigator.pop(context, 'Result from New Page');
- },
- child: Text("pop"))
- ],
- ),
- ),
- );
- }
- }
注意:这里用多个地方调用Navigator.pop,从不同地方返回时,回传的值也会不同。如果要求回传的数据一致,就将Navigator.pop方法抽离放到一个方法中,多个返回位置调用同一个方法回传同样的数据。
在Flutter中,onGenerateRoute
是一个非常强大的钩子,允许开发者对路由进行自定义操作。它在MaterialApp
或CupertinoApp
中定义,并在导航到命名路由时被调用,特别是当使用Navigator.pushNamed
时。它可以用于动态生成路由,传递参数到新页面,甚至处理未知的路由。
下面是一个使用onGenerateRoute
的示例,其中包括了处理动态路由和传递参数到未知页面的代码:
- import 'package:flutter/material.dart';
-
- void main() {
- runApp(MyApp());
- }
-
- class MyApp extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- // 应用初始路由
- initialRoute: '/',
- // onGenerateRoute 用于处理动态路由
- onGenerateRoute: (RouteSettings settings) {
- // 获取传递过来的参数,如果参数为null,则提供一个空的Map
- final arguments = settings.arguments as Map<String, dynamic>? ?? {};
-
- // 根据 settings.name 处理不同路由
- switch (settings.name) {
- case '/':
- return MaterialPageRoute(builder: (context) => HomePage());
- case '/details':
- // 假设 DetailsPage 接受一个 'data' 参数
- final String data = arguments['data'] as String? ?? '默认值';
- return MaterialPageRoute(builder: (context) => DetailsPage(data: data));
- default:
- // 如果没有匹配的路由,返回到一个未知页面路由
- return MaterialPageRoute(builder: (context) => UnknownPage());
- }
- },
- );
- }
- }
-
- class HomePage extends StatefulWidget {
- const HomePage({super.key});
-
- @override
- State<HomePage> createState() => _HomePageState();
- }
-
- class _HomePageState extends State<HomePage> {
- String _data = "缺省值";
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('首页'),
- ),
- body: Column(
- children: [
- Text(_data), // 显示传递到本页面的数据
- ElevatedButton(
- onPressed: () async {
- // 导航到详情页,并传递数据。同时,使用await等待详情页返回的结果
- final result = await Navigator.pushNamed(
- context,
- '/details',
- arguments: {'data': '这是一个秘密信息!'},
- );
- final arguments = result as Map<String, dynamic>? ?? {};
- setState(() {
- if (mounted) {
- _data = arguments["data"] as String? ?? "";
- }
- });
- },
- child: Text('前往详情页'),
- ),
- ],
- ),
- );
- }
- }
-
- class DetailsPage extends StatelessWidget {
- final String data;
-
- DetailsPage({required this.data});
-
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('详情页'),
- ),
- body: Column(
- children: [
- Text(data), // 显示传递到本页面的数据
- TextButton(
- onPressed: () {
- // 回传数据数据
- Navigator.pop(context, {'data': '详情返回数据'});
- },
- child: Text("pop"))
- ],
- ),
- );
- }
- }
-
- class UnknownPage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('未知页面'),
- ),
- body: Center(
- child: Text('该路由名称不存在。'),
- ),
- );
- }
- }
当通过路由传递数据时,重要的是要验证接收的数据是否符合预期。你可以使用类型检查、正则表达式或自定义验证函数来确保数据的有效性和安全性。
- bool isValidData(dynamic data) {
- // 在这里添加验证逻辑,例如类型检查、内容检查等
- return data is String && data.isNotEmpty;
- }
在使用数据之前,你可以调用这个函数来验证:
- if (isValidData(receivedData)) {
- // 数据有效,可以安全使用
- } else {
- // 数据无效,可以抛出异常或进行错误处理
- }
处理空值和异常是确保应用程序稳定性的重要部分。当你从路由接收数据时,应该始终假设这些数据可能为空或者不是预期的格式。下面是一些处理这些情况的策略:
当你期望的数据可能为空时,可以使用Dart的null-aware运算符来优雅地处理:
String data = receivedData ?? '默认值';
或者在使用之前检查数据是否为null:
- if (receivedData != null) {
- // 使用 receivedData
- } else {
- // 处理空值情况,例如返回错误提示或设置默认值
- }
如果数据转换或验证过程中可能抛出异常,你应该使用try-catch
语句来捕获这些异常:
- try {
- // 尝试使用 receivedData
- } catch (e) {
- // 处理异常,例如记录日志、显示错误信息等
- }
Provider
是一个流行的状态管理库,它依赖于 Flutter 的 InheritedWidget
来向下传递数据。它能够让你在 widget 树中跨越多个层级来传递和修改数据,而无需手动传递回调或数据。
使用 Provider
,你可以在应用的顶层提供一个状态,然后在应用的任何其他部分访问或修改这个状态。这适用于跨多个页面传递数据,甚至是整个应用的状态管理。
详细使用参见另一文https://gamin.blog.csdn.net/article/details/136556092
首先,你需要在 pubspec.yaml
文件中添加 provider
依赖项:
- dependencies:
- flutter:
- sdk: flutter
- provider: ^6.1.2 # 使用适合你的版本
然后,在应用顶层(即要包裹住MaterialApp)引入 Provider
:
- import 'package:flutter/material.dart';
- import 'package:provider/provider.dart';
-
- void main() {
- runApp(
- // 通过 MultiProvider 可以提供多个对象
- MultiProvider(
- providers: [
- // ChangeNotifierProvider 是 Provider 的一种,它可以响应通知
- ChangeNotifierProvider(create: (context) => DataProvider()),
- ],
- child: MyApp(),
- ),
- );
- }
-
- class MyApp extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- title: 'Flutter Demo',
- home: HomePage(),
- );
- }
- }
- // 定义一个继承自 ChangeNotifier 的数据模型,用来传递和响应变化
- class DataProvider extends ChangeNotifier {
- String _data = "初始数据";
-
- String get data => _data;
-
- void setData(String newData) {
- _data = newData;
- notifyListeners(); // 当更新数据时,通知监听的 widgets 进行重建
- }
- }
HomePage
中的按钮点击时,可以使用 Provider
来更新数据:
- class HomePage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- // 使用 Provider.of 来获取最近的 DataProvider 实例
- final dataProvider = Provider.of<DataProvider>(context);
-
- return Scaffold(
- appBar: AppBar(
- title: Text('首页'),
- ),
- body: Center(
- child: ElevatedButton(
- onPressed: () {
- // 更新数据
- dataProvider.setData('更新的数据');
- // 导航到详情页
- Navigator.push(
- context,
- MaterialPageRoute(builder: (context) => DetailsPage()),
- );
- },
- child: Text('前往详情页并传递数据'),
- ),
- ),
- );
- }
- }
-
- class DetailsPage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- // 监听 DataProvider,当数据变化时重建这个 widget
- final data = Provider.of<DataProvider>(context).data;
-
- return Scaffold(
- appBar: AppBar(
- title: Text('详情页'),
- ),
- body: Center(
- // 显示从 Provider 获取的数据
- child: Text(data),
- ),
- );
- }
- }
下面是一个使用Provider
进行状态管理和跨页面传值的完整示例,包括异常处理和空值检查:
首先,确保已经添加了provider
依赖。
- // main.dart
- import 'package:flutter/material.dart';
- import 'package:provider/provider.dart';
-
- void main() {
- runApp(MyApp());
- }
-
- class MyApp extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return ChangeNotifierProvider<DataModel>(
- create: (_) => DataModel(),
- child: MaterialApp(
- title: 'Flutter Demo',
- home: HomePage(),
- ),
- );
- }
- }
-
- class DataModel extends ChangeNotifier {
- String _data = '';
-
- String get data => _data;
-
- void updateData(String newData) {
- if (newData.isNotEmpty) {
- _data = newData;
- notifyListeners();
- } else {
- throw Exception('Data cannot be empty');
- }
- }
- }
-
- class HomePage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(title: Text('Home')),
- body: Center(
- child: Consumer<DataModel>(
- builder: (context, dataModel, child) {
- return ElevatedButton(
- onPressed: () {
- try {
- dataModel.updateData('New Data from Home');
- Navigator.push(
- context,
- MaterialPageRoute(builder: (context) => DetailsPage()),
- );
- } catch (e) {
- // Handle the exception
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(content: Text(e.toString())),
- );
- }
- },
- child: Text('Go to Details'),
- );
- },
- ),
- ),
- );
- }
- }
-
- class DetailsPage extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(title: Text('Details')),
- body: Center(
- child: Consumer<DataModel>(
- builder: (context, dataModel, child) {
- return Text(dataModel.data);
- },
- ),
- ),
- );
- }
- }
在这个例子中,DataModel
是一个简单的数据持有类,它通过Provider
允许在应用程序的其他部分访问和修改数据。HomePage
设置新的数据,并导航到DetailsPage
。DetailsPage
显示当前的数据。如果尝试设置空的数据,DataModel
将抛出一个异常,该异常在HomePage
中被捕获并显示为一个SnackBar
。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。