日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

在這里插入圖片描述

大家好,我是 17。

在 Flutter 中使用圖片是最基礎(chǔ)能力之一。17 做了精心準(zhǔn)備,滿滿的都是干貨!本文介紹如何在 Flutter 中使用圖片,盡量詳細,示例完整,包會!

使用網(wǎng)絡(luò)圖片

使用網(wǎng)絡(luò)圖片超級簡單,直接給出網(wǎng)絡(luò)地址就行,本例運行后,顯示的是一張貓頭鷹的圖片。

完整代碼,貼到 main.dart 就能用。后面的代碼只給出 image 相關(guān)的。

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 請求頭如何,都會被緩存起來,下次請求這個圖片會直接從內(nèi)存中讀取。

一般我們需要指定圖片的寬度和高度,讓它以指定的尺寸顯示,避免圖片過大撐破布局。

Image.network(imageSrc,width: 100,height: 100,)

如果提供了 cacheWidth 或 cacheHeight,則指示引擎應(yīng)以指定大小解碼圖像。無論這些參數(shù)如何,圖像都將根據(jù)約束進行渲染。cacheWidth 和 cacheHeight 主要是為了減少 ImageCache 的內(nèi)存使用。

cacheWidth 和 cacheHeight 是為了優(yōu)化內(nèi)存用的,如果你能確定網(wǎng)絡(luò)圖片的尺寸都是合適的尺寸,就不用設(shè)置這兩個參數(shù)。如果不能保證來源圖片的尺寸,比如可能有大尺寸的圖片,最好設(shè)置這兩個參數(shù)。這兩個參數(shù)只能優(yōu)化內(nèi)存占用,對下載和解碼沒有幫助。如果要優(yōu)化下載,需要把圖片緩存在磁盤上,下次直接從磁盤讀取,就像 web 緩存那樣。

把網(wǎng)絡(luò)圖片緩存到磁盤

我們可以用 cached_network_image 這個插件實現(xiàn)把網(wǎng)絡(luò)圖片緩存到磁盤這個功能。

安裝插件

Bash
flutter pub add cached_network_image

必須的參數(shù)只有一個 imageUrl。

 MaterialApp(
    home: Scaffold(
      body: Center(child: CachedNetworkImage(
        imageUrl: imageSrc,
    )),
 ));

cached_network_image 自帶 fadeIn 的效果,在圖片加載過程中顯示 placeholder,出現(xiàn)錯誤,顯示 errorWidget。

CachedNetworkImage(
        imageUrl: imageSrc,
        placeholder: (context, url) => CircularProgressIndicator(),
        errorWidget: (context, url, error) => Icon(Icons.error),
 )

有時我們需要把圖片應(yīng)用到其它 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),
),

還有很多參數(shù),可以 在文檔中查看

使用 assets 圖片

assets 也可以叫做資源。資源是與您的應(yīng)用程序一起捆綁和部署的文件,可在運行時訪問。常見的資源類型包括靜態(tài)數(shù)據(jù)(例如 JSON 文件)、配置文件、圖標(biāo)和圖像(JPEG、WebP、GIF、動畫 WebP/GIF、PNG、BMP 和 WBMP)。

每個資源都由資源文件所在的顯式路徑(相對于 pubspec.yaml 文件)標(biāo)識。聲明資源的順序無關(guān)緊要。包含資源的目錄名稱無關(guān)緊要。

在構(gòu)建過程中,F(xiàn)lutter 將資源放入一個名為資源包的特殊存檔中,應(yīng)用程序會在運行時從中讀取。

Flutter 使用位于項目根目錄的 pubspec.yaml 文件來識別應(yīng)用程序所需的資源。

資源文件夾的名稱是隨意的,我們可以把資源文件夾放在和 lib 平級的根目錄下面,為圖片建立文件夾 images,把上面示例中的貓頭鷹圖片放入其中。

image.png

修改 pubspec.yaml 的配置。

flutter:
  assets:
    - images/owl.png

注意空格

在代碼中可以通過 images/owl.png 使用圖片。

Image.asset(
    'images/owl.png',
    width: 200,
    height: 200,
);

運行,成功顯示了貓頭鷹的圖片。當(dāng)你發(fā)布應(yīng)用程序的時候,pubspec.yaml 中配置的圖片會和代碼一起打包發(fā)布。

