Flutter Dojo设计之道——骚气的闪屏动画是如何实现的

Android群英传

共 5013字,需浏览 11分钟

 ·

2020-08-06 20:28

这篇文章是对Flutter动画实现思路的一篇剖析,用一个简单的动画,分析Flutter创建动画的一般步骤

闪屏,实际上有两个作用。

  • 宣传。通过Logo、广告等形式,在启动时,展示要宣传的广告等内容。
  • 后台初始化。借助这个时间做一些后台操作,初始化一些SDK或者代码。

Flutter Dojo的闪屏动画,参考了著名大厂——P站的App闪屏,相信大家应该都不陌生。

动画其实比较简单,只是一个从两边向中间靠拢的动画。

一般来说,Flutter的动画创作,有下面几个步骤。

  1. 创建静态布局
  2. 创建Tween,标记动画的起始值
  3. 给静态代码添加AnimatedBuilder,驱动动画

静态布局

这个布局没有什么太大难度,这个效果其实有很多实现方案,比如Center-Row的方式,让【Flutter】Text和【Dojo】Text在Row中居中即可。或者可以用Stack-Positioned的方式,通过left、right来定位。

相比来说,Center-Row的方式会比较直观,所以我这里准备使用Stack-Positioned的方式来进行演示。

不管使用哪种方案,需要注意的一点是,【Flutter】Text和【Dojo】Text是整体居中的,并不是分别居中,因为【Flutter】Text比【Dojo】Text要长,所以沿屏幕中线居中会很不协调。

布局之外,需要稍微提下【Dojo】Text的实现,实际上就是通过BoxDecoration来实现的,代码如下所示。

decoration: BoxDecoration(
  borderRadius: BorderRadius.circular(8),
  color: Color.fromARGB(255, 253, 152, 39),
),

定义动画

这里的动画分为两部分,左边和右边,如果使用Center-Row的方式,由于两个Text并不在屏幕中线对齐,所以实际上是有个offset的,然后再通过Transform.translate来进行偏移。另一种方式,Stack-Positioned实际上也是如此,但是可以通过Positioned中的left和right来进行动画。

所以首先一步,需要获取【Flutter】Text和【Dojo】Text的宽度差,这里又有多种方式来获取一个Widget的Size了。

  • LayoutBuilder。由于需要提前创建动画,所以这个方案不是很好。
  • TextPainter。对于文字,可以使用TextPainter来进行文本的测量。
  • Key。通过Key来获取RenderBox,从而获取Widget的Size。

Key的方式比较简单,所以这里我准备用TextPainter的方式来演示。下面这个函数就演示了如何获取一个特定TextStyle下Text的计算宽度。

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

经过简单的计算,【Flutter】Text和【Dojo】Text偏移的值实际上就是两个文本的宽度差的一半。

由于前面使用的是Stack-Positioned的方式进行的布局,所以动画也需要根据静态布局来定义。

先看【Flutter】Text的动画,它从屏幕左边作用到中间带偏移的地方,所以其动画值的范围是:

begin: screenWidth, end: screenWidth / 2 - offset

相应的,【Dojo】Text的动画,也类似:

begin: screenWidth, end: screenWidth / 2 + offset

动画管理

在确定的动画值的范围之后,实际上Tween就已经确定了,这里介绍一个动画管理的技巧,通过一个类来封装Widget所需要的不同的Tween,这样可以将动画的逻辑和Widget进行解耦,代码如下所示。

import 'package:flutter/material.dart';

class SplashAnimManager {
  final AnimationController controller;
  final Animation animLeft;
  final Animation animRight;
  final double screenWidth;
  final double offset;

  SplashAnimManager(this.controller, this.screenWidth, this.offset)
      : animLeft = Tween(begin: screenWidth, end: screenWidth / 2 - offset).animate(
          CurvedAnimation(
            parent: controller,
            curve: Curves.easeIn,
          ),
        ),
        animRight = Tween(begin: screenWidth, end: screenWidth / 2 + offset).animate(
          CurvedAnimation(
            parent: controller,
            curve: Curves.easeIn,
          ),
        );
}

这里仅仅是为了演示这种动画管理的思想,才将仅仅两个动画写在了管理类中。

实际上Flutter Dojo中有很多地方都是这样,不仅仅可以从App上学习Flutter的相关知识,通过阅读Dojo的源码,你会发现更多。

动画组装

最后就是通过AnimatedBuilder来进行组装,动画的本质实际上就是不断修改某个属性的值,从而产生动画的效果。【Flutter】Text和【Dojo】Text也是一样,以【Flutter】Text为例,实际上就是right属性从Tween的begin到end进行变化,所以,给静态布局套上AnimatedBuilder,再给相应的属性设置Tween的值就可以了,代码如下所示。

@override
Widget build(BuildContext context) {
  final double screenWidth = MediaQuery.of(context).size.width;
  _splashAnimManager = SplashAnimManager(
    _animationController,
    screenWidth,
    (getTextWidth('Flutter') - getTextWidth('Dojo') - 4) / 2,
  );
  return Container(
    alignment: Alignment.center,
    color: Colors.black,
    child: Stack(
      fit: StackFit.expand,
      alignment: Alignment.center,
      children: [
        AnimatedBuilder(
          animation: _animationController,
          builder: (context, widget) {
            return Positioned(
              right: _splashAnimManager.animLeft.value,
              child: Text(
                'Flutter',
                style: TextStyle(
                  fontSize: 60,
                  color: Colors.white,
                  fontWeight: FontWeight.w600,
                ),
              ),
            );
          },
        ),
        AnimatedBuilder(
          animation: _animationController,
          builder: (context, widget) {
            return Positioned(
              left: _splashAnimManager.animRight.value,
              child: Container(
                padding: EdgeInsets.symmetric(
                  horizontal: 4,
                ),
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(8),
                  color: Color.fromARGB(255, 253, 152, 39),
                ),
                child: Text(
                  'Dojo',
                  style: TextStyle(
                    fontSize: 60,
                    fontWeight: FontWeight.w600,
                    color: Colors.black,
                  ),
                ),
              ),
            );
          },
        ),
      ],
    ),
  );
}

以上,一个骚气的闪屏动画就完成了。

代码地址

https://github.com/xuyisheng/flutter_dojo/blob/master/lib/pages/splash/slpash.dart

修仙

Flutter Dojo开源至今,受到了很多Flutter学习者和爱好者的喜爱,也有越来越多的人加入到Flutter的学习中来,所以我建了个Flutter修仙群,但是人数太多,所以分成了【Flutter修仙指南】【Flutter修仙指北】【Flutter修仙指东】三个群,对Flutter感兴趣的朋友,可以添加我的微信,注明加入Flutter修仙群,或者直接关注我的微信公众号【Android群英传】。

   

项目地址:

https://github.com/xuyisheng/flutter_dojo

- END -


浏览 42
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报