[译]Flutter基于river_pod和Navigator2.0的导航库riverpod_navigator(一)- 简介

320 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情


本文翻译自: riverpod_navigator | Flutter Package (flutter-io.cn)

译时版本:1.0.10


Riverpod 导航

如果你对该包创建的动机、该包解决了什么问题的详细很感兴趣,可读一下这个中篇文章: 使用 Riverpod 的简易的 Flutter 导航

使用 riverpod 和 Navigator 2.0 的简易强大的 Flutter 导航解决了下面的问题:

  • 严格类型导航:
    可以在代码中使用 navigate([HomeSegment(),BookSegment(id: 2)]); 代替 navigate('home/book;id:2');
  • 异步导航... ... 是当导航状态的改变需要异步进行(如 从网络加载或保存数据)时的情况。
  • 多个 provider... ... 是当导航状态依赖多个 riverpod 的 provider 时。
  • 更简单的编码: 导航问题简化为操作类的集合。
  • 更好地分离关注点:UI x Model (得益于 riverpod 👍):
    导航逻辑可被开发和测试而不需要创建一个简单的 Flutter 组件。
  • 嵌套导航 只需要使用 riverpod 的 ProviderScope() 和 Flutter 的 Router 组件。

使用术语

看一下下面和 URL 路径 home/book;id=2 相关的项目。

  • 字符串-路径:  e.g. home/book;id=2
  • 字符串-段:  字符串-路径 由反斜杠界定的 字符串-段 (home 和 book;id=2) 组成
  • 类型-段 描述了相应字符串-段 (HomeSegment() 对应 'home' , BookSegment(id:2) 对应 'book;id=2')
    类型段 是 class TypedSegment {} 的派生。
  • 类型-路径 描述了相应的 字符串-路径 ([HomeSegment(), BookSegment(id:2)])
    类型-路径 即 typedef TypedPath = List<TypedSegment>
  • Flutter Navigator 2.0 的 导航栈 是由类型路径唯一确定。(每个类型路径的 类型段实例对应一个屏幕和页面实例):
    pages = [MaterialPage (child: HomeScreen(HomeSegment())), MaterialPage (child: BookScreen(BookSegment(id:2)))]

简单示例

使用下面的简单几步创建应用:

第 1 步 - 为类型-段定义类

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);
}

encode 和 decncode 用于辅助 类型-段 和 字符串-段 之间的相互转换。

第 2 步 - 配置应用的导航

... 通过继承 RNavigator 类。

class AppNavigator extends RNavigator {
  AppNavigator(Ref ref)
      : super(
          ref,
          [
            /// 'home' 和 'book' 字符串用在 WEB URL中,例,'home/book;id=2'
            /// decode 用于将 URL 解码为 HomeSegment/BookSegment
            /// HomeScreen/BookScreen.new 是用于指定段的页面构建器
            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 ...

... 在 main 入口处

void main() => runApp(
      ProviderScope(
        // [HomeSegment()] 作为主页的类型路径,需要导航构造方法
        overrides: riverpodNavigatorOverrides([HomeSegment()], AppNavigator.new),
        child: const App(),
      ),
    );

第 5 步 - 编写页面组件的代码

有两个页面需要编码:HomeScreen 和 BookScreen。这些页面继承 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}'),
          /// [appBarLeading] 重写标准的后退按钮行为
          leading: appBarLeading,
        ),
        body: 
...

RScreen 组件:

该组件对于异步导航能正常运行是必需的。

  • 替换标准的安卓后退按钮行为(使用 Flutter BackButtonListener 组件)
  • 会提供 appBarLeading 图标替换标准的 AppBar 后退按钮行为

然后就完成了。

查看:

 [运行示例] 中的链接 Go to book: [3, 13, 103](pavelpz.github.io/doc_simple/) 不会是有实际意义的书籍应用。但他展现了四个页面导航栈的导航:

  • 字符串-路径 = home/book;id=3/book;id=13/book;id=103.
  • 类型-路径 = [HomeSegment(), BookSegment(id:3), BookSegment(id:13), BookSegment(id:103)].
  • 导航-栈 (flutter Navigator.pages) = [MaterialPage (child: HomeScreen(HomeSegment())), MaterialPage (child: BookScreen(BookSegment(id:3))), MaterialPage (child: BookScreen(BookSegment(id:13))), MaterialPage (child: BookScreen(BookSegment(id:103)))].

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情