Flutter开发实战之路由与导航

第5章:路由与导航

在移动应用开发中,页面间的跳转是最基本也是最重要的功能之一。就像我们在现实生活中需要从一个房间走到另一个房间一样,在App中,用户需要在不同的界面间自由切换。Flutter提供了强大而灵活的路由系统来管理这些页面跳转,本章将深入探讨Flutter的路由与导航机制。

5.1 Navigator 1.0基础路由管理

5.1.1 什么是路由?

在Flutter中,**路由(Route)可以理解为应用中的一个页面或屏幕。每个路由都是一个Widget,通常是一个完整的界面。而导航(Navigation)**就是在这些路由之间进行切换的过程。

想象一下,如果把Flutter应用比作一栋楼房,那么每个路由就是楼房中的一个房间,而Navigator就像是连接这些房间的走廊和楼梯,帮助我们在不同房间间移动。

5.1.2 Navigator基础概念

Navigator是Flutter中管理路由栈的核心组件。它维护着一个路由栈(Route Stack),类似于我们常说的"后进先出"的数据结构。

import 'package:flutter/material.dart';

class HomePage extends StatelessWidget {
   
   
  
  Widget build(BuildContext context) {
   
   
    return Scaffold(
      appBar: AppBar(
        title: Text('首页'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
   
   
            // 最基础的页面跳转
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => DetailPage(),
              ),
            );
          },
          child: Text('跳转到详情页'),
        ),
      ),
    );
  }
}

class DetailPage extends StatelessWidget {
   
   
  
  Widget build(BuildContext context) {
   
   
    return Scaffold(
      appBar: AppBar(
        title: Text('详情页'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
   
   
            // 返回上一页
            Navigator.pop(context);
          },
          child: Text('返回'),
        ),
      ),
    );
  }
}

5.1.3 常用的导航方法

Navigator提供了多种导航方法,每种都有其特定的使用场景:

class NavigationDemo extends StatelessWidget {
   
   
  
  Widget build(BuildContext context) {
   
   
    return Scaffold(
      appBar: AppBar(title: Text('导航方法演示')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          // 1. push - 跳转到新页面
          ElevatedButton(
            onPressed: () {
   
   
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => NewPage()),
              );
            },
            child: Text('Push - 跳转'),
          ),
          
          // 2. pushReplacement - 替换当前页面
          ElevatedButton(
            onPressed: () {
   
   
              Navigator.pushReplacement(
                context,
                MaterialPageRoute(builder: (context) => ReplacementPage()),
              );
            },
            child: Text('Push Replacement - 替换'),
          ),
          
          // 3. pushAndRemoveUntil - 跳转并清空特定路由
          ElevatedButton(
            onPressed: () {
   
   
              Navigator.pushAndRemoveUntil(
                context,
                MaterialPageRoute(builder: (context) => HomePage()),
                (route) => false, // 清空所有路由
              );
            },
            child: Text('Push And Remove Until - 清空并跳转'),
          ),
          
          // 4. pop - 返回上一页
          ElevatedButton(
            onPressed: () {
   
   
              Navigator.pop(context);
            },
            child: Text('Pop - 返回'),
          ),
        ],
      ),
    );
  }
}

5.1.4 路由动画自定义

Flutter允许我们自定义页面切换的动画效果,让应用体验更加流畅:

class CustomRouteAnimation extends PageRouteBuilder {
   
   
  final Widget child;
  
  CustomRouteAnimation({
   
   required this.child})
      : super(
          transitionDuration: Duration(milliseconds: 500),
          pageBuilder: (context, animation, secondaryAnimation) => child,
        );

  
  Widget buildTransitions(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child) {
   
   
    // 滑入动画
    return SlideTransition(
      position: Tween<Offset>(
        begin: Offset(1.0, 0.0),
        end: Offset.zero,
      ).animate(animation),
      child: child,
    );
  }
}

// 使用自定义动画
void navigateWithCustomAnimation(BuildContext context) {
   
   
  Navigator.push(
    context,
    CustomRouteAnimation(child: DetailPage()),
  );
}

5.2 命名路由与路由表配置

5.2.1 为什么需要命名路由?

随着应用规模的增长,我们会发现直接使用MaterialPageRoute会让代码变得难以维护。命名路由就像给每个页面起一个独特的名字,让我们可以通过名字来进行导航,这样代码更清晰、更易维护。

5.2.2 配置路由表

在应用的根部配置路由表是管理大型应用路由的最佳实践:

class MyApp extends StatelessWidget {
   
   
  
  Widget build(BuildContext context) {
   
   
    return MaterialApp(
      title: 'Flutter路由演示',
      // 设置初始路由
      initialRoute: '/',
      // 配置路由表
      routes: {
   
   
        '/': (context) => HomePage(),
        '/detail': (context) => DetailPage(),
        '/profile': (context) => ProfilePage(),
        '/settings': (context) => SettingsPage(),
        '/login': (context) => LoginPage(),
      },
      // 处理未定义的路由
      onUnknownRoute: (settings) {
   
   
        return MaterialPageRoute(
          builder: (context) => NotFoundPage(),
        );
      },
    );
  }
}

5.2.3 使用命名路由进行导航

有了路由表,页面跳转就变得简单明了:

class NavigationWithNamedRoutes extends StatelessWidget {
   
   
  
  Widget build(BuildContext context) {
   
   
    return Scaffold(
      appBar: AppBar(title: Text('命名路由导航')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () {
   
   
                // 使用命名路由跳转
                Navigator.pushNamed(context, '/detail');
              },
              child: Text('跳转到详情页'),
            ),
            ElevatedButton(
              onPressed: () {
   
   
                Navigator.pushNamed(context, '/profile');
              },
              child: Text('跳转到个人资料'),
            ),
            ElevatedButton(
              onPressed: () {
   
   
                // 替换当前页面
                Navigator.pushReplacementNamed(context, '/login');
              },
              child: Text('跳转到登录页(替换)'),
            ),
          ],
        ),
      ),
    );
  }
}

5.2.4 动态路由生成

对于需要动态生成的路由,我们可以使用onGenerateRoute

class MyApp extends StatelessWidget {
   
   
  
  Widget build(BuildContext context) {
   
   
    return MaterialApp(
      onGenerateRoute: (settings) {
   
   
        // 根据路由名称动态生成页面
        switch (settings.name) {
   
   
          case '/':
            return MaterialPageRoute(builder: (context) => HomePage());
          case '/detail':
            // 可以从settings.arguments获取传递的参数
            final args = settings.arguments as Map<String, dynamic>?;
            return MaterialPageRoute(
              builder: (context) => DetailPage(data: args?['data']),
            );
          case '/user':
            // 支持路径参数,如 /user/123
            final userId = settings.name!.split('/').last;
            return MaterialPageRoute(
              builder: (context) => UserPage(userId: userId),
            );
          default:
            return MaterialPageRoute(builder: (context) => NotFoundPage());
        }
      },
    );
  }
}

5.3 页面间数据传递的多种方式

5.3.1 通过构造函数传递数据

这是最直接的数据传递方式,适用于简单的数据传递:

class ProductListPage extends StatelessWidget {
   
   
  
  Widget build(BuildContext context) {
   
   
    return Scaffold(
      appBar: AppBar(title: Text('商品列表')),
      body: ListView.builder(
        itemCount: products.length,
        itemBuilder: (context, index) {
   
   
          final product = products[index];
          return ListTile(
            title: Text(product.name),
            subtitle: Text('¥${product.price}'),
            onTap: () {
   
   
              // 通过构造函数传递商品数据
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => ProductDetailPage(product: product),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

class ProductDetailPage extends StatelessWidget {
   
   
  final Product product;
  
  // 通过构造函数接收数据
  const ProductDetailPage({
   
   Key? key, required this.product}) : super(key: key);

  
  Widget build(BuildContext context) {
   
   
    return Scaffold(
      appBar: AppBar(title: Text(product.name)),
      body: Column(
        children: [
          Image.network(product.imageUrl),
          Text('价格: ¥${product.price}'),
          Text(product.description),
        ],
      ),
    );
  }
}

5.3.2 通过命名路由传递参数

使用命名路由时,我们可以通过arguments参数传递数据:

// 发送数据的页面
class DataSenderPage extends StatelessWidget {
   
   
  
  Widget build(BuildContext context) {
   
   
    return Scaffold(
      body: ElevatedButton(
        onPressed: () {
   
   
          // 通过arguments传递数据
          Navigator.pushNamed(
            context,
            '/receiver',
            arguments: {
   
   
              'title': '传递的标题',
              'message': '这是传递的消息',
              'count': 42,
            },
          );
        },
        child: Text('发送数据'),
      ),
    );
  }
}

// 接收数据的页面
class DataReceiverPage extends StatelessWidget {
   
   
  
  Widget build(BuildContext context) {
   
   
    // 获取传递的参数
    final args = ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>;
    
    return Scaffold(
      appBar: AppBar(title: Text(args['title'])),
      body: Column(
        children: [
          Text('消息: ${args['message']}'),
          Text('数量: ${args['count']}'),
        ],
      ),
    );
  }
}

5.3.3 返回数据给上一页

有时我们需要从子页面返回数据给父页面,比如从设置页面返回用户选择的配置:

class SettingsPage extends StatefulWidget {
   
   
  
  _SettingsPageState createState() => _SettingsPageState();
}

class _SettingsPageState extends State<SettingsPage> {
   
   
  bool _notificationsEnabled = true;
  String _selectedTheme = 'light';

  
  Widget build(BuildContext context) {
   
   
    return Scaffold(
      appBar: AppBar(
        title: Text('设置'),
        actions: [
          TextButton(
            onPressed: () {
   
   
              // 返回设置数据给上一页
              Navigator.pop(context, {
   
   
                'notifications': _notificationsEnabled,
                'theme': _selectedTheme,
              });
            },
            child: Text('保存'),
          ),
        ],
      ),
      body: Column(
        children: [
          SwitchListTile(
            title: Text('推送通知'),
            value: _notificationsEnabled,
            onChanged: (value) {
   
   
              setState(() {
   
   
                _notificationsEnabled = value;
              });
            },
          ),
          ListTile(
            title: Text('主题'),
            subtitle: Text(_selectedTheme),
            onTap
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值