如何在Flutter中实现iOS Cupertino堆叠式全屏弹窗过渡效果?
实现iOS时钟应用的导航堆叠效果
现成组件方案
目前Flutter官方没有直接匹配该效果的组件,但可以使用第三方包flutter_cupertino_navigation_stack,它专门模拟iOS原生的导航堆叠过渡,包括时钟应用里新页面从底部滑入、旧页面保持堆叠的视觉效果。
自定义实现方案
如果不想依赖第三方包,可以基于PageRouteBuilder和动画自行实现:
1. 自定义路由类
创建继承自PageRoute的自定义路由,在buildTransitions方法中实现堆叠动画逻辑:
class IOSStackedRoute<T> extends PageRoute<T> { final WidgetBuilder builder; IOSStackedRoute({required this.builder}) : super(settings: RouteSettings()); @override Color? get barrierColor => null; @override String? get barrierLabel => null; @override Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) { return builder(context); } @override Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) { // 新页面从底部滑入的动画 final slideAnimation = Tween<Offset>( begin: const Offset(0, 1), end: Offset.zero, ).animate(CurvedAnimation( parent: animation, curve: Curves.easeInOut, )); // 旧页面的缩放+透明度变化,模拟堆叠效果 final oldPageAnimation = Tween<double>( begin: 1.0, end: 0.9, ).animate(CurvedAnimation( parent: animation, curve: Curves.easeInOut, )); return Stack( children: [ if (secondaryAnimation.status != AnimationStatus.completed) SlideTransition( position: Tween<Offset>( begin: Offset.zero, end: const Offset(0, -0.05), ).animate(secondaryAnimation), child: ScaleTransition( scale: oldPageAnimation, child: FadeTransition( opacity: oldPageAnimation, child: ModalRoute.of(context)?.previousContentWidget, ), ), ), SlideTransition( position: slideAnimation, child: child, ), ], ); } @override bool get maintainState => true; @override Duration get transitionDuration => const Duration(milliseconds: 300); }
2. 使用自定义路由
在导航跳转时调用这个自定义路由:
Navigator.push( context, IOSStackedRoute( builder: (context) => YourTargetPage(), ), );
3. 细节优化
- 可根据需求调整动画曲线、时长、缩放比例和偏移量,让效果更贴近iOS原生表现
- 上述代码已通过
secondaryAnimation实现了页面关闭时的反向恢复动画
内容的提问来源于stack exchange,提问作者Andres Eloy
相关产品推荐
相关产品推荐