如果有很多圖片,這樣一張一張注冊很是麻煩,我們可以直接指定文件夾。比如我們可以一次性注冊 images 文件夾下面的所有圖片。

flutter:
  assets:
    - images/

適配淺色與深色模式

正常情況下,我們用的是淺色模式,在弱光環(huán)境下,打開深色模式可獲得出色的視覺體驗。

構(gòu)建過程支持資源變體的概念:可以在不同上下文中顯示資源的不同版本。當(dāng)在 pubspec.yaml 中指定資源路徑時,構(gòu)建過程會在相鄰子目錄中查找任何具有相同名稱的文件。然后,此類文件與指定資源一起包含在資源包中。

在 images 下面增加 dark 文件夾,增加在深色模式下使用的與淺色文件同名的圖片。17 的電腦屏幕截圖:

image.png

適配淺色與深色模式的工作就完成了!

images/owl.png 和 images/dark/owl.png 都包含在您的資源包中。前者被視為主要資源,而后者被視為變體。在淺色模式下,F(xiàn)lutter 為我們顯示顯示 images/owl.png ,在深色模式下顯示 images/dark/owl.png。

在不同的設(shè)備使用不同分辨率的圖片

Flutter 可以根據(jù)當(dāng)前設(shè)備像素比加載分辨率合適的圖像。

在 image 文件夾下增加 2.0x,3.0x文件夾,放入同名的高分辨率的圖片。17 的電腦屏幕的截圖:

image.png

1.5x 文件夾也是合法的。

適合不同設(shè)備分辨率的工作就完成了!

images/2.0x/owl.png 和 images/3.0x/owl.png 都包含在您的資源包中,都被視為變體。
flutter 會自動為我們在 dpr 為 2 的設(shè)備上使用 images/2.0x/owl.png 在 dpr 為 3 的設(shè)備上使用 images/3.0x/owl.png,在 dpr 為 1 設(shè)備上使用 images/owl.png。 images/owl.png 相當(dāng)于是 images/1.0x/owl.png。

dpr 為設(shè)備分辨率(device pixel ratio)英文單詞的首字母

還是一樣的代碼,現(xiàn)在可以適配不同 dpr 的設(shè)備!

Image.asset(
    'images/owl.png',
    width: 200,
    height: 200,
);

關(guān)于設(shè)備 dpr 不完全匹配的處理

Flutter 以 dpr 2.0 為界,采用不同的處理方案,目的是為了得到更好的體驗。

  1. 2.0 以下的設(shè)備匹配分辨率更高的圖片
  2. 2.0 以上的設(shè)備匹配分辨率最接近的圖片

比如有一個 dpr 為 1.25 的設(shè)備,會采用 2.0 的圖片,而不是 1.0的圖片。再比如有一個 dpr 為 2.25 的設(shè)備,會采用 2.0 的圖片,而不會采用 3.0 的圖片。

忽略 dpr 信息

如果要忽略 dpr 信息直接讀取主資源(就是不帶 x.0路徑的那個),用 ExactAssetImage。可以指定 scale,默認為 1.0。

如果 scale 為 2.0,則意味著每個邏輯像素對應(yīng)四個圖像像素。看下實際的效果就明白了。

image.png

Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Image(image: ExactAssetImage('images/owl.png', scale: 5)),
          Image(image: ExactAssetImage('images/owl.png', scale: 10)),
        ],
      )

scale 越大,圖片顯示的越小,因為 scale 越大,每個邏輯像素對應(yīng)的圖像像素就越多。

邏輯像素,也叫 設(shè)備獨立像素(device independent pixels),簡稱 dip ,與具體設(shè)備無關(guān)。

使用相冊圖片

先安裝插件

Bash
flutter pub add image_picker

使用相冊圖片需要兩步

  1. 使用 image_picker 插件從相冊中讀取圖片
  2. 使用 Image.file 展示圖片
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 最好設(shè)置一下,從源頭上控制一下圖片的大小,提高效率。如果這里沒控制大小,就必須讓 Image.file 加上 cacheWidthcacheHeight參數(shù),因為用 Image.file 顯示的圖片也會緩存起來,需要控制緩存的圖片大小,減少內(nèi)存消耗。

