Flutter實(shí)現(xiàn)簡(jiǎn)單的下載按鈕動(dòng)畫(huà)
簡(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)文章
android使用webwiew載入頁(yè)面使用示例(Hybrid App開(kāi)發(fā))
Hybrid App 融合 Web App 的原理就是嵌入一個(gè)WebView組件,可以在這個(gè)組件中載入頁(yè)面,相當(dāng)于內(nèi)嵌的瀏覽器,下面是使用示例2014-03-03Android仿簡(jiǎn)書(shū)長(zhǎng)按文章生成圖片效果
使用簡(jiǎn)書(shū)APP的同學(xué)都知道,簡(jiǎn)書(shū)有這樣一個(gè)功能;文章頁(yè)長(zhǎng)按內(nèi)容時(shí)底部會(huì)出現(xiàn)一個(gè) 生成圖片分享 的按鈕,點(diǎn)擊之后就可以將當(dāng)前的文章生成一張長(zhǎng)圖片;這張圖片可以保存到本地或分享給好友,同時(shí)還可為圖片設(shè)置成為白和黑兩種風(fēng)格,很有藝術(shù)范2017-03-03用MOB實(shí)例開(kāi)發(fā)實(shí)現(xiàn)短信驗(yàn)證功能
本篇文章通學(xué)習(xí)通過(guò)MOB平臺(tái)開(kāi)發(fā)APP實(shí)現(xiàn)簡(jiǎn)單的短信驗(yàn)證功能,對(duì)此有需求的朋友跟著好好學(xué)習(xí)下吧。2018-01-01Android基于Aidl的跨進(jìn)程間雙向通信管理中心
這篇文章主要為大家詳細(xì)介紹了Android基于Aidl的跨進(jìn)程間雙向通信管理中心,類(lèi)似于聊天室,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11DCloud的native.js調(diào)用系統(tǒng)分享實(shí)例Android版代碼
本文為大家分享了DCloud的native.js如何調(diào)用系統(tǒng)分享功能Android版的實(shí)例代碼,直接拿來(lái)就用2018-09-09Android SQLite數(shù)據(jù)庫(kù)增刪改查操作的案例分析
本篇文章介紹了,在Android中SQLite數(shù)據(jù)庫(kù)增刪改查操作的案例分析,需要的朋友參考下2013-04-04Android操作SQLite數(shù)據(jù)庫(kù)(增、刪、改、查、分頁(yè)等)及ListView顯示數(shù)據(jù)的方法詳解
這篇文章主要介紹了Android操作SQLite數(shù)據(jù)庫(kù)(增、刪、改、查、分頁(yè)等)及ListView顯示數(shù)據(jù)的方法,結(jié)合實(shí)例形式詳細(xì)分析了Android操作SQLite數(shù)據(jù)庫(kù)及使用ListView顯示數(shù)據(jù)的相關(guān)技巧,需要的朋友可以參考下2016-02-02