欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Flutter實(shí)現(xiàn)簡(jiǎn)單的下載按鈕動(dòng)畫(huà)

 更新時(shí)間:2023年05月24日 16:22:00   作者:程序那些事  
我們?cè)赼pp的開(kāi)發(fā)過(guò)程中經(jīng)常會(huì)用到一些表示進(jìn)度類(lèi)的動(dòng)畫(huà)效果,比如一個(gè)下載按鈕,那么在flutter中一個(gè)下載按鈕的動(dòng)畫(huà)應(yīng)該如何制作呢,一起來(lái)看看吧

簡(jiǎn)介

我們?cè)赼pp的開(kāi)發(fā)過(guò)程中經(jīng)常會(huì)用到一些表示進(jìn)度類(lèi)的動(dòng)畫(huà)效果,比如一個(gè)下載按鈕,我們希望按鈕能夠動(dòng)態(tài)顯示下載的進(jìn)度,這樣可以給用戶一些直觀的印象,那么在flutter中一個(gè)下載按鈕的動(dòng)畫(huà)應(yīng)該如何制作呢?

一起來(lái)看看吧。

定義下載的狀態(tài)

我們?cè)谡嬲_(kāi)發(fā)下載按鈕之前,首先定義幾個(gè)下載的狀態(tài),因?yàn)椴煌南螺d狀態(tài)導(dǎo)致的按鈕展示樣子也是不一樣的,我們用下面的一個(gè)枚舉類(lèi)來(lái)設(shè)置按鈕的下載狀態(tài):

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

基本上有4個(gè)狀態(tài),分別是沒(méi)有下載,準(zhǔn)備下載但是還沒(méi)有獲取到下載的資源鏈接,獲取到下載資源正在下載中,最后是下載完畢。

定義DownloadButton的屬性

這里我們需要自定義一個(gè)DownloadButton組件,這個(gè)組件肯定是一個(gè)StatelessWidget,所有的狀態(tài)信息都是由外部傳入的。

我們需要根據(jù)下載狀態(tài)來(lái)指定DownloadButton的樣式,所以需要一個(gè)status屬性。下載過(guò)程中還有一個(gè)下載的進(jìn)度條,所以我們需要一個(gè)downloadProgress屬性。

另外在點(diǎn)擊下載按鈕的時(shí)候會(huì)觸發(fā)onDownload事件,下載過(guò)程中可以觸發(fā)onCancel事件,下載完畢之后可以出發(fā)onOpen事件。

最后因?yàn)槭且粋€(gè)動(dòng)畫(huà)組件,所以還需要一個(gè)動(dòng)畫(huà)的持續(xù)時(shí)間屬性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的屬性可以動(dòng)態(tài)變化

上面提到了DownloadButton是一個(gè)StatelessWidget,所有的屬性都是由外部傳入的,但是對(duì)于一個(gè)動(dòng)畫(huà)的DownloadButton來(lái)說(shuō),status,downloadProgress這些信息都是會(huì)動(dòng)態(tài)變化的,那么怎么才能讓變化的屬性傳到DownloadButton中進(jìn)行組件的重繪呢?

因?yàn)樯婕暗綇?fù)雜的狀態(tài)變化,所以簡(jiǎn)單的AnimatedWidget已經(jīng)滿足不了我們的需求了,這里就需要用到flutter中的AnimatedBuilder組件了。

AnimatedBuilder是AnimatedWidget的子類(lèi),它有兩個(gè)必須的參數(shù),分別是animation和builder。

其中animation是一個(gè)Listenable對(duì)象,它可以是Animation,ChangeNotifier或者等。

AnimatedBuilder會(huì)通過(guò)監(jiān)聽(tīng)animation的變動(dòng)情況,來(lái)重新構(gòu)建builder中的組件。buidler方法可以從animation中獲取對(duì)應(yīng)的變動(dòng)屬性。

