MaterialPageRoute:从入门到精通

路由导航这件事,说白了就是页面之间的跳转。在Flutter里,MaterialPageRoute是最常用的路由方式。我个人习惯把它当作「默认选项」——除非有特殊需求,否则用它准没错。

基本用法:三行代码搞定跳转

先看一个最简单的例子。从首页跳转到详情页,代码长这样:

Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => DetailPage(),
  ),
);

嗯,就这么简单。builder参数返回你要跳转的目标页面。返回上一页用Navigator.pop(context)

我在项目中遇到过一个问题:新手容易忘记context必须是当前页面的上下文。如果你在异步回调里用Navigator.push,记得先检查context是否还活着。

小技巧:如果你需要传参,直接在目标页面的构造函数里接收就行。比如 DetailPage(id: 123)

页面过渡动画:默认的就很香

MaterialPageRoute自带Material Design风格的过渡动画。Android上是从底部滑入,iOS上是从右侧滑入。你想想看,这其实挺合理的——平台一致性嘛。

但有时候默认动画不太够用。比如我做电商项目时,产品详情页需要从底部弹出,像抽屉一样。这时候就要动点手脚了。

自定义过渡效果:让页面动起来

自定义过渡的核心是重写buildTransitions方法。来看一个从底部滑入的例子:

class BottomSlideRoute extends PageRouteBuilder {
  final Widget page;

  BottomSlideRoute({required this.page})
      : super(
          pageBuilder: (context, animation, secondaryAnimation) => page,
          transitionsBuilder: (context, animation, secondaryAnimation, child) {
            const begin = Offset(0.0, 1.0);
            const end = Offset.zero;
            const curve = Curves.easeInOut;

            var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
            var offsetAnimation = animation.drive(tween);

            return SlideTransition(
              position: offsetAnimation,
              child: child,
            );
          },
        );
}

这里的关键是transitionsBuilder。它接收四个参数:

  • context:构建上下文
  • animation:主动画,值从0到1
  • secondaryAnimation:副动画,用于处理返回时的效果
  • child:目标页面组件

你可以在animation上做各种变换。缩放、旋转、透明度,随便玩。

核心思路:动画的本质就是「在0到1之间插值」。你定义好起始状态和结束状态,Flutter帮你算中间帧。

避坑指南:我曾经踩过的坑

我曾经在自定义过渡动画时,忘记处理secondaryAnimation。结果页面返回时动画卡顿,用户体验很差。后来我加上了secondaryAnimation的监听,问题才解决。

还有一个坑:PageRouteBuildertransitionDuration默认是300毫秒。如果你觉得太快或太慢,可以手动设置:

return MaterialPageRoute(
  transitionDuration: Duration(milliseconds: 500),
  builder: (context) => DetailPage(),
);
注意:过渡时间别设太长。超过1秒用户会觉得卡顿。我一般控制在200-500毫秒之间。

进阶:组合动画效果

有时候单一动画不够炫酷。比如我想让页面从底部滑入的同时,透明度也从0变到1。组合动画用AnimationControllerdrive方法:

transitionsBuilder: (context, animation, secondaryAnimation, child) {
  var slideTween = Tween<Offset>(
    begin: Offset(0.0, 1.0),
    end: Offset.zero,
  );
  var fadeTween = Tween<double>(
    begin: 0.0,
    end: 1.0,
  );

  var slideAnimation = slideTween.animate(animation);
  var fadeAnimation = fadeTween.animate(animation);

  return SlideTransition(
    position: slideAnimation,
    child: FadeTransition(
      opacity: fadeAnimation,
      child: child,
    ),
  );
},

你看,把SlideTransitionFadeTransition嵌套起来就行。外层控制位置,内层控制透明度。

性能优化:别让动画卡顿

自定义动画时,记得用const构造函数。我在项目中遇到过动画掉帧的问题,排查后发现是页面组件没有用const。加上之后,动画流畅多了。

优化点 说明
使用const构造函数 减少重建次数
避免复杂计算 动画帧率60fps,计算要快
使用RepaintBoundary 隔离重绘区域
个人经验:如果你发现动画卡顿,先检查是不是有setState在频繁触发。动画期间尽量减少状态更新。

总结一下

MaterialPageRoute是Flutter路由的基石。默认动画够用,自定义也不难。你想想看,掌握了这些,大部分页面跳转场景都能搞定。

最后提醒一句:动画是为了提升体验,不是为了炫技。我见过有人把页面跳转做成3D翻转,结果用户晕头转向。嗯,适可而止就好。