[- Flutter福利篇 -] Hero转场组件共享 — 附赠-路由动画工具类

时间:2022-07-27
本文章向大家介绍[- Flutter福利篇 -] Hero转场组件共享 — 附赠-路由动画工具类,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

最近比较忙,很少发文章了。今天抽空把Hero的转场组件共享说一下 顺便发个转场的动画福利类,可以定义转场的时间,曲线,别客气,随便拿去用。 废话不多说,先看图:


  • 1.透明:FadeRouter

  • 2.缩放:ScaleRouter

  • 3.旋转:RotateRouter

  • 4.透明+缩放+旋转
  • 5.右--->左:Right2LeftRouter

  • 6.左--->右:Left2RightRouter

  • 7.上--->下:Top2BottomRouter

  • 8.下--->上:Bottom2TopRouter

Hero元素共享

也许上面吸引你的不是界面跳转的动画,而是那个头像神奇般的轨迹。下面就来说一下如何实现。

起始页:OriginPage
class OriginPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
      var hero= Hero(//----定义一个Hero,并添加tag标签,此中组件共享
        tag: 'user-head',
        child: ClipRRect(
          borderRadius: BorderRadius.all(Radius.circular(30)),
          child: Image.asset(
            "images/mani.jpg",width: 60,height: 60,fit: BoxFit.cover,
          ),
        ),
      );

      var container= Container(//容器,没啥好说的
        alignment: Alignment(-0.8, -0.8),
        child: hero,
        width: 250,
        height: 250*0.618,
        decoration: BoxDecoration(//添加渐变色
        gradient: LinearGradient(colors: [Colors.red.withAlpha(99),Colors.yellow.withAlpha(189),Colors.green.withAlpha(88),Colors.blue.withAlpha(230)])
      ),);

    return Scaffold(
      body: Center(//点击跳转
        child: GestureDetector(child: Card(elevation:5,child:container ,),onTap: (){
          _toNext(context);
        },),
      ),
    );
  }

  void _toNext(context) {//跳转路由
    Navigator.push(
        context,
//      FadeRouter(child:TargetPage()),
//      ScaleRouter(child:TargetPage()),
//      RotateRouter(child:TargetPage()),
//      ScaleFadeRotateRouter(child:TargetPage()),
//      Right2LeftRouter(child:TargetPage()),
//      Left2RightRouter(child:TargetPage()),
//      Top2BottomRouter(child:TargetPage()),
      Bottom2TopRouter(child:TargetPage()),//跳转+动画
    );
  }
}
目标页:TargetPage
class TargetPage extends StatelessWidget {
  
  @override
  Widget build(BuildContext context) {
    var hero=Hero(//----定义一个Hero,为其添加标签,两个标签相同,则可以共享
      tag: 'user-head',
      child: Padding(
        padding: EdgeInsets.all(16.0),
        child: CircleAvatar(
          backgroundColor: Colors.transparent,
          radius: 72.0,
          backgroundImage: AssetImage(
            "images/mani.jpg",
          ),
        ),
      ),
    );

    var touch=InkWell(onTap: (){
      Navigator.of(context).pop();
    },child: hero,);

    return Scaffold(
      appBar: AppBar(leading:touch ,),
      body: Container(
        decoration: BoxDecoration(
            gradient: LinearGradient(colors: [Colors.red.withAlpha(99),Colors.yellow.withAlpha(189),Colors.green.withAlpha(88),Colors.blue.withAlpha(230)])
        ),
      ),
    );
  }
}

关于createRectTween属性

在回调中会给两个初始和结束的两个矩形,可以进行矩形动画来控制共享组件的区域,如下图在目标页:

