Flutter路由管理插件riverpod_navigator_core的使用

Flutter路由管理插件 riverpod_navigator_core 的使用

简介

riverpod_navigator_core 是一个基于 Riverpod 和 Navigator 2.0 的强大且简单的 Flutter 路由管理插件。它解决了以下问题:

  • 严格的类型导航:你可以使用 navigate([HomeSegment(), BookSegment(id: 2)]) 而不是 navigate('home/book;id:2')
  • 异步导航:当更改导航状态需要执行异步操作时(如从网络加载或保存数据)。
  • 多个 Provider:当导航状态依赖于多个 Riverpod Provider 时。
  • 简化编码:将导航问题简化为类集合的操作。
  • 更好的关注点分离:通过 Riverpod 实现 UI 和模型的分离,可以在不编写任何 Flutter 小部件的情况下开发和测试导航逻辑。
  • 嵌套导航:只需使用嵌套的 Riverpod ProviderScope() 和 Flutter 的 Router 小部件。

示例 Demo

步骤 1 - 定义 TypedSegment 类

首先,定义用于表示不同类型路径段的类:

class HomeSegment extends TypedSegment {
  const HomeSegment();
  factory HomeSegment.decode(UrlPars pars) => const HomeSegment();
}

class BookSegment extends TypedSegment {
  const BookSegment({required this.id});
  factory BookSegment.decode(UrlPars pars) => BookSegment(id: pars.getInt('id'));

  final int id;
  @override
  void encode(UrlPars pars) => pars.setInt('id', id);
}

步骤 2 - 配置 AppNavigator

通过扩展 RNavigator 类来配置 AppNavigator

class AppNavigator extends RNavigator {
  AppNavigator(Ref ref)
      : super(
          ref,
          [
            RRoute<HomeSegment>(
              'home',
              HomeSegment.decode,
              HomeScreen.new,
            ),
            RRoute<BookSegment>(
              'book',
              BookSegment.decode,
              BookScreen.new,
            ),
          ],
        );
}

步骤 3 - 在 MaterialApp.router 中使用 AppNavigator

如果你熟悉 Flutter Navigator 2.0 和 Riverpod,以下代码应该很容易理解:

class App extends ConsumerWidget {
  const App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final navigator = ref.read(navigatorProvider) as AppNavigator;
    return MaterialApp.router(
      title: 'Riverpod Navigator Example',
      routerDelegate: navigator.routerDelegate,
      routeInformationParser: navigator.routeInformationParser,
    );
  }
}

步骤 4 - 配置 Riverpod ProviderScope

在主入口点中配置 Riverpod ProviderScope

void main() => runApp(
      ProviderScope(
        overrides: riverpodNavigatorOverrides([HomeSegment()], AppNavigator.new),
        child: const App(),
      ),
    );

步骤 5 - 编写屏幕小部件

编写两个屏幕小部件 HomeScreenBookScreen,并继承自 RScreen 小部件:

class BookScreen extends RScreen<AppNavigator, BookSegment> {
  const BookScreen(BookSegment segment) : super(segment);

  @override
  Widget buildScreen(ref, navigator, appBarLeading) => Scaffold(
        appBar: AppBar(
          title: Text('Book ${segment.id}'),
          leading: appBarLeading,
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () => navigator.push(BookSegment(id: segment.id + 1)),
            child: Text('Next Book'),
          ),
        ),
      );
}

运行示例、源码和测试代码

异步导航

定义 TypedSegment 类

为每个 TypedSegment 应用 AsyncSegment 混入:

class HomeSegment extends TypedSegment with AsyncSegment<String>{
  ....
}

class BookSegment extends TypedSegment with AsyncSegment<String>{
  ....
}

配置 AppNavigator

RRoute 定义中添加 opening, closingreplacing 动作:

