flutter系列之:做一个下载按钮的动画

这篇具有很好参考价值的文章主要介绍了flutter系列之:做一个下载按钮的动画。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录
  • 简介
  • 定义下载的状态
  • 定义DownloadButton的属性
  • 让DownloadButton的属性可以动态变化
  • 定义downloadController
  • 定义DownloadButton的细节
  • 总结

简介

我们在app的开发过程中经常会用到一些表示进度类的动画效果,比如一个下载按钮,我们希望按钮能够动态显示下载的进度,这样可以给用户一些直观的印象,那么在flutter中一个下载按钮的动画应该如何制作呢?

一起来看看吧。

定义下载的状态

我们在真正开发下载按钮之前,首先定义几个下载的状态,因为不同的下载状态导致的按钮展示样子也是不一样的,我们用下面的一个枚举类来设置按钮的下载状态:

enum DownloadStatus {
  notDownloaded,
  fetchingDownload,
  downloading,
  downloaded,
}

基本上有4个状态,分别是没有下载,准备下载但是还没有获取到下载的资源链接,获取到下载资源正在下载中,最后是下载完毕。

定义DownloadButton的属性

这里我们需要自定义一个DownloadButton组件,这个组件肯定是一个StatelessWidget,所有的状态信息都是由外部传入的。

我们需要根据下载状态来指定DownloadButton的样式,所以需要一个status属性。下载过程中还有一个下载的进度条,所以我们需要一个downloadProgress属性。

另外在点击下载按钮的时候会触发onDownload事件,下载过程中可以触发onCancel事件,下载完毕之后可以出发onOpen事件。

最后因为是一个动画组件,所以还需要一个动画的持续时间属性transitionDuration。

所以我们的DownloadButton需要下面一些属性:

class DownloadButton extends StatelessWidget {
  ...
  const DownloadButton({
    super.key,
    required this.status,
    this.downloadProgress = 0.0,
    required this.onDownload,
    required this.onCancel,
    required this.onOpen,
    this.transitionDuration = const Duration(milliseconds: 500),
  });

让DownloadButton的属性可以动态变化

上面提到了DownloadButton是一个StatelessWidget,所有的属性都是由外部传入的,但是对于一个动画的DownloadButton来说,status,downloadProgress这些信息都是会动态变化的,那么怎么才能让变化的属性传到DownloadButton中进行组件的重绘呢?

因为涉及到复杂的状态变化,所以简单的AnimatedWidget已经满足不了我们的需求了,这里就需要用到flutter中的AnimatedBuilder组件了。

AnimatedBuilder是AnimatedWidget的子类,它有两个必须的参数,分别是animation和builder。

其中animation是一个Listenable对象,它可以是Animation,ChangeNotifier或者等。

AnimatedBuilder会通过监听animation的变动情况,来重新构建builder中的组件。buidler方法可以从animation中获取对应的变动属性。

这样我们创建一个Listenable的DownloadController对象,然后把DownloadButton用AnimatedBuilder封装起来,就可以实时监测到downloadStatus和downloadProgress的变化了。

如下所示:

Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('下载按钮')),
      body: Center(
        child: SizedBox(
          width: 96,
          child: AnimatedBuilder(
            animation: _downloadController,
            builder: (context, child) {
              return DownloadButton(
                status: _downloadController.downloadStatus,
                downloadProgress: _downloadController.progress,
                onDownload: _downloadController.startDownload,
                onCancel: _downloadController.stopDownload,
                onOpen: _downloadController.openDownload,
              );
            },
          ),
        ),
      ),
    );
  }

定义downloadController

downloadController是一个Listenable对象,这里我们让他实现ChangeNotifier接口,并且定义了两个获取下载状态和下载进度的方法,同时也定义了三个点击触发事件:

abstract class DownloadController implements ChangeNotifier  {
  DownloadStatus get downloadStatus;
  double get progress;

  void startDownload();
  void stopDownload();
  void openDownload();
}

接下来我们来实现这个抽象方法:

