[译]Flutter Favorite之路由包go_router - 高级路由 - 过渡

3,281 阅读3分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。

本文翻译自 go_router (gorouter.dev)

水平太烂~ 翻到自己怀疑人生~ 不吝赐教~

[译]Flutter Favorite之路由包go_router - 基础篇 - 掘金 (juejin.cn)


go_router - 高级路由 - 过渡

go_router 内置支持默认的页面过渡和自定义页面过渡。

默认过渡

默认情况下,go_router 会为组件树中的APP类型使用合适的页面过渡,例:如果在组件树中使用了 MaterialApp ,默认的过渡会使用 MaterialPage 来定义。这通过在 go_router 中定义的默认的页面构建函数来完成,如下:

MaterialPage<void> pageBuilderForMaterialApp(
  LocalKey key,
  String restorationId,
  Widget child,
) =>
    MaterialPage<void>(
      key: key,
      restorationId: restorationId,
      child: child,
    );

这里的 child 参数是 router 的 build 函数的输出结果,该 build 函数的作用是如何使应用的屏幕在页面间进行合适地过渡,而不需要为每个路由写这些代码。

所有这些对于 CupertinoApp 和 CupertinoPage 也是一样的。

如果在组件中没有 MaterialApp 也没有 CupertinoApp ,则默认没有过渡。

pageBuilder 代替 builder

如果要提供自己的 pageBuilder 函数, 可以在每个路由上来设置:

GoRoute(
  path: '/page2',
  pageBuilder: (context, state) => MaterialPage<void>(
    key: state.pageKey,
    restorationId: state.pageKey.value,
    child: const Page2Screen(),
  ),
),

这在功能上等同于 go_router 提供的默认功能。 state.pageKey 属性基于页面栈中页面的当前路径,所以它会唯一标识页面,而不需要硬编码 key 或者自己提供一个 key , 使其可以用于构成页面的 key 或者 restorationId 属性。 可以设置 child 属性为任何你想使用的内容,或者 builder 函数的返回结果。

errorPageBuilder 代替 errorBuilder

如果想完全控制创建中的页面,可以提供一个 pageBuilder 代替 builder ,同样的方式,也可以使用 errorPageBuilder 代替 errorBuilder :

class App extends StatelessWidget {
  ...
  final _router = GoRouter(
    ...
    errorPageBuilder: (context, state) => MaterialPage<void>(
      key: state.pageKey,
      child: ErrorScreen(state.error),
    ),
  );
}

自定义过渡

实现 pageBuilder 代替 builder (或者 errorPageBuilder 代替 errorBuilder) 的一个有用的原因是在页面间提供一个自定义过渡,代替默认的过渡。可以使用 go_router 的 CustomTransitionPage 来做过这一点:

GoRoute(
  path: '/fade',
  pageBuilder: (context, state) => CustomTransitionPage<void>(
    key: state.pageKey,
    child: const TransitionsPage(kind: 'fade', color: Colors.red),
    transitionsBuilder: (context, animation, secondaryAnimation, child) =>
        FadeTransition(opacity: animation, child: child),
  ),
),

当导航到一个新的路由时,CustomTransitionPagetransitionBuilder 参数会被调用,这时是返回一个过渡组件的时机。 过渡示例 展示了4中不同的过渡,但是你真的可以做任何你想做的过渡。

transitions.gif

CustomTransitionPage 也接收一个 transitionsDuration 参数,当想自定义过渡时长时可以使用(默认为 300 毫秒)。

如果自定义为无任何过渡,可以使用 go_router 提供的 NoTransitionPage:

GoRoute(
  path: '/none',
  pageBuilder: (context, state) => NoTransitionPage<void>(
    key: state.pageKey,
    child: const ExampleTransitionsScreen(
      kind: 'none',
      color: Colors.white,
    ),
  ),
),

key 和 有状态组件

如果你发现自己实现了一个 pageBuilder 函数,该函数返回了一个扩展 StatefulWidget 的屏幕组件,那么你需要注意你的 key 。例如,在the books 示例 中,多个路由会导航到同一个页面:

class Bookstore extends StatelessWidget {
  final _scaffoldKey = const ValueKey<String>('App scaffold'); // shared key

  late final _router = GoRouter(
    routes: [
      ...
      GoRoute(
        path: '/authors',
        pageBuilder: (context, state) => FadeTransitionPage(
          key: _scaffoldKey, // shared key
          child: const BookstoreScaffold(
            selectedTab: ScaffoldTab.authors,
            child: AuthorsScreen(),
          ),
        ),
        ...
      ),
      GoRoute(
        path: '/settings',
        pageBuilder: (context, state) => FadeTransitionPage(
          key: _scaffoldKey, // shared key
          child: const BookstoreScaffold(
            selectedTab: ScaffoldTab.settings,
            child: SettingsScreen(),
          ),
        ),
      ),
    ],
    ...
  );
  ...
}

注意,除了使用自定义过渡页面( FadeTransitionPage 扩展了 CustomTransitionPage )之外, /authors 和 /settings 路由都使用了相同的 key 。这很重要,因为 BookstoreScaffold 页面是有状态的,然后在基于 selectedTab 属性的标签间进行切换。如果 key 是不同的,在标签间切换时用户会看到不和谐的过渡。我们可以为导向相同页面的不同路由使用相同的 key 来避免这种情况。