requestFullMetadata: false 是為了避免 IOS 閃退。requestFullMetadata: false 表示要請求完整的 metadata,需要在 info.plist( ios / Runner 下面 )中申請權(quán)限。

打開 info.plist 的 dict 中加入如下內(nèi)容就可以了。

<key>NSPhotoLibraryUsageDescription</key>
<string>APP需要您的同意,才能使用相冊,以便于上傳,發(fā)布照片</string>

Android 不需要申請權(quán)限。

使用相機拍攝的圖片

和使用相冊圖片步驟一樣,有兩點不同

  1. source: ImageSource.gallery 修改為 source: ImageSource.camera
  2. 申請相機的權(quán)限

android 不需要申請權(quán)限,直接可以使用相機,ios 需要 打開 info.plist 在 dict 中加入如下內(nèi)容

<key>NSCameraUsageDescription</key>
<string>APP需要您的同意,才能使用攝像頭,以便于相機拍攝,上傳、發(fā)布照片</string>

使用內(nèi)存圖片

Image.memory 的必選參數(shù) bytes 是 Uint8List 類型,base64Decode 的返回值正好是 Uint8List,我們用 Image.memory 展示一下 base64 格式的圖片。

我們得到的 base64格式的圖片可能是這樣的

imageString = 'image/jpeg;base64,/9j/4AA...'

把 image/jpeg;base64, 刪除,只保留后面的數(shù)據(jù),這樣才能正常顯示。

引用 dart:convert 把 imageString 用 base64Decode 轉(zhuǎn)成 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 參數(shù)類型是 ImageProvider,ImageProvider 的子類都可以用作參數(shù)。除了 AssetImage,還可以用 FileImage,MemoryImage,NetworkImage。

圖片預(yù)加載

在網(wǎng)頁中的輪播圖中我們一般都會做圖片的預(yù)加載,用 js 預(yù)加載圖片,避免圖片在輪播時無法顯示。Flutter 中也有輪播圖,我們也可以做類似的事情。

和 js 預(yù)加載一樣,F(xiàn)lutter 預(yù)加載圖片也是很簡單的。

preload(BuildContext context) {
    var configuration = createLocalImageConfiguration(context);
    for (var src in ['圖片地址1', '圖片地址2', '圖片地址13']) {
      NetworkImage(src).resolve(configuration);
    }
  }

圖片什么時候加載完成不用管。Flutter 會用 NetWorkImage 做 key,緩存圖片,下次用 NetworkImage 加載同樣的圖片,無論是否加載完成,都不會再次加載。

resolve 的作用就是把加載的工作提前執(zhí)行。

判斷兩個 NetWorkImage 相同,需要 url,scale 都相同,所以如果如果 scale 不同,會觸發(fā)重新加載。

  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType) {
      return false;
    }
    return other is NetworkImage
        && other.url == url
        && other.scale == scale;
  }

centerSlice

centerSlice 用來切 9圖的。比如下面這張圖片,我們用 9 圖的方式來切圖。

btn.png

這張圖是300 x 300 的圖,把它顯示成 400 x 400。我們把它分成 9 個區(qū)域。

  • 區(qū)域 5 會被垂直水平拉伸
  • 區(qū)域 4,6 被垂直拉伸
  • 區(qū)域 2,8 被水平拉伸
  • 1,3,7,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,)
  ],
);

根據(jù) 9 圖的特性,我們可以把按鈕的背景圖做 9 圖,用來容納可變化的字數(shù),也可以把聊天用的氣泡做成 9 圖。

centerSlice 只能放大,不能縮小。

原圖是 300 x 300, centerSlice 處理后的圖只能是寬比 300 大,高也比300 大,否則報錯。

全局緩存 ImageCache 的設(shè)置

ImageCache 有兩個屬性

  1. maximumSize 可以獲取和設(shè)置可以緩存的圖片的最大數(shù)量,默認 1000 張。
  2. maximumSizeBytes 可以獲取和設(shè)置可以緩存的圖片的最大容量,默認 100M。

既然這兩個屬性可以讓我們設(shè)置,就說明在有的時候,這兩個屬性的默認值是不合適的。我們可以通過 PaintingBinding.instance.imageCache 拿到全局 ImageCache 的實例,通過PaintingBinding.instance.imageCache.currentSize 監(jiān)控一下當(dāng)前已經(jīng)緩存的圖片數(shù)量,如果經(jīng)常達到最大值,說明默認值太小,可以設(shè)置的更大些。