class MyDownloadController extends DownloadController
    with ChangeNotifier {
  MyDownloadController({
    DownloadStatus downloadStatus = DownloadStatus.notDownloaded,
    double progress = 0.0,
    required VoidCallback onOpenDownload,
  })  : _downloadStatus = downloadStatus,
        _progress = progress,
        _onOpenDownload = onOpenDownload;

startDownload,stopDownload这两个方法是跟下载状态和下载进度相关的,先看下stopDownload:

  void stopDownload() {
    if (_isDownloading) {
      _isDownloading = false;
      _downloadStatus = DownloadStatus.notDownloaded;
      _progress = 0.0;
      notifyListeners();
    }
  }

可以看到这个方法最后需要调用notifyListeners来通知AnimatedBuilder来进行组件的重绘。

startDownload方法会复杂一点,我们需要模拟下载状态的变化和进度的变化,如下所示:

  Future<void> _doDownload() async {
    _isDownloading = true;
    _downloadStatus = DownloadStatus.fetchingDownload;
    notifyListeners();

    // fetch耗时1秒钟
    await Future<void>.delayed(const Duration(seconds: 1));

    if (!_isDownloading) {
      return;
    }

    // 转换到下载的状态
    _downloadStatus = DownloadStatus.downloading;
    notifyListeners();

    const downloadProgressStops = [0.0, 0.15, 0.45, 0.8, 1.0];
    for (final progress in downloadProgressStops) {
      await Future<void>.delayed(const Duration(seconds: 1));
      if (!_isDownloading) {
        return;
      }
      //更新progress
      _progress = progress;
      notifyListeners();
    }

    await Future<void>.delayed(const Duration(seconds: 1));
    if (!_isDownloading) {
      return;
    }
    //切换到下载完毕状态
    _downloadStatus = DownloadStatus.downloaded;
    _isDownloading = false;
    notifyListeners();
  }
}

因为下载是一个比较长的过程,所以这里用的是异步方法,在异步方法中进行通知。

定义DownloadButton的细节

有了可以动态变化的状态和进度之后,我们就可以在DownloadButton中构建具体的页面展示了。

在未开始下载之前,我们希望downloadButton是一个长条形的按钮,按钮上的文字显示GET,下载过程中希望是一个类似CircularProgressIndicator的动画,可以根据下载进度来动态变化。

同时,在下载过程中,我们希望能够隐藏之前的长条形按钮。 下载完毕之后,再次展示长条形按钮,这时候按钮上的文字显示为OPEN。

因为动画比较复杂,所以我们将动画组件分成两部分,第一部分就是展示和隐藏长条形的按钮,这里我们使用AnimatedOpacity来实现文字的淡入淡出的效果,并将AnimatedOpacity封装在AnimatedContainer中,实现decoration的动画效果:

  return AnimatedContainer(
      duration: transitionDuration,
      curve: Curves.ease,
      width: double.infinity,
      decoration: shape,
      child: Padding(
        padding: const EdgeInsets.symmetric(vertical: 6),
        child: AnimatedOpacity(
          duration: transitionDuration,
          opacity: isDownloading || isFetching ? 0.0 : 1.0,
          curve: Curves.ease,
          child: Text(
            isDownloaded ? 'OPEN' : 'GET',
            textAlign: TextAlign.center,
            style: Theme.of(context).textTheme.button?.copyWith(
              fontWeight: FontWeight.bold,
              color: CupertinoColors.activeBlue,
            ),
          ),
        ),
      ),
    );

实现效果如下所示:

flutter系列之:做一个下载按钮的动画

接下来再处理CircularProgressIndicator的部分:

 Widget build(BuildContext context) {
    return AspectRatio(
      aspectRatio: 1,
      child: TweenAnimationBuilder<double>(
        tween: Tween(begin: 0, end: downloadProgress),
        duration: const Duration(milliseconds: 200),
        builder: (context, progress, child) {
          return CircularProgressIndicator(
            backgroundColor: isDownloading
                ? CupertinoColors.lightBackgroundGray
                : Colors.white.withOpacity(0),
            valueColor: AlwaysStoppedAnimation(isFetching
                ? CupertinoColors.lightBackgroundGray
                : CupertinoColors.activeBlue),
            strokeWidth: 2,
            value: isFetching ? null : progress,
          );
        },
      ),
    );
  }

这里使用的是TweenAnimationBuilder来实现CircularProgressIndicator根据不同progress的动画效果。