這樣我們創(chuàng)建一個(gè)Listenable的DownloadController對(duì)象,然后把DownloadButton用AnimatedBuilder封裝起來(lái),就可以實(shí)時(shí)監(jiān)測(cè)到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是一個(gè)Listenable對(duì)象,這里我們讓他實(shí)現(xiàn)ChangeNotifier接口,并且定義了兩個(gè)獲取下載狀態(tài)和下載進(jìn)度的方法,同時(shí)也定義了三個(gè)點(diǎn)擊觸發(fā)事件:

abstract class DownloadController implements ChangeNotifier  {
  DownloadStatus get downloadStatus;
  double get progress;
  void startDownload();
  void stopDownload();
  void openDownload();
}

接下來(lái)我們來(lái)實(shí)現(xiàn)這個(gè)抽象方法:

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這兩個(gè)方法是跟下載狀態(tài)和下載進(jìn)度相關(guān)的,先看下stopDownload:

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

可以看到這個(gè)方法最后需要調(diào)用notifyListeners來(lái)通知AnimatedBuilder來(lái)進(jìn)行組件的重繪。

startDownload方法會(huì)復(fù)雜一點(diǎn),我們需要模擬下載狀態(tài)的變化和進(jìn)度的變化,如下所示:

  Future<void> _doDownload() async {
    _isDownloading = true;
    _downloadStatus = DownloadStatus.fetchingDownload;
    notifyListeners();
    // fetch耗時(shí)1秒鐘
    await Future<void>.delayed(const Duration(seconds: 1));
    if (!_isDownloading) {
      return;
    }
    // 轉(zhuǎn)換到下載的狀態(tài)
    _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;
    }
    //切換到下載完畢狀態(tài)
    _downloadStatus = DownloadStatus.downloaded;
    _isDownloading = false;
    notifyListeners();
  }
}

因?yàn)橄螺d是一個(gè)比較長(zhǎng)的過(guò)程,所以這里用的是異步方法,在異步方法中進(jìn)行通知。

定義DownloadButton的細(xì)節(jié)

有了可以動(dòng)態(tài)變化的狀態(tài)和進(jìn)度之后,我們就可以在DownloadButton中構(gòu)建具體的頁(yè)面展示了。

在未開(kāi)始下載之前,我們希望downloadButton是一個(gè)長(zhǎng)條形的按鈕,按鈕上的文字顯示GET,下載過(guò)程中希望是一個(gè)類(lèi)似CircularProgressIndicator的動(dòng)畫(huà),可以根據(jù)下載進(jìn)度來(lái)動(dòng)態(tài)變化。

同時(shí),在下載過(guò)程中,我們希望能夠隱藏之前的長(zhǎng)條形按鈕。 下載完畢之后,再次展示長(zhǎng)條形按鈕,這時(shí)候按鈕上的文字顯示為OPEN。

因?yàn)閯?dòng)畫(huà)比較復(fù)雜,所以我們將動(dòng)畫(huà)組件分成兩部分,第一部分就是展示和隱藏長(zhǎng)條形的按鈕,這里我們使用AnimatedOpacity來(lái)實(shí)現(xiàn)文字的淡入淡出的效果,并將AnimatedOpacity封裝在AnimatedContainer中,實(shí)現(xiàn)decoration的動(dòng)畫(huà)效果:

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,
            ),
          ),
        ),
      ),
    );

實(shí)現(xiàn)效果如下所示:

接下來(lái)再處理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來(lái)實(shí)現(xiàn)CircularProgressIndicator根據(jù)不同progress的動(dòng)畫(huà)效果。

因?yàn)樵谙螺d過(guò)程中,還有停止的功能,所以我們?cè)贑ircularProgressIndicator上再放一個(gè)stop icon,最后將這個(gè)stack封裝在AnimatedOpacity中,實(shí)現(xiàn)整體的一個(gè)淡入淡出功能:

         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,
                    ),
                ],
              ),
            ),

總結(jié)

這樣,我們一個(gè)動(dòng)畫(huà)的下載按鈕就制作完成了,效果如下:

本文的例子:https://github.com/ddean2009/learn-flutter.git

到此這篇關(guān)于Flutter實(shí)現(xiàn)簡(jiǎn)單的下載按鈕動(dòng)畫(huà)的文章就介紹到這了,更多相關(guān)Flutter下載按鈕動(dòng)畫(huà)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論