var height=MediaQuery.of(context).size.height;
var width=MediaQuery.of(context).size.width;
var size=min(height,width);

 var hero=Hero(//----定义一个Hero,为其添加标签,两个标签相同,则可以共享
      createRectTween: ((r1,r2){
        return RectTween(begin: Rect.fromLTWH(size/2,0, size, size),end:  r2);
      }),

另外还有个要点:同一页面不能出现多处同名Hero


福利时间:路由动画工具

使用方法看上面的路由跳转处,当然你也可以根据下面的定制更酷炫的跳转效果。

import 'package:flutter/cupertino.dart';

//缩放路由动画
class ScaleRouter extends PageRouteBuilder {
  final Widget child;
  final int duration_ms;
  final Curve curve;
  ScaleRouter({this.child, this.duration_ms = 500,this.curve=Curves.fastOutSlowIn})
      : super(
    pageBuilder: (context, animation, secondaryAnimation) => child,
    transitionDuration: Duration(milliseconds: duration_ms),
    transitionsBuilder: (context, a1, a2, child) =>
        ScaleTransition(
          scale: Tween(begin: 0.0, end: 1.0).animate(
              CurvedAnimation(parent: a1, curve: curve)),
          child: child,
        ),
  );
}
//渐变透明路由动画
class FadeRouter extends PageRouteBuilder {
  final Widget child;
  final int duration_ms;
  final Curve curve;
  FadeRouter({this.child, this.duration_ms = 500,this.curve=Curves.fastOutSlowIn})
      : super(
      pageBuilder: (context, animation, secondaryAnimation) => child,
      transitionDuration: Duration(milliseconds: duration_ms),
      transitionsBuilder: (context, a1, a2, child) =>
          FadeTransition(
            opacity: Tween(begin: 0.1, end: 1.0).animate(
                CurvedAnimation(parent: a1, curve:curve,)),
            child: child,
          ));
}

//旋转路由动画
class RotateRouter extends PageRouteBuilder {
  final Widget child;
  final int duration_ms;
  final Curve curve;
  RotateRouter({this.child, this.duration_ms = 500,this.curve=Curves.fastOutSlowIn})
      : super(
      pageBuilder: (context, animation, secondaryAnimation) => child,
      transitionDuration: Duration(milliseconds: duration_ms),
      transitionsBuilder: (context, a1, a2, child) =>
          RotationTransition(
            turns: Tween(begin: 0.1, end: 1.0).animate(
                CurvedAnimation(parent: a1, curve:curve,)),
            child: child,
          ));
}

//右--->左
class Right2LeftRouter extends PageRouteBuilder {
  final Widget child;
  final int duration_ms;
  final Curve curve;
  Right2LeftRouter({this.child,this.duration_ms=500,this.curve=Curves.fastOutSlowIn})
      :super(
      transitionDuration:Duration(milliseconds: duration_ms),
      pageBuilder:(ctx,a1,a2){return child;},
      transitionsBuilder:(ctx,a1,a2,Widget child,) {
        return SlideTransition(
            position: Tween(
              begin: Offset(1.0, 0.0), end: Offset(0.0, 0.0),).animate(
                CurvedAnimation(parent: a1, curve: curve)),
            child:  child
        );
      });
}

//左--->右
class Left2RightRouter extends PageRouteBuilder {
  final Widget child;
  final int duration_ms;
  final Curve curve;
  List mapper;
  Left2RightRouter({this.child,this.duration_ms=500,this.curve=Curves.fastOutSlowIn})
      :assert(true),super(
      transitionDuration:Duration(milliseconds: duration_ms),
      pageBuilder:(ctx,a1,a2){return child;},
      transitionsBuilder:(ctx,a1,a2,Widget child,) {
        return SlideTransition(
            position: Tween(
              begin: Offset(-1.0, 0.0), end: Offset(0.0, 0.0),).animate(
                CurvedAnimation(parent: a1, curve: curve)),
            child:  child
        );
      });
}

//上--->下
class Top2BottomRouter extends PageRouteBuilder {
  final Widget child;
  final int duration_ms;
  final Curve curve;
  Top2BottomRouter({this.child,this.duration_ms=500,this.curve=Curves.fastOutSlowIn})
      :super(
      transitionDuration:Duration(milliseconds: duration_ms),
      pageBuilder:(ctx,a1,a2){return child;},
      transitionsBuilder:(ctx,a1,a2,Widget child,) {
        return SlideTransition(
            position: Tween(
              begin: Offset(0.0,-1.0), end: Offset(0.0, 0.0),).animate(
                CurvedAnimation(parent: a1, curve: curve)),
            child:  child
        );
      });
}

//下--->上
class Bottom2TopRouter extends PageRouteBuilder {
  final Widget child;
  final int duration_ms;
  final Curve curve;
  Bottom2TopRouter({this.child,this.duration_ms=500,this.curve=Curves.fastOutSlowIn})
      :super(
      transitionDuration:Duration(milliseconds: duration_ms),
      pageBuilder:(ctx,a1,a2){return child;},
      transitionsBuilder:(ctx,a1,a2,Widget child,) {
        return SlideTransition(
            position: Tween(
              begin: Offset(0.0, 1.0), end: Offset(0.0, 0.0),).animate(
                CurvedAnimation(parent: a1, curve: curve)),
            child:  child
        );
      });
}

//缩放+透明+旋转路由动画
class ScaleFadeRotateRouter extends PageRouteBuilder {
  final Widget child;
  final int duration_ms;
  final Curve curve;
  ScaleFadeRotateRouter({this.child, this.duration_ms = 500,this.curve=Curves.fastOutSlowIn}) : super(
      transitionDuration: Duration(milliseconds: duration_ms),
      pageBuilder: (ctx, a1, a2)=>child,//页面
      transitionsBuilder: (ctx, a1, a2, Widget child,) {//构建动画
        return RotationTransition(//旋转动画
          turns: Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
            parent: a1,
            curve: curve,
          )),
          child: ScaleTransition(//缩放动画
            scale: Tween(begin: 0.0, end: 1.0).animate(
                CurvedAnimation(parent: a1, curve: curve)),
            child: FadeTransition(opacity://透明度动画
            Tween(begin: 0.5, end: 1.0).animate(CurvedAnimation(parent: a1, curve: curve)),
              child: child,),
          ),
        );
      });
}
//无动画
class NoAnimRouter extends PageRouteBuilder {
  final Widget page;
  NoAnimRouter(this.page)
      : super(
      opaque: false,
      pageBuilder: (context, animation, secondaryAnimation) => page,
      transitionDuration: Duration(milliseconds: 0),
      transitionsBuilder:
          (context, animation, secondaryAnimation, child) => child);
}