我的项目中有 3 条路线和许多英雄动画。现在,我想使用具有三个选项卡的路线在页面之间轻松滑动,但这些更改禁用了我所有的英雄动画。我使用TabBarView并TabController喜欢所有其他材料TabBar实现的示例。
这个问题有什么解决办法吗?
为了使Hero动画正常工作,您需要在Navigator堆栈上的页面之间导航。不幸的是, aTabBarView不使用 aNavigator在选项卡之间切换,因此我们必须实现自己的自定义选项卡视图小部件,该小部件确实使用 aNavigator以使英雄动画正常工作。
让我们首先制作一个模仿 a 的滑动过渡动画的页面路由TabBarView:
/// A [PageRoute] that mimics the transition animation of a [TabBarView].
class TabPageRoute extends PageRoute {
final TabController tabController;
final Widget child;
TabPageRoute({
required this.tabController,
required this.child,
});
@override
Color? get barrierColor => null;
@override
String? get barrierLabel => null;
@override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return child;
}
Animation<double> applyEasing(Animation<double> animation) =>
CurvedAnimation(parent: animation, curve: Curves.ease);
@override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
final isAnimatingNext = tabController.index > tabController.previousIndex;
// Animates the page when it moves between the middle of the screen and the
// right of the screen.
final rightMiddleTween = Tween<Offset>(
begin: isAnimatingNext ? const Offset(1, 0) : Offset.zero,
end: isAnimatingNext ? Offset.zero : const Offset(1, 0),
);
// Animates the page when it moves between the middle of the screen and the
// left of the screen.
final middleLeftTween = Tween<Offset>(
begin: isAnimatingNext ? Offset.zero : const Offset(-1, 0),
end: isAnimatingNext ? const Offset(-1, 0) : Offset.zero,
);
return SlideTransition(
position: rightMiddleTween.animate(applyEasing(animation)),
child: SlideTransition(
position: middleLeftTween.animate(applyEasing(secondaryAnimation)),
child: child,
),
);
}
@override
// If you want the state of each of your tab pages to be preserved when that
// page isn't visible, keep this set to true. Otherwise, set it to false.
bool get maintainState => true;
@override
// This seems to be the default transition duration that TabBars use.
Duration get transitionDuration => const Duration(milliseconds: 300);
}
Run Code Online (Sandbox Code Playgroud)
现在让我们创建一个自定义选项卡视图,使用 aNavigator和 newTabPageRoute在选项卡之间切换:
class HeroTabBarView extends StatefulWidget {
final List<Widget> pages;
// Just like a normal TabView, you need to either have a parent
// DefaultTabController or pass a TabController.
final TabController? controller;
const HeroTabBarView({
super.key,
required this.pages,
this.controller,
});
@override
State<HeroTabBarView> createState() => _HeroTabBarViewState();
}
class _HeroTabBarViewState extends State<HeroTabBarView> {
late TabController _controller;
final _navigatorKey = GlobalKey<NavigatorState>();
@override
void didChangeDependencies() {
final resolvedController =
widget.controller ?? DefaultTabController.maybeOf(context);
if (resolvedController == null) {
throw FlutterError(
'No TabController for ${widget.runtimeType}.\n'
'When creating a ${widget.runtimeType}, you must either provide an explicit '
'TabController using the "controller" property, or you must ensure that there '
'is a DefaultTabController above the ${widget.runtimeType}.\n'
'In this case, there was neither an explicit controller nor a default controller.',
);
}
_controller = resolvedController;
_controller.addListener(_switchPage);
super.didChangeDependencies();
}
@override
void dispose() {
_controller.removeListener(_switchPage);
super.dispose();
}
void _switchPage() {
// Use the route's name as the index of the page to switch to.
_navigatorKey.currentState!.pushReplacementNamed('${_controller.index}');
}
@override
Widget build(BuildContext context) {
// Use a nested Navigator so that switching tabs involves pushing a
// new page onto a Navigator stack without changing the general
// navigation state of your app.
return Navigator(
key: _navigatorKey,
initialRoute: '${_controller.index}',
// This is required in order to make Hero animations work in a
// nested navigator.
observers: [HeroController()],
onGenerateRoute: (settings) => TabPageRoute(
tabController: _controller,
child: widget.pages[int.parse(settings.name!)],
),
);
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以像使用普通的 一样使用HeroTabBarViewa 。确保您有一个包含您的和 的父级,或者您将自己的父级传递给您的和。TabBarTabBarViewDefaultTabControllerTabBarHeroTabBarViewTabControllerTabBarHeroTabBarView
class TabBarDemo extends StatelessWidget {
const TabBarDemo({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: const TabBar(
// Change these to whatever icons you want.
tabs: [
Tab(icon: Icon(Icons.directions_car)),
Tab(icon: Icon(Icons.directions_transit)),
Tab(icon: Icon(Icons.directions_bike)),
],
),
title: const Text('Tabs Demo'),
),
body: const HeroTabBarView(
// Replace with the pages you want for your tabs.
pages: [PageOne(), PageTwo(), PageThree()],
),
),
),
);
}
}
Run Code Online (Sandbox Code Playgroud)
您可以通过 DartPad 上的交互式示例来查看此代码的实际运行情况。
| 归档时间: |
|
| 查看次数: |
652 次 |
| 最近记录: |