class AppNavigator extends RNavigator {
  AppNavigator(Ref ref)
      : super(
          ref,
          [
            RRoute<HomeSegment>(
              'home',
              HomeSegment.decode,
              HomeScreen.new,
              opening: (sNew) => sNew.setAsyncValue(_simulateAsyncResult('Home.opening', 2000)),
            ),
            RRoute<BookSegment>(
              'book',
              BookSegment.decode,
              BookScreen.new,
              opening: (sNew) => sNew.setAsyncValue(_simulateAsyncResult('Book ${sNew.id}.opening', 240)),
              replacing: (sOld, sNew) => sNew.setAsyncValue(_simulateAsyncResult('Book ${sOld.id}=>${sNew.id}.replacing', 800)),
              closing: (sOld) => Future.delayed(Duration(milliseconds: 500)),
            ),
          ],
        );
}

Future<String> _simulateAsyncResult(String asyncResult, int msec) async {
  await Future.delayed(Duration(milliseconds: msec));
  return '$asyncResult: async result after $msec msec';
}

使用异步动作结果构建屏幕

在构建屏幕时使用异步动作的结果:

...
Text('Async result: "${segment.asyncValue}"'),
...

其他功能和示例

示例安装

克隆 riverpod_navigator 仓库 后,进入 examples/doc 子目录并执行以下命令:

flutter create .
flutter pub get

更多关于Flutter路由管理插件riverpod_navigator_core的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter路由管理插件riverpod_navigator_core的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用 riverpod_navigator_core 插件进行 Flutter 路由管理的代码示例。riverpod_navigator_core 是一个与 Riverpod 状态管理库结合使用的 Flutter 路由管理插件。它允许你以声明式的方式管理应用中的路由。

首先,确保你已经在 pubspec.yaml 文件中添加了必要的依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_riverpod: ^1.0.0
  riverpod_navigator_core: ^1.0.0

然后,运行 flutter pub get 来安装这些依赖。

接下来,我们来看一个完整的示例,展示如何使用 riverpod_navigator_core

主应用入口 (main.dart)

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_navigator_core/riverpod_navigator_core.dart';
import 'routes.dart'; // 我们稍后会创建这个文件

void main() {
  runApp(
    ProviderScope(
      child: MaterialApp.router(
        routeInformationParser: RiverpodRouteInformationParser<RiverpodRouteData>(),
        routerDelegate: RiverpodRouterDelegate<RiverpodRouteData>(
          navigatorKey: GlobalKey(),
          routesBuilder: (context, state) => Routes().buildRoutes(),
        ),
      ),
    ),
  );
}

路由配置 (routes.dart)

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_navigator_core/riverpod_navigator_core.dart';
import 'screens/home_screen.dart';
import 'screens/details_screen.dart';

class Routes {
  Router buildRoutes() {
    return Router(
      routes: [
        RiverpodRoute(
          path: '/',
          builder: (_, __) => const HomeScreen(),
        ),
        RiverpodRoute(
          path: '/details/:id',
          builder: (_, routeData) {
            final id = routeData.parameters['id']!;
            return DetailsScreen(id: id);
          },
        ),
      ],
    );
  }
}

主屏幕 (screens/home_screen.dart)

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_navigator_core/riverpod_navigator_core.dart';

class HomeScreen extends ConsumerWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // 导航到详情页面,传递一个示例ID
            context.read(navigationRef).pushNamed('/details/123');
          },
          child: const Text('Go to Details'),
        ),
      ),
    );
  }
}

详情屏幕 (screens/details_screen.dart)

import 'package:flutter/material.dart';

class DetailsScreen extends StatelessWidget {
  final String id;

  const DetailsScreen({Key? key, required this.id}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Details Screen'),
      ),
      body: Center(
        child: Text('Details for ID: $id'),
      ),
    );
  }
}

注意事项

  1. 依赖注入riverpod_navigator_core 依赖于 Riverpod,因此你需要使用 ProviderScope 来包裹你的应用。
  2. 路由参数:在 RiverpodRoute 中,你可以使用 routeData.parameters 来访问 URL 中的参数。
  3. 导航:使用 context.read(navigationRef).pushNamedpop 方法来进行导航操作。

这个示例展示了如何使用 riverpod_navigator_core 来配置和管理 Flutter 应用中的路由。通过这种方式,你可以将路由配置与状态管理紧密结合,从而构建一个更加模块化和可维护的应用。

回到顶部