<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在 Flutter 中使用圖片是最基礎能力之一。作為春節開工後的第一篇文章,17 做了精心準備,滿滿的都是乾貨!本文
介紹如何在 Flutter 中使用圖片,儘量詳細,範例完整,包會!
使用網路圖片超級簡單,直接給出網路地址就行,本例執行後,顯示的是一張貓頭鷹的圖片。
完整程式碼,貼到 main.dart 就能用。後面的程式碼只給出 image 相關的。
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); final imageSrc = 'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/04ec6088c3c544a2b9459582e335483c~tplv-k3u1fbpfcp-watermark.image?'; @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center(child: Image.network(imageSrc)), )); } }
圖片載入成功後,不管 http 請求頭如何,都會被快取起來,下次請求這個圖片會直接從記憶體中讀取。
一般我們需要指定圖片的寬度和高度,讓它以指定的尺寸顯示,避免圖片過大撐破布局。
Image.network(imageSrc,width: 100,height: 100,)
如果提供了 cacheWidth 或 cacheHeight,則指示引擎應以指定大小解碼影象。無論這些引數如何,影象都將根據約束進行渲染。cacheWidth 和 cacheHeight 主要是為了減少 ImageCache 的記憶體使用。
cacheWidth 和 cacheHeight 是為了優化記憶體用的,如果你能確定網路圖片的尺寸都是合適的尺寸,就不用設定這兩個引數。如果不能保證來源圖片的尺寸,比如可能有大尺寸的圖片,最好設定這兩個引數。這兩個引數只能優化記憶體佔用,對下載和解碼沒有幫助。如果要優化下載,需要把圖片快取在磁碟上,下次直接從磁碟讀取,就像 web 快取那樣。
我們可以用 cached_network_image 這個外掛實現把網路圖片快取到磁碟這個功能。
安裝外掛
flutter pub add cached_network_image
必須的引數只有一個 imageUrl。
MaterialApp( home: Scaffold( body: Center(child: CachedNetworkImage( imageUrl: imageSrc, )), ));
cached_network_image 自帶 fadeIn 的效果,在圖片載入過程中顯示 placeholder,出現錯誤,顯示 errorWidget。
CachedNetworkImage( imageUrl: imageSrc, placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Icon(Icons.error), )
有時我們需要把圖片應用到其它 widget ,比如用在 BoxDecoration 中,這時需要提供 imageProvider。
CachedNetworkImage( imageUrl: imageSrc, imageBuilder: (context, imageProvider) => Container( decoration: BoxDecoration( image: DecorationImage( image: imageProvider, fit: BoxFit.cover, colorFilter: ColorFilter.mode(Colors.red, BlendMode.colorBurn)), ), ), placeholder: (context, url) => CircularProgressIndicator(), errorWidget: (context, url, error) => Icon(Icons.error), ),
還有很多引數,可以 在檔案中檢視
assets 也可以叫做資源。資源是與您的應用程式一起捆綁和部署的檔案,可在執行時存取。常見的資源型別包括靜態資料(例如 JSON 檔案)、組態檔、圖示和影象(JPEG、WebP、GIF、動畫 WebP/GIF、PNG、BMP 和 WBMP)。
每個資源都由資原始檔所在的顯式路徑(相對於 pubspec.yaml 檔案)標識。宣告資源的順序無關緊要。包含資源的目錄名稱無關緊要。
在構建過程中,Flutter 將資源放入一個名為資源包的特殊存檔中,應用程式會在執行時從中讀取。
Flutter 使用位於專案根目錄的 pubspec.yaml 檔案來識別應用程式所需的資源。
資原始檔夾的名稱是隨意的,我們可以把資原始檔夾放在和 lib 平級的根目錄下面,為圖片建立資料夾 images,把上面範例中的貓頭鷹圖片放入其中。
修改 pubspec.yaml 的設定。
flutter: assets: - images/owl.png
注意空格
在程式碼中可以通過 images/owl.png
使用圖片。
Image.asset( 'images/owl.png', width: 200, height: 200, );
執行,成功顯示了貓頭鷹的圖片。當你釋出應用程式的時候,pubspec.yaml 中設定的圖片會和程式碼一起打包釋出。
如果有很多圖片,這樣一張一張註冊很是麻煩,我們可以直接指定資料夾。比如我們可以一次性註冊 images 資料夾下面的所有圖片。
flutter: assets: - images/
正常情況下,我們用的是淺色模式,在弱光環境下,開啟深色模式可獲得出色的視覺體驗。
構建過程支援資源變體的概念:可以在不同上下文中顯示資源的不同版本。當在 pubspec.yaml 中指定資源路徑時,構建過程會在相鄰子目錄中查詢任何具有相同名稱的檔案。然後,此類檔案與指定資源一起包含在資源包中。
在 images 下面增加 dark 資料夾,增加在深色模式下使用的與淺色檔案同名的圖片。17 的電腦螢幕截圖:
適配淺色與深色模式的工作就完成了!
images/owl.png 和 images/dark/owl.png 都包含在您的資源包中。前者被視為主要資源,而後者被視為變體。在淺色模式下,Flutter 為我們顯示顯示 images/owl.png ,在深色模式下顯示 images/dark/owl.png。
Flutter 可以根據當前裝置畫素比載入解析度合適的影象。
在 image 資料夾下增加 2.0x,3.0x資料夾,放入同名的高解析度的圖片。17 的電腦螢幕的截圖:
1.5x 資料夾也是合法的。
適合不同裝置解析度的工作就完成了!
images/2.0x/owl.png 和 images/3.0x/owl.png 都包含在您的資源包中,都被視為變體。 flutter 會自動為我們在 dpr 為 2 的裝置上使用 images/2.0x/owl.png 在 dpr 為 3 的裝置上使用 images/3.0x/owl.png,在 dpr 為 1 裝置上使用 images/owl.png。 images/owl.png 相當於是 images/1.0x/owl.png。
dpr 為裝置解析度(device pixel ratio)英文單詞的首字母
還是一樣的程式碼,現在可以適配不同 dpr 的裝置!
Image.asset( 'images/owl.png', width: 200, height: 200, );
Flutter 以 dpr 2.0 為界,採用不同的處理方案,目的是為了得到更好的體驗。
比如有一個 dpr 為 1.25 的裝置,會採用 2.0 的圖片,而不是 1.0的圖片。再比如有一個 dpr 為 2.25 的裝置,會採用 2.0 的圖片,而不會採用 3.0 的圖片。
如果要忽略 dpr 資訊直接讀取主資源(就是不帶 x.0路徑的那個),用 ExactAssetImage。可以指定 scale,預設為 1.0。
如果 scale 為 2.0,則意味著每個邏輯畫素對應四個影象畫素。看下實際的效果就明白了。
Column( mainAxisSize: MainAxisSize.min, children: [ Image(image: ExactAssetImage('images/owl.png', scale: 5)), Image(image: ExactAssetImage('images/owl.png', scale: 10)), ], )
scale 越大,圖片顯示的越小,因為 scale 越大,每個邏輯畫素對應的影象畫素就越多。
邏輯畫素,也叫 裝置獨立畫素(device independent pixels),簡稱 dip ,與具體裝置無關。
先安裝外掛
flutter pub add image_picker
使用相簿圖片需要兩步
class MyWidget extends StatefulWidget { const MyWidget({super.key}); @override State<MyWidget> createState() => _MyWidgetState(); } class _MyWidgetState extends State<MyWidget> { final _picker = ImagePicker(); File? _file; @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: () async { var xfile = await _picker.pickImage( source: ImageSource.gallery, maxWidth: 200, maxHeight: 300, requestFullMetadata: false); if (xfile != null) { setState(() { _file = File(xfile.path); }); } }, child: const Text('從相簿中選擇圖片')), if (_file != null) Image.file(_file!) ], ); } }
maxWidth
和 maxHeight
最好設定一下,從源頭上控制一下圖片的大小,提高效率。如果這裡沒控制大小,就必須讓 Image.file 加上 cacheWidth
,cacheHeight
引數,因為用 Image.file 顯示的圖片也會快取起來,需要控制快取的圖片大小,減少記憶體消耗。
requestFullMetadata: false
是為了避免 ios 閃退。requestFullMetadata: false
表示要請求完整的 metadata,需要在 info.plist( ios / Runner 下面 )中申請許可權。
開啟 info.plist 的 dict 中加入如下內容就可以了。
<key>NSPhotoLibraryUsageDescription</key> <string>APP需要您的同意,才能使用相簿,以便於上傳,釋出照片</string>
android 不需要申請許可權。
和使用相簿圖片步驟一樣,有兩點不同
source: ImageSource.camera
android 不需要申請許可權,直接可以使用相機,ios 需要 開啟 info.plist 在 dict 中加入如下內容
<key>NSCameraUsageDescription</key> <string>APP需要您的同意,才能使用攝像頭,以便於相機拍攝,上傳、釋出照片</string>
Image.memory 的必選引數 bytes 是 Uint8List 型別,base64Decode 的返回值正好是 Uint8List,我們用 Image.memory 展示一下 base64 格式的圖片。
我們得到的 base64格式的圖片可能是這樣的
imageString = 'image/jpeg;base64,/9j/4AA...'
把 image/jpeg;base64,
刪除,只保留後面的資料,這樣才能正常顯示。
參照 dart:convert
把 imageString 用 base64Decode
轉成 Uint8List 型別,Image.memory 就能顯示了。
import 'dart:convert'; Image.memory(base64Decode(imageString));
DecoratedBox 是專門用來做裝飾的 widget
DecoratedBox( decoration: BoxDecoration( image: DecorationImage(image: AssetImage('images/owl.png')), ), child: SizedBox( width: 100, height: 100, ), )
更多時候,我們可以用 Container。
Container( width: 100, height: 100, decoration: BoxDecoration( image: DecorationImage(image: AssetImage('images/owl.png')) ), )
DecorationImage 的 image 引數型別是 ImageProvider,ImageProvider 的子類都可以用作引數。除了 AssetImage,還可以用 FileImage,MemoryImage,NetworkImage。
在網頁中的輪播圖中我們一般都會做圖片的預載入,用 js 預載入圖片,避免圖片在輪播時無法顯示。Flutter 中也有輪播圖,我們也可以做類似的事情。
和 js 預載入一樣,Flutter 預載入圖片也是很簡單的。
preload(BuildContext context) { var configuration = createLocalImageConfiguration(context); for (var src in ['圖片地址1', '圖片地址2', '圖片地址13']) { NetworkImage(src).resolve(configuration); } }
圖片什麼時候載入完成不用管。Flutter 會用 NetWorkImage 做 key,快取圖片,下次用 NetworkImage 載入同樣的圖片,無論是否載入完成,都不會再次載入。
resolve
的作用就是把載入的工作提前執行。
判斷兩個 NetWorkImage 相同,需要 url,scale 都相同,所以如果如果 scale 不同,會觸發重新載入。
bool operator ==(Object other) { if (other.runtimeType != runtimeType) { return false; } return other is NetworkImage && other.url == url && other.scale == scale; }
centerSlice 用來切 9圖的。比如下面這張圖片,我們用 9 圖的方式來切圖。
這張圖是300 x 300 的圖,把它顯示成 400 x 400。我們把它分成 9 個區域。
Column( mainAxisSize: MainAxisSize.min, children: [ Image.asset("images/btn.png",width: 300,height: 300,fit: BoxFit.fill,), Image.asset("images/btn.png", centerSlice: Rect.fromLTRB(100, 100, 200,200),width: 400,height:400,scale: 1,) ], );
根據 9 圖的特性,我們可以把按鈕的背景圖做 9 圖,用來容納可變化的字數,也可以把聊天用的氣泡做成 9 圖。
原圖是 300 x 300, centerSlice 處理後的圖只能是寬比 300 大,高也比300 大,否則報錯。
ImageCache 有兩個屬性
既然這兩個屬性可以讓我們設定,就說明在有的時候,這兩個屬性的預設值是不合適的。我們可以通過 PaintingBinding.instance.imageCache
拿到全域性 ImageCache 的範例,通過PaintingBinding.instance.imageCache.currentSize
監控一下當前已經快取的圖片數量,如果經常達到最大值,說明預設值太小,可以設定的更大些。
maximumSize 設定的數量調高需要考慮到硬體的承受能力。如果硬體條件差,反而會適得其反。這個時候非但不能加大設定,還需要減小設定。
如果只是想會用 Flutter Image,上面的內容就夠用了,可以跳過後面的內容。
上面講了很多,都是零散的,涉及到的類很多,難免有混亂之感,所以需要對他們之間的關係梳理一下。
Image widget 是直接給我們用的。拋開那些命名建構函式,如果我們直接用 Image,只有一個必須的引數 image,image 型別是 ImageProvider。
abstract class ImageProvider<T> { ImageStream resolve(ImageConfiguration configuration) { // 省略 } Future<bool> evict({ ImageCache cache, ImageConfiguration configuration = ImageConfiguration.empty }) async { // 省略 } Future<T> obtainKey(ImageConfiguration configuration); @protected ImageStreamCompleter load(T key); // 需子類實現 }
該介面主要是為了配合實現圖片快取,不同的 key 代表不同的圖片資料快取。ImageProvider 從資料來源載入完資料後,會在全域性的 ImageCache 中快取圖片。
ImageStream resolve(ImageConfiguration configuration) { ... //省略 final ImageStream stream = ImageStream(); T obtainedKey; // //定義錯誤處理常式 Future<void> handleError(dynamic exception, StackTrace stack) async { ... //省略 stream.setCompleter(imageCompleter); imageCompleter.setError(...); } // 建立一個新Zone,為了當發生錯誤時不會干擾 MainZone final Zone dangerZone = Zone.current.fork(...); dangerZone.runGuarded(() { Future<T> key; // 先驗證是否已經有快取 try { // 生成快取key,後面會根據此key來檢測是否有快取 key = obtainKey(configuration); } catch (error, stackTrace) { handleError(error, stackTrace); return; } key.then<void>((T key) { obtainedKey = key; // 快取的處理邏輯 final ImageStreamCompleter completer = PaintingBinding.instance .imageCache.putIfAbsent(key, () => load(key), onError: handleError); if (completer != null) { stream.setCompleter(completer); } }).catchError(handleError); }); return stream; }
resolve 是 ImageProvider 對外暴露的主要方法,我們可以呼叫這個方法來載入圖片,Image Widget 也是呼叫這個方法載入圖片。
要從 ImageProvider 獲取 ImageStream,呼叫 resolve 並向其傳遞一個 ImageConfiguration 物件。 ImageProvider 通過 obtainKey 獲得 Key 並使用全域性的 imageCache 快取圖片。
型別引數 T 是用於表示已解析設定的物件的型別。這也是影象快取中用於鍵的型別。它應該是不可變的並實現 == 運運算元和 hashCode getter。
AssetBundleImageProvider,FileImage,MemoryImage,NetworkImage
這四個都是 ImageProvider 的子類,AssetBundleImageProvider 又有兩個子類,AssetImage 和 ExactAssetImage,這兩個和 FileImage,MemoryImage,NetworkImage 都可以直接給 image 引數賦值。
比如我們要讀取 owl.png
Image(image: AssetImage("image/owl.png"),); Image.asset("image/owl.png");
這兩種都能顯示 owl.png。那麼有什麼區別呢?我們看下 Image.asset 建構函式的原始碼
Image.asset( String name, { 省略... }) : image = ResizeImage.resizeIfNeeded( cacheWidth, cacheHeight, scale != null ? ExactAssetImage(name, bundle: bundle, scale: scale, package: package) : AssetImage(name, bundle: bundle, package: package), ), 省略... ;
Image.asset 建構函式為我們建立了 ImageProvider。
ResizeImage.resizeIfNeeded 執行下面的邏輯:如果 cacheWidth,cacheHeight 同時為空,直接返回原 ImageProvider,否則返回 ResizeImage。
static ImageProvider<Object> resizeIfNeeded(int? cacheWidth, int? cacheHeight, ImageProvider<Object> provider) { if (cacheWidth != null || cacheHeight != null) { return ResizeImage(provider, width: cacheWidth, height: cacheHeight); } return provider; }
ResizeImage 在放進快取之前,會根據 cacheWidth,cacheHeight 對圖片做優化,這對於減少記憶體開銷有幫助。
從以上可以看出,沒有特殊需要,我們都使用 Image.asset、Image.network、Image.file、age.memory
,這四個命名建構函式。
ImageProvider 的子類還有一個 ScrollAwareImageProvider, RawImage 會呼叫他避免在快速捲動時載入影象。我們一般不需要直接使用他。
以上就是Flutter 圖片開發核心技能快速掌握教學的詳細內容,更多關於Flutter 圖片開發的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45