「这是我参与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),
),
),
当导航到一个新的路由时,CustomTransitionPage 的 transitionBuilder 参数会被调用,这时是返回一个过渡组件的时机。 过渡示例 展示了4中不同的过渡,但是你真的可以做任何你想做的过渡。
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 来避免这种情况。