使用Flutter实现一个Nike的启动加载页

1,731 阅读2分钟

前言

前段时间终于把Getwidget系列组件介绍完毕了,今天打开Nike看着里面的启动载入动画突然灵机一动,我好像可以用Getwidget轻松的写一个出来,话不多说,开整。

准备工作

需要准备一张高清无码的耐克Logo,推荐一个我常用的矢量图下载网站(切记商用要注意版权问题)。

新建项目,导入图片、集成Getwidget。

开始写代码

分析动画

未命名.gif

通过观察发现整体大概有以下要点:

  • 整体背景为黑色,logo及加载指示器为白色。
  • 开屏后首先展示的是一个logo处于屏幕的正中间,初始宽度大约为屏幕宽度的1/3。
  • 经过大概1秒的静止后后logo开始变小,缩放比率大约是0.4,动画时间大约为0.3s。
  • 缩放动画结束时启动外圈的加载指示器。

开始布局

1.为了方便观察,我这里使用了GFBorder先将大概的动画区域圈了出来。

image.png

class NikeScreenView extends StatefulWidget {
    const NikeScreenView({Key? key}) : super(key: key);
    
    @override
    State<NikeScreenView> createState() => _NikeScreenViewState();
}

class _NikeScreenViewState extends State<NikeScreenView> {

    @override
    Widget build(BuildContext context) {
        double size = MediaQuery.of(context).size.width / 3;
        return Container(
                color: Colors.black,
                child: Center(
                    child: GFBorder(
                    color: GFColors.LIGHT,
                    padding: const EdgeInsets.all(0),
                    type: GFBorderType.rect,
                    child: SizedBox(
                        width: size,
                        height: size,
                    ),
                ),
            ),
        );
    }
}

2.使用Getwidget中的动画容器GFAnimation实现logo动画部分:

未命名-2.gif

...
class _NikeScreenViewState extends State<NikeScreenView>
    with TickerProviderStateMixin {
    
late AnimationController controller = AnimationController(
    duration: const Duration(milliseconds: 300),
    lowerBound: 0.4,
    upperBound: 1.0,
    // 因为是缩小动画,所以设置初始值为1.0
    value: 1.0,
    vsync: this);
    
late Animation<double> animation = CurvedAnimation(
    parent: controller, 
    curve: Curves.linear);

@override
void initState() {
    // 1秒后开始执行动画,无需延迟则设置为0
    Future.delayed(const Duration(milliseconds: 1000), () {
        controller.reverse();
    });
    
    super.initState();
}

// 别忘了界面销毁时释放控制器
@override
void dispose() {
    controller.dispose();
    super.dispose();
}
    
@override
...
    // 设置stack为sizedbox的子组件,用于盛放logo及加载器
    Stack(
        alignment: Alignment.center,
        children: [
            GFAnimation(
            scaleAnimation: animation,
            controller: controller,
            type: GFAnimationType.scaleTransition,
            alignment: Alignment.center,
            child: Image.asset(
                'images/nike_logo.png',
            ),
        ),
    ],
),
...

3.使用GFLoader的android样式来处理转圈圈的指示器,但你会发现它并不能直接设置颜色,别急,点开GFLoader会发现其实它内部调用的是CircularProgressIndicator,这是material自带的组件,设置宽度、颜色,然后监听logo动画的状态,在缩放结束时展示加载器。

image.png (写到这里豁然发现完全没必要用Getwidget,靓仔落泪...)

...
    bool isShowLoader = false;
    
    @override
    void initState() {
        Future.delayed(const Duration(milliseconds: 500), () {
            controller.reverse();
        });
    
        // 监听动画执行结束,展示Loader
        controller.addStatusListener((status) {
            if (status == AnimationStatus.dismissed) {
                setState(() {
                    isShowLoader = true;
                });
            }
        });
        super.initState();
    }
...
    Stack(
        alignment: Alignment.center,
        children: [
        ...
        isShowLoader ? SizedBox(
                width: size * 0.7,
                height: size * 0.7,
                child: const CircularProgressIndicator(
                    color: Colors.white,
                    strokeWidth: 2,
                    semanticsValue: 'abc',
                ),
            )
        : const SizedBox(),
        ])
...

4.最后去掉GFBorder看下效果

pm.gif

结语

至此,一个仿Nike的加载页就完成了。 Flutter启动白屏处理请戳