Flutter 之 自定义路由切换动画

Material组件库中提供了一个MaterialPageRoute组件,它可以使用和平台风格一致的路由切换动画,如在iOS上会左右滑动切换,而在Android上会上下滑动切换

1. CupertinoPageRoute

CupertinoPageRoute是Cupertino组件库提供的iOS风格的路由切换组件
如果在Android上也想使用左右切换风格,可以使用CupertinoPageRoute。

 Navigator.push(context, CupertinoPageRoute(  
   builder: (context)=>PageB(),
 ));

示例


class MSCupertinoPageRouteDemo extends StatelessWidget {
  const MSCupertinoPageRouteDemo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("CupertinoPage")),
      body: Container(
        child: Text("Home", textScaleFactor: 2.0),
        alignment: Alignment.center,
        color: Colors.red,
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.pets),
        onPressed: () {
          Navigator.of(context).push(CupertinoPageRoute(builder: (context) {
            return MSRoutePageDetail();
          }));
        },
      ),
    );
  }
}

class MSRoutePageDetail extends StatelessWidget {
  const MSRoutePageDetail({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Detail"),
      ),
      body: Container(
        child: Text("Detail", textScaleFactor: 2.0),
        alignment: Alignment.center,
        color: Colors.amber,
      ),
    );
  }
}

2. PageRouteBuilder

PageRouteBuilder可以自定义路由切换动画

例如我们想以渐隐渐入动画来实现路由过渡

          Navigator.of(context).push(
            PageRouteBuilder(
              transitionDuration: Duration(milliseconds: 2000),
              pageBuilder: (ctx, anim1, anim2) {
                return FadeTransition(
                  opacity: anim1,
                  child: MSRoutePageDetail(),
                );
              },
            ),
          );

class MSPageRouteBuilderDemo extends StatelessWidget {
  const MSPageRouteBuilderDemo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("PageRouteBuilderDemo")),
      body: Container(
        alignment: Alignment(0, 0),
        color: Colors.red,
        child: Text(
          "Home",
          textScaleFactor: 2.0,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.pets),
        onPressed: () {
          Navigator.of(context).push(
            PageRouteBuilder(
              transitionDuration: Duration(milliseconds: 2000),
              pageBuilder: (ctx, anim1, anim2) {
                return FadeTransition(
                  opacity: anim1,
                  child: MSRoutePageDetail(),
                );
              },
            ),
          );
        },
      ),
    );
  }
}

class MSRoutePageDetail extends StatelessWidget {
  const MSRoutePageDetail({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Detail"),
      ),
      body: Container(
        child: Text("Detail", textScaleFactor: 2.0),
        alignment: Alignment.center,
        color: Colors.amber,
      ),
    );
  }
}

我们可以看到pageBuilder 有一个animation参数,这是Flutter路由管理器提供的,在路由切换时pageBuilder在每个动画帧都会被回调,因此我们可以通过animation对象来自定义过渡动画。

3. 自定义 PageRoute

无论是MaterialPageRoute、CupertinoPageRoute,还是PageRouteBuilder,它们都继承自PageRoute类,而PageRouteBuilder其实只是PageRoute的一个包装,我们可以直接继承PageRoute类来实现自定义路由。


class MSFadeRoute extends PageRoute {
  MSFadeRoute(
      {required this.builder,
      this.transitionDuration = const Duration(microseconds: 300),
      this.maintainState = true,
      this.barrierColor,
      this.barrierLabel});

  final WidgetBuilder builder;

  @override
  final Color? barrierColor;

  @override
  final String? barrierLabel;

  @override
  final bool maintainState;

  @override
  final Duration transitionDuration;

  @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) {
    return FadeTransition(opacity: animation, child: builder(context));
  }
}

使用MSFadeRoute

          Navigator.of(context).push(
            MSFadeRoute(
              builder: (ctx) {
                return MSRoutePageDetail();
              },
              transitionDuration: Duration(seconds: 2),
            ),
          );

完整代码


class MSFadeRouteDemo extends StatelessWidget {
  const MSFadeRouteDemo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("MSFadeRouteDemo"),
      ),
      body: Container(
        alignment: Alignment(0, 0),
        color: Colors.red,
        child: Text(
          "Home",
          textScaleFactor: 2.0,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.pets),
        onPressed: () {
          Navigator.of(context).push(
            MSFadeRoute(
              builder: (ctx) {
                return MSRoutePageDetail();
              },
              transitionDuration: Duration(seconds: 2),
            ),
          );
        },
      ),
    );
  }
}

class MSRoutePageDetail extends StatelessWidget {
  const MSRoutePageDetail({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Detail"),
      ),
      body: Container(
        child: Text("Detail", textScaleFactor: 2.0),
        alignment: Alignment.center,
        color: Colors.amber,
      ),
    );
  }
}

class MSFadeRoute extends PageRoute {
  MSFadeRoute(
      {required this.builder,
      this.transitionDuration = const Duration(microseconds: 300),
      this.maintainState = true,
      this.barrierColor,
      this.barrierLabel});

  final WidgetBuilder builder;

  @override
  final Color? barrierColor;

  @override
  final String? barrierLabel;

  @override
  final bool maintainState;

  @override
  final Duration transitionDuration;

  @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) {
    return FadeTransition(opacity: animation, child: builder(context));
  }
}

虽然PageRouteBuilder 和 MSFadeRoute都可以实现自定义切换动画,但实际使用时应优先考虑使用PageRouteBuilder,这样无需定义一个新的路由类,使用起来会比较方便。但是有些时候PageRouteBuilder是不能满足需求的,例如在应用过渡动画时我们需要读取当前路由的一些属性,这时就只能通过继承PageRoute的方式了。

举个例子,假如我们只想在打开新路由时应用动画,而在返回时不使用动画,那么我们在构建过渡动画时就必须判断当前路由isActive属性是否为true,代码如下

@override
Widget buildTransitions(BuildContext context, Animation<double> animation,
    Animation<double> secondaryAnimation, Widget child) {
 //当前路由被激活,是打开新路由
 if(isActive) {
   return FadeTransition(
     opacity: animation,
     child: builder(context),
   );
 }else{
   //是返回,则不应用过渡动画
   return Padding(padding: EdgeInsets.zero);
 }
}

https://book.flutterchina.club/chapter9/route_transition.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值