Flutter實現簡單的下載按鈕動畫
簡介
我們在app的開發(fā)過程中經常會用到一些表示進度類的動畫效果,比如一個下載按鈕,我們希望按鈕能夠動態(tài)顯示下載的進度,這樣可以給用戶一些直觀的印象,那么在flutter中一個下載按鈕的動畫應該如何制作呢?
一起來看看吧。
定義下載的狀態(tài)
我們在真正開發(fā)下載按鈕之前,首先定義幾個下載的狀態(tài),因為不同的下載狀態(tài)導致的按鈕展示樣子也是不一樣的,我們用下面的一個枚舉類來設置按鈕的下載狀態(tài):
enum DownloadStatus { notDownloaded, fetchingDownload, downloading, downloaded, }
基本上有4個狀態(tài),分別是沒有下載,準備下載但是還沒有獲取到下載的資源鏈接,獲取到下載資源正在下載中,最后是下載完畢。
定義DownloadButton的屬性
這里我們需要自定義一個DownloadButton組件,這個組件肯定是一個StatelessWidget,所有的狀態(tài)信息都是由外部傳入的。
我們需要根據下載狀態(tài)來指定DownloadButton的樣式,所以需要一個status屬性。下載過程中還有一個下載的進度條,所以我們需要一個downloadProgress屬性。
另外在點擊下載按鈕的時候會觸發(fā)onDownload事件,下載過程中可以觸發(fā)onCancel事件,下載完畢之后可以出發(fā)onOpen事件。
最后因為是一個動畫組件,所以還需要一個動畫的持續(xù)時間屬性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的屬性可以動態(tài)變化
上面提到了DownloadButton是一個StatelessWidget,所有的屬性都是由外部傳入的,但是對于一個動畫的DownloadButton來說,status,downloadProgress這些信息都是會動態(tài)變化的,那么怎么才能讓變化的屬性傳到DownloadButton中進行組件的重繪呢?
因為涉及到復雜的狀態(tài)變化,所以簡單的AnimatedWidget已經滿足不了我們的需求了,這里就需要用到flutter中的AnimatedBuilder組件了。
AnimatedBuilder是AnimatedWidget的子類,它有兩個必須的參數,分別是animation和builder。
其中animation是一個Listenable對象,它可以是Animation,ChangeNotifier或者等。
AnimatedBuilder會通過監(jiān)聽animation的變動情況,來重新構建builder中的組件。buidler方法可以從animation中獲取對應的變動屬性。
這樣我們創(chuàng)建一個Listenable的DownloadController對象,然后把DownloadButton用AnimatedBuilder封裝起來,就可以實時監(jiān)測到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接口,并且定義了兩個獲取下載狀態(tài)和下載進度的方法,同時也定義了三個點擊觸發(fā)事件:
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這兩個方法是跟下載狀態(tài)和下載進度相關的,先看下stopDownload:
void stopDownload() { if (_isDownloading) { _isDownloading = false; _downloadStatus = DownloadStatus.notDownloaded; _progress = 0.0; notifyListeners(); } }
可以看到這個方法最后需要調用notifyListeners來通知AnimatedBuilder來進行組件的重繪。
startDownload方法會復雜一點,我們需要模擬下載狀態(tài)的變化和進度的變化,如下所示:
Future<void> _doDownload() async { _isDownloading = true; _downloadStatus = DownloadStatus.fetchingDownload; notifyListeners(); // fetch耗時1秒鐘 await Future<void>.delayed(const Duration(seconds: 1)); if (!_isDownloading) { return; } // 轉換到下載的狀態(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(); } }
因為下載是一個比較長的過程,所以這里用的是異步方法,在異步方法中進行通知。
定義DownloadButton的細節(jié)
有了可以動態(tài)變化的狀態(tài)和進度之后,我們就可以在DownloadButton中構建具體的頁面展示了。
在未開始下載之前,我們希望downloadButton是一個長條形的按鈕,按鈕上的文字顯示GET,下載過程中希望是一個類似CircularProgressIndicator的動畫,可以根據下載進度來動態(tài)變化。
同時,在下載過程中,我們希望能夠隱藏之前的長條形按鈕。 下載完畢之后,再次展示長條形按鈕,這時候按鈕上的文字顯示為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, ), ), ), ), );
實現效果如下所示:
接下來再處理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, ), ], ), ),
總結
這樣,我們一個動畫的下載按鈕就制作完成了,效果如下:
本文的例子:https://github.com/ddean2009/learn-flutter.git
到此這篇關于Flutter實現簡單的下載按鈕動畫的文章就介紹到這了,更多相關Flutter下載按鈕動畫內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
android使用webwiew載入頁面使用示例(Hybrid App開發(fā))
Hybrid App 融合 Web App 的原理就是嵌入一個WebView組件,可以在這個組件中載入頁面,相當于內嵌的瀏覽器,下面是使用示例2014-03-03DCloud的native.js調用系統(tǒng)分享實例Android版代碼
本文為大家分享了DCloud的native.js如何調用系統(tǒng)分享功能Android版的實例代碼,直接拿來就用2018-09-09Android操作SQLite數據庫(增、刪、改、查、分頁等)及ListView顯示數據的方法詳解
這篇文章主要介紹了Android操作SQLite數據庫(增、刪、改、查、分頁等)及ListView顯示數據的方法,結合實例形式詳細分析了Android操作SQLite數據庫及使用ListView顯示數據的相關技巧,需要的朋友可以參考下2016-02-02