因为在下载过程中,还有停止的功能,所以我们在CircularProgressIndicator上再放一个stop icon,最后将这个stack封装在AnimatedOpacity中,实现整体的一个淡入淡出功能:

         Positioned.fill(
            child: AnimatedOpacity(
              duration: transitionDuration,
              opacity: _isDownloading || _isFetching ? 1.0 : 0.0,
              curve: Curves.ease,
              child: Stack(
                alignment: Alignment.center,
                children: [
                  ProgressIndicatorWidget(
                    downloadProgress: downloadProgress,
                    isDownloading: _isDownloading,
                    isFetching: _isFetching,
                  ),
                  if (_isDownloading)
                    const Icon(
                      Icons.stop,
                      size: 14,
                      color: CupertinoColors.activeBlue,
                    ),
                ],
              ),
            ),

总结

这样,我们一个动画的下载按钮就制作完成了,效果如下:

flutter系列之:做一个下载按钮的动画

本文的例子:https://github.com/ddean2009/learn-flutter.git文章来源地址https://www.toymoban.com/news/detail-457490.html

到了这里,关于flutter系列之:做一个下载按钮的动画的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • flutter系列之:使用AnimationController来控制动画效果

    目录 简介 构建一个要动画的widget 让图像动起来 总结 之前我们提到了flutter提供了比较简单好用的AnimatedContainer和SlideTransition来进行一些简单的动画效果,但是要完全实现自定义的复杂的动画效果,还是要使用AnimationController。 今天我们来尝试使用AnimationController来实现一个拖

    2024年02月05日
    浏览(39)
  • Matlab/Python教程系列 | 根据目录下的已有图片制作视频(动画)

    注1:本文系“MATLAB/Python编程教程”系列之一,致力于使用Python和Matlab实现特定的功能。本次要实现的功能是:根据目录下的已有图片制作视频(动画)。 在这个教程中,我们将一起学习如何使用MATLAB和Python编程语言,根据目录下的已有图片制作视频(动画)。我们将从头开始,逐

    2024年02月09日
    浏览(45)
  • flutter系列之:做一个图像滤镜

    很多时候,我们需要一些特效功能,比如给图片做个滤镜什么的,如果是h5页面,那么我们可以很容易的通过css滤镜来实现这个功能。 那么如果在flutter中,如果要实现这样的滤镜功能应该怎么处理呢?一起来看看吧。 在继续进行之前,我们先来讨论下本章到底要做什么。最

    2024年02月09日
    浏览(77)
  • flutter系列之:做一个会飞的菜单

    目录 简介 定义一个菜单项目 让menu动起来 添加菜单内部的动画 总结 flutter中自带了drawer组件,可以实现通用的菜单功能,那么有没有一种可能,我们可以通过自定义动画来实现一个别样的菜单呢? 答案是肯定的,一起来看看吧。 因为这里的主要目的是实现菜单的动画,所以

    2024年02月08日
    浏览(35)
  • 【Linux系列】linux下如何将一个zip文件挂载成一个目录

      由于项目需要,需要将一个zip挂载成一个目录。因此就出现了这个问题。linux下如何将一个zip文件挂载成一个目录 在 Linux 下,可以使用 FUSE(Filesystem in Userspace)技术将一个 ZIP 文件挂载成一个目录,从而实现直接使用 ZIP 文件的效果,而不需要解压缩。 具体来说,可以使用

    2024年02月13日
    浏览(55)
  • 分布式 - 服务器Nginx:基础系列之Nginx简介 | 下载安装 | 启动和停止服务

    Nginx是一个高性能的Web服务器,同时也是一个反向代理服务器。它最初是为了解决C10K问题而开发的,即如何让一个服务器同时处理成千上万个并发连接。Nginx采用了事件驱动的异步非阻塞处理方式,能够高效地处理大量并发连接,因此被广泛应用于高并发的Web应用场景中。 此

    2024年02月10日
    浏览(69)
  • 【Unity游戏开发】动画系统(三)按钮动画

    动画系统是一个比较重要的系统,因为我们做游戏是离不开这个东西的,但是我们又不用自己做动画,而动画系统就是管理美工做的动画,我们加以修饰,使之适配我们的游戏 而按钮动画也是一个点,当然重不重要其实还是你说了算,而按钮动画适配我们的2D和3D游戏 用于

    2024年02月09日
    浏览(56)
  • 分布式 - 服务器Nginx:应知应会系列之Nginx简介 | 下载安装 | 启动和停止服务

    Nginx是一个高性能的Web服务器,同时也是一个反向代理服务器。它最初是为了解决C10K问题而开发的,即如何让一个服务器同时处理成千上万个并发连接。Nginx采用了事件驱动的异步非阻塞处理方式,能够高效地处理大量并发连接,因此被广泛应用于高并发的Web应用场景中。 此

    2024年02月11日
    浏览(63)
  • flutter自定义按钮-文本按钮

    目录 前言 需求 实现 前言 最近闲着无聊学习了flutter的一下知识,发现flutter和安卓之间,页面开发的方式还是有较大的差异的,众所周知,android的页面开发都是写在xml文件中的,而flutter直接写在代码里(dart文件中),从目前我的认识来看,两者在UI上的“套娃”方式,flu

    2024年02月10日
    浏览(36)
  • CSS 弹幕按钮动画

     

    2024年01月20日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包