Flutter对角线帘式页面过渡:如何提前显示新页面?
解决Flutter对角窗帘过渡时显示新页面的问题
你的问题核心在于:之前的实现应该是先在当前页面执行窗帘动画,等动画结束后才跳转新页面,这就导致动画过程中新页面还没被加载,所以下面是空白。要让新页面在动画一开始就显示,我们需要把两个页面的过渡逻辑整合到自定义PageRoute中,让新页面作为底层,旧页面拆分成窗帘层在上面做拉开动画。
具体实现步骤
1. 自定义窗帘裁剪器
我们需要两个CustomClipper来分别裁剪旧页面的左上和右下部分,配合动画值实现窗帘拉开的效果:
class _LeftCurtainClipper extends CustomClipper<Path> { final double progress; _LeftCurtainClipper(this.progress); @override Path getClip(Size size) { final path = Path(); // 随着progress从0到1,裁剪区域向左上角收缩 final x = size.width * progress; final y = size.height * progress; path.moveTo(x, y); path.lineTo(size.width, 0); path.lineTo(size.width, size.height); path.lineTo(0, size.height); path.lineTo(x, y); return path; } @override bool shouldReclip(_LeftCurtainClipper oldClipper) { return oldClipper.progress != progress; } } class _RightCurtainClipper extends CustomClipper<Path> { final double progress; _RightCurtainClipper(this.progress); @override Path getClip(Size size) { final path = Path(); // 随着progress从0到1,裁剪区域向右下角收缩 final x = size.width * (1 - progress); final y = size.height * (1 - progress); path.moveTo(x, y); path.lineTo(0, 0); path.lineTo(0, size.height); path.lineTo(size.width, size.height); path.lineTo(x, y); return path; } @override bool shouldReclip(_RightCurtainClipper oldClipper) { return oldClipper.progress != progress; } }
2. 自定义对角窗帘过渡路由
创建一个继承自PageRouteBuilder的自定义路由,在过渡逻辑中同时渲染新页面(底层)和旧页面的窗帘部分(上层):
class DiagonalCurtainPageRoute extends PageRouteBuilder { final Widget destination; DiagonalCurtainPageRoute({required this.destination}) : super( transitionDuration: const Duration(milliseconds: 800), pageBuilder: (context, animation, secondaryAnimation) => destination, transitionsBuilder: (context, animation, secondaryAnimation, child) { // 获取旧页面的实例 final oldPage = ModalRoute.of(context)?.previousContentWidget; if (oldPage == null) return child; // 定义窗帘动画的进度值(从0到1) final curtainAnimation = Tween<double>(begin: 0, end: 1).animate( CurvedAnimation(parent: animation, curve: Curves.easeInOut), ); return Stack( children: [ // 新页面始终显示在底层 child, // 旧页面的两个窗帘部分,随动画拉开 AnimatedBuilder( animation: curtainAnimation, builder: (context, _) { return Stack( children: [ ClipPath( clipper: _LeftCurtainClipper(curtainAnimation.value), child: oldPage, ), ClipPath( clipper: _RightCurtainClipper(curtainAnimation.value), child: oldPage, ), ], ); }, ), ], ); }, ); }
3. 使用自定义路由跳转
在需要跳转的地方,用这个自定义路由替代默认的MaterialPageRoute:
// 示例:在首页按钮点击时跳转 ElevatedButton( onPressed: () { Navigator.push( context, DiagonalCurtainPageRoute(destination: const NewPage()), ); }, child: const Text('打开新页面'), )
关键原理说明
- 同时渲染两个页面:自定义路由中用
Stack布局让新页面始终在底层,旧页面的裁剪部分在上方,动画过程中两者同时存在。 - 裁剪动画:通过
ClipPath和动画进度值动态修改旧页面的显示区域,模拟窗帘向对角拉开的效果。 - 避免延迟跳转:不需要等动画结束再跳转,过渡逻辑完全由路由管理,动画开始时新页面就已渲染完成。
这样实现后,点击跳转按钮时,新页面会立刻显示在旧页面的窗帘下方,随着动画推进,窗帘逐渐拉开,全程不会出现空白区域,完美实现你想要的效果。
内容的提问来源于stack exchange,提问作者Evgeniy Strokin
相关产品推荐
相关产品推荐