maximumSize 設(shè)置的數(shù)量調(diào)高需要考慮到硬件的承受能力。如果硬件條件差,反而會適得其反。這個時候非但不能加大設(shè)置,還需要減小設(shè)置。

圖片類之間的關(guān)系

如果只是想會用 Flutter Image,上面的內(nèi)容就夠用了,可以跳過后面的內(nèi)容。

上面講了很多,都是零散的,涉及到的類很多,難免有混亂之感,所以需要對他們之間的關(guān)系梳理一下。

Image widget 是直接給我們用的。拋開那些命名構(gòu)造函數(shù),如果我們直接用 Image,只有一個必須的參數(shù) image,image 類型是 ImageProvider。

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); // 需子類實現(xiàn)
}

obtainKey(ImageConfiguration) 方法

該接口主要是為了配合實現(xiàn)圖片緩存,不同的 key 代表不同的圖片數(shù)據(jù)緩存。ImageProvider 從數(shù)據(jù)源加載完數(shù)據(jù)后,會在全局的 ImageCache 中緩存圖片。

resolve(ImageConfiguration) 方法

ImageStream resolve(ImageConfiguration configuration) {
  ... //省略
  final ImageStream stream = ImageStream();
  T obtainedKey; //
  //定義錯誤處理函數(shù)
  Future<void> handleError(dynamic exception, StackTrace stack) async {
    ... //省略
    stream.setCompleter(imageCompleter);
    imageCompleter.setError(...);
  }

  // 創(chuàng)建一個新Zone,為了當(dāng)發(fā)生錯誤時不會干擾 MainZone
  final Zone dangerZone = Zone.current.fork(...);
  
  dangerZone.runGuarded(() {
    Future<T> key;
    // 先驗證是否已經(jīng)有緩存
    try {
      // 生成緩存key,后面會根據(jù)此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 對外暴露的主要方法,我們可以調(diào)用這個方法來加載圖片,Image Widget 也是調(diào)用這個方法加載圖片。

要從 ImageProvider 獲取 ImageStream,調(diào)用 resolve 并向其傳遞一個 ImageConfiguration 對象。
ImageProvider 通過 obtainKey 獲得 Key 并使用全局的 imageCache 緩存圖片。

類型參數(shù) T 是用于表示已解析配置的對象的類型。這也是圖像緩存中用于鍵的類型。它應(yīng)該是不可變的并實現(xiàn) == 運算符和 hashCode getter。

AssetBundleImageProvider,F(xiàn)ileImage,MemoryImage,NetworkImage

這四個都是 ImageProvider 的子類,AssetBundleImageProvider 又有兩個子類,AssetImage 和 ExactAssetImage,這兩個和 FileImage,MemoryImage,NetworkImage 都可以直接給 image 參數(shù)賦值。

比如我們要讀取 owl.png

 Image(image: AssetImage("image/owl.png"),);
 Image.asset("image/owl.png");

這兩種都能顯示 owl.png。那么有什么區(qū)別呢?我們看下 Image.asset 構(gòu)造函數(shù)的源碼

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 構(gòu)造函數(shù)為我們創(chuàng)建了 ImageProvider。

  1. 如果 scale 不為空,創(chuàng)建 ExactAssetImage,否則創(chuàng)建 AssetImage
  2. 用 ResizeImage.resizeIfNeeded 包起來。

ResizeImage.resizeIfNeeded 執(zhí)行下面的邏輯:如果 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 在放進緩存之前,會根據(jù) cacheWidth,cacheHeight 對圖片做優(yōu)化,這對于減少內(nèi)存開銷有幫助。

從以上可以看出,沒有特殊需要,我們都使用 Image.asset、Image.network、Image.file、age.memory,這四個命名構(gòu)造函數(shù)。

ImageProvider 的子類還有一個 ScrollAwareImageProvider, RawImage 會調(diào)用他避免在快速滾動時加載圖像。我們一般不需要直接使用他。

本文到這里就結(jié)束了,謝謝觀看!

分享到:
標(biāo)簽:Flutter
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定