Flutter之简单的闪屏动画

802 阅读4分钟

先展示一下效果图:

这样的效果相信小伙伴在使用某些app的时候会经常看到吧,那让我们来看看用flutter是如何实现的呢?

我们需要明确几点:

1、“Flutter”,“帅帅影视”为两组Text组件,而“帅帅影视”文字颜色不一样是通过Text里两个TextSpan来组合而成的。

2、“Flutter”和“帅帅影视”在动画结束完成之后整体在水平居中的。

3、“Flutter”和“帅帅影视”的起点分别在屏幕的左侧右对齐和屏幕的左侧左对齐,之后再同一个时间段内进行动画移动,移动到整体居中的位置。

实现思路分析:

如果对上边有所理解的话,那么我们接下来思考如何去实现,实现的思路如下:

1、需要获取“Flutter”和“帅帅影视”两个文字的width。获取文字宽度可以通过TextPainter进行绘制测量,或者通过key来获取它的RendBox,从而获取Size。本实例采用第一种方式实现。

2、布局方式,可以通过Stack-position 来动态改变它的left或者right完成动画的实现,同样也可以通过Center-row让组件默认居中之后通过Transform平移完成动画的实现。本实例采用第二个方式。

进行实现:

创建共同动画的Controller,设置执行动画的时间为1s:

_animationController = AnimationController(
    duration: Duration(milliseconds: 1000), vsync: this);

获取文字的width,你设置的字体大小等参数要与你通过TextPainter进行绘制的字体大小等参数一致才可以获取到准确值。

double getTextWidth(String text) {
  final textPainter = TextPainter(
    text: TextSpan(
      text: text,
      style: TextStyle(
        fontSize: 30,
        fontWeight: FontWeight.w600,
      ),
    ),
    textDirection: TextDirection.ltr,
  );
  textPainter.layout(minWidth: 0, maxWidth: double.infinity);
  return textPainter.width;
}

因为两组文字的width是不相等的,所以我们这边需要先获取到它们之间的差值:

final offset = getTextWidth("帅帅影视") - getTextWidth("Flutter");

Transform的原点坐标(0,0)默认为当前组件所在位置的左上角,向左为负值,向右为正值。通过屏幕的宽度和offset的偏移量我们就能分别算出“Flutter”,“帅帅影视”与原点的位置。

“Flutter”组件动画的起始位置X轴的值为 (-screenWidth / 2 - offset / 2

“帅帅影视”组件动画的起始位置X轴的值为 (screenWidth / 2 - offset / 2

它们的终点X轴的值都为0

通过上边的数据就可以分别创建“Flutter”动画和“帅帅影视”动画,设置执行动画的起始点和终止点。(CurvedAnimation 动画是系统提供的插值器, 是系统提供的插值器,Curves.easeIn执行速度由慢变快。系统提供的还有很多就不在这里列举了,具体可以看文档或者源码。同样也支持自定义)

_animLeft = Tween(begin: -screenWidth / 2 - offset / 2, end: 0.toDouble())
    .animate(CurvedAnimation(
        parent: _animationController, curve: Curves.easeIn));
_animRight = Tween(begin: screenWidth / 2 - offset / 2, end: 0.toDouble())
    .animate(CurvedAnimation(
        parent: _animationController, curve: Curves.easeIn));

最后就是通过AnimatedBuilder来进行组装,动画的本质实际上就是不断修改某个属性的值,从而产生动画的效果。在本实例里边就是不断修改Transform.translate的偏移量offset来完成动画的效果的,代码如下:

Container(
  color: AppColor.white,
  alignment: Alignment.center,
  child: Row(
    mainAxisSize: MainAxisSize.min,
    children: <Widget>[
      if (_animLeft != null)
        AnimatedBuilder(
          animation: _animLeft,
          builder: (BuildContext context, Widget child) {
            return Transform.translate(
              offset: Offset(_animLeft.value, 0),
              child: Text(
                'Flutter',
                style: TextStyle(
                  fontSize: 30,
                  color: Colors.black,
                  fontWeight: FontWeight.w600,
                ),
              ),
            );
          },
        ),
      SizedBox(
        width: 5,
      ),
      if (_animRight != null)
        AnimatedBuilder(
          animation: _animRight,
          builder: (BuildContext context, Widget child) {
            return Transform.translate(
              offset: Offset(_animRight.value, 0),
              child: Text.rich(
                TextSpan(
                  text: "帅帅",
                  style: TextStyle(
                    foreground: Paint()
                      ..shader = _textGradient
                          .createShader(Rect.fromLTRB(0, 0, 40, 40)),
                    fontSize: 30,
                    fontWeight: FontWeight.w600,
                  ),
                  children: [
                    TextSpan(
                      text: "影视",
                      style: TextStyle(
                        fontSize: 30,
                        fontWeight: FontWeight.w600,
                        foreground: Paint()..color = Colors.pink[400],
                      ),
                    ),
                  ],
                ),
              ),
            );
          },
        ),
    ],
  ),
),

到这里这个实例就已经完成了,一个很简单的动画效果就出来了。

代码地址:

本实例代码已经用于帅帅影视开源项目闪屏页“SplashPage”中,如有需要可点击链接或者下载源码进行查看。

结束语:

本实例展示了Flutter最基础的动画的实现。对于想学习Flutter的新手来说一定要从基础开始学起,这样对于你在Flutter的成长道路上才会有一个质的提升。作者在闲暇之余正在维护帅帅影视这个开源项目,如果有小伙伴有好的想法想参与进来可以在下方留言,让我们在Flutter的道路上共同成长!