深入解析 Flutter 路由与导航:从基础到项目实战

460 阅读4分钟

深入解析 Flutter 路由与导航:从基础到项目实战

在 Flutter 中,路由与导航是构建多页面应用的核心功能。无论是简单的页面跳转,还是复杂的多级路由管理,掌握路由与导航的使用是开发高质量 Flutter 应用的关键。本篇博客将从基础到高级,详细介绍 Flutter 的路由与导航功能,并结合实际项目场景,探讨如何使用第三方路由库(如 go_routerauto_route)实现高效的路由管理。


1. 路由与导航的基础概念

1.1 什么是路由?

  • 路由是页面的唯一标识符,用于管理页面的跳转和导航。
  • 在 Flutter 中,路由可以是一个简单的字符串(命名路由)或一个复杂的对象(自定义路由)。

1.2 Navigator 的作用

  • Navigator 是 Flutter 提供的内置导航管理器,用于管理页面的堆栈。
  • 页面跳转时,新的页面会被推入堆栈;返回时,页面会从堆栈中弹出。

2. Flutter 内置路由与导航

2.1 基本路由

Flutter 提供了 Navigator.pushNavigator.pop 方法,用于实现页面的跳转和返回。

示例代码
// 页面 A
class PageA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("页面 A")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => PageB()),
            );
          },
          child: Text("跳转到页面 B"),
        ),
      ),
    );
  }
}

// 页面 B
class PageB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("页面 B")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text("返回页面 A"),
        ),
      ),
    );
  }
}
代码解析
  1. Navigator.push
    • 将页面 B 推入导航堆栈。
    • 使用 MaterialPageRoute 定义页面的路由。
  2. Navigator.pop
    • 从导航堆栈中弹出页面 B,返回页面 A。

2.2 命名路由

命名路由通过字符串标识页面,适合管理多页面应用。

示例代码
void main() {
  runApp(MaterialApp(
    initialRoute: '/',
    routes: {
      '/': (context) => PageA(),
      '/pageB': (context) => PageB(),
    },
  ));
}

// 页面 A
class PageA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("页面 A")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pushNamed(context, '/pageB');
          },
          child: Text("跳转到页面 B"),
        ),
      ),
    );
  }
}

// 页面 B
class PageB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("页面 B")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text("返回页面 A"),
        ),
      ),
    );
  }
}
代码解析
  1. initialRoute:定义应用的初始路由。
  2. routes:定义路由表,映射路由名称到页面。

2.3 路由传参

在页面跳转时,可以通过路由传递参数。

示例代码
// 页面 A
class PageA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("页面 A")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => PageB(data: "Hello from Page A"),
              ),
            );
          },
          child: Text("跳转到页面 B"),
        ),
      ),
    );
  }
}

// 页面 B
class PageB extends StatelessWidget {
  final String data;

  PageB({required this.data});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("页面 B")),
      body: Center(
        child: Text(data),
      ),
    );
  }
}

3. 第三方路由库的使用

3.1 为什么使用第三方路由库?

  • 内置路由的局限性
    • 路由表难以管理,尤其是多级嵌套路由。
    • 缺乏路由拦截和守卫功能。
  • 第三方路由库的优势
    • 提供更强大的路由管理功能。
    • 支持嵌套路由、动态路由和路由守卫。

3.2 使用 go_router

go_router 是一个轻量级的路由库,支持嵌套路由和动态路由。

安装
dependencies:
  go_router: ^6.0.0
示例代码
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final GoRouter _router = GoRouter(
    initialLocation: '/',
    routes: [
      GoRoute(
        path: '/',
        builder: (context, state) => PageA(),
      ),
      GoRoute(
        path: '/pageB',
        builder: (context, state) => PageB(data: state.queryParams['data'] ?? ''),
      ),
    ],
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
    );
  }
}

// 页面 A
class PageA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("页面 A")),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            context.go('/pageB?data=Hello%20from%20Page%20A');
          },
          child: Text("跳转到页面 B"),
        ),
      ),
    );
  }
}

// 页面 B
class PageB extends StatelessWidget {
  final String data;

  PageB({required this.data});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("页面 B")),
      body: Center(
        child: Text(data),
      ),
    );
  }
}
代码解析
  1. GoRouter:定义路由表和初始路由。
  2. 动态路由参数:通过 queryParams 获取路由参数。
  3. context.go:实现页面跳转。

3.3 使用 auto_route

auto_route 是一个功能强大的路由库,支持代码生成和路由守卫。

安装
dependencies:
  auto_route: ^5.0.0
  auto_route_generator: ^5.0.0
dev_dependencies:
  build_runner: ^2.0.0
示例代码
  1. 定义路由表

    import 'package:auto_route/auto_route.dart';
    import 'package:flutter/material.dart';
    
    @MaterialAutoRouter(
      routes: <AutoRoute>[
        AutoRoute(page: PageA, initial: true),
        AutoRoute(page: PageB),
      ],
    )
    class $AppRouter {}
    
  2. 生成路由代码

    flutter pub run build_runner build
    
  3. 使用路由

    final _appRouter = AppRouter();
    
    @override
    Widget build(BuildContext context) {
      return MaterialApp.router(
        routerDelegate: _appRouter.delegate(),
        routeInformationParser: _appRouter.defaultRouteParser(),
      );
    }
    

4. 项目实战:实现一个多页面电商应用

4.1 功能需求

  1. 首页:展示商品分类。
  2. 商品详情页:展示商品详情。
  3. 购物车页:展示已添加的商品。

4.2 路由设计

  • /:首页
  • /product/:id:商品详情页
  • /cart:购物车页

4.3 使用 go_router 实现路由

完整代码
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final GoRouter _router = GoRouter(
    initialLocation: '/',
    routes: [
      GoRoute(
        path: '/',
        builder: (context, state) => HomePage(),
      ),
      GoRoute(
        path: '/product/:id',
        builder: (context, state) {
          final id = state.params['id']!;
          return ProductPage(productId: id);
        },
      ),
      GoRoute(
        path: '/cart',
        builder: (context, state) => CartPage(),
      ),
    ],
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
    );
  }
}

// 首页
class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("首页")),
      body: ListView(
        children: [
          ListTile(
            title: Text("商品 1"),
            onTap: () => context.go('/product/1'),
          ),
          ListTile(
            title: Text("商品 2"),
            onTap: () => context.go('/product/2'),
          ),
        ],
      ),
    );
  }
}

// 商品详情页
class ProductPage extends StatelessWidget {
  final String productId;

  ProductPage({required this.productId});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("商品详情")),
      body: Center(
        child: Text("商品 ID: $productId"),
      ),
    );
  }
}

// 购物车页
class CartPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("购物车")),
      body: Center(
        child: Text("购物车为空"),
      ),
    );
  }
}

总结

  1. 基础路由

    • 使用 Navigator 实现页面跳转和返回。
    • 使用命名路由管理多页面应用。
  2. 第三方路由库

    • 使用 go_router 实现嵌套路由和动态路由。
    • 使用 auto_route 提供代码生成和路由守卫功能。
  3. 项目实战

    • 通过路由设计和动态参数传递,构建一个多页面电商应用。