幹一個Flutter元件:動動小手磨出一個資源多選外掛(1)——基礎構建篇

AlexV525發表於2020-04-09

相關文章

  • 幹一個Flutter元件:動動小手磨出一個資源多選外掛(1)——基礎構建篇
  • 幹一個Flutter元件:動動小手磨出一個資源多選外掛(2)——介面開發篇(準備中)

背景

  Flutter曾經有非常好用的多選元件,例如Sh1d0wmulti_image_picker,但他們都有或多或少的問題,例如不支援GIF選擇,不支援視訊或音訊選擇,定製程度不夠高、依賴原生元件、不是純Dart元件等。

  隨著Flutter的不斷髮展,越來越多的packages湧現,專案迭代使得multi_image_picker已逐漸不能滿足需求,且其依賴的iOS原生依賴作者在開源方向上的態度也使得這個庫不再穩定,我個人便萌生了自己定製外掛的想法。於是利用清明前一週開始至今的閒時,結合自己的專案OpenJMU定製了一個純Dart的仿微信的資源選擇元件,本次主要使用了三個重要依賴:photo_manager出自財經龍大佬之手,提供完整的API獲取資源資訊,為定製資源選擇元件提供了啟動基礎?;extended_image出自法佬之手,作為強力的圖片展示元件,體驗+++++;以及大家熟知的provider用於維護選擇器及各種部件的狀態。

簡介

  wechat_assets_picker是一個對標微信的多選資源選擇器,99%接近於原生微信的操作,純Dart編寫,支援選擇的同時也支援預覽資源。截止發文已釋出1.3.0版本,支援如下功能:

  • 圖片資源支援
  • 視訊資源支援
  • 國際化支援
  • 自定義文字支援

  效果圖:

幹一個Flutter元件:動動小手磨出一個資源多選外掛(1)——基礎構建篇
幹一個Flutter元件:動動小手磨出一個資源多選外掛(1)——基礎構建篇
幹一個Flutter元件:動動小手磨出一個資源多選外掛(1)——基礎構建篇
幹一個Flutter元件:動動小手磨出一個資源多選外掛(1)——基礎構建篇
幹一個Flutter元件:動動小手磨出一個資源多選外掛(1)——基礎構建篇
幹一個Flutter元件:動動小手磨出一個資源多選外掛(1)——基礎構建篇

實現過程

  下面是具體的實現過程,將基於1.3.0版本進行說明。對於涉及到各依賴的使用方法,請移步對應倉庫進行檢視。

呼叫方法

  呼叫一個元件的方法是一切的開始,既然這個元件是一個純Dart元件,那麼也應該基於Flutter的上下文(context)進行呼叫,用於路由跳轉。所以我們很快的寫出一個元件的靜態呼叫方法:

class AssetPicker extends StatlessWidget {
  /// 跳轉至選擇器的靜態方法
  static Future<void> pickAssets(BuildContext context) async {}
}
複製程式碼

  作為一個多選元件,當然需要知道我能選幾個資源,所以加入int maxAssets指定最大的資源可選數量,預設為9

  使用者可以自定義網格數量,所以加入int gridCount指定網格每行格子數,預設為4

  使用者可以指定縮圖的清晰度,所以加入int pageThumbSize指定選擇器中縮圖載入的畫素,預設為200

  再加億點......

  最後我們的靜態呼叫方法如下:

static Future<List<AssetEntity>> pickAssets( // 通過路由來傳遞已選中的資源
  BuildContext context, {
  int maxAssets = 9,
  int pathThumbSize = 200,
  int gridCount = 4,
  RequestType requestType = RequestType.image, // 請求載入的型別
  List<AssetEntity> selectedAssets, // 已選的資源,用來處理重複選中的問題
  Color themeColor = C.themeColor, // 主題色,預設採用了微信的#00bc56
  TextDelegate textDelegate, // 文字代理構建,用於構建每個文字點
}) async {}
複製程式碼

  一個完整的靜態方法,通過AssetPicker.pickAssets就可以呼叫了。

元件的狀態維護

  作為一個一把梭選手,在這裡選用了ChangeNotifier作為選擇器的model,進行對應狀態的控制,因為涉及到大量資源的展示,如果不進行區域性控制,將導致效能的大幅度下降。接下來開始設計AssetPickerProvider

  選擇器需要保持什麼狀態?簡單分析後,大概需要幾個狀態:

  • 裝置上是否有資原始檔(bool isAssetsEmpty)。在載入完成後,如果裝置沒有資源,則展示空佈局。
  • 選中路徑下是否有資源可供顯示(bool hasAssetsToDisplay)。切換路徑載入後,如果路徑下沒有資源,則展示空佈局。
  • 是否正在進行路徑選擇(bool isSwitchingPath)。正在進行切換路徑操作時,顯示路徑切換元件,並變換對應Widget
  • 所有資源路徑及其的第一個資源的縮圖資料(Map<AssetPathEntity, Uint8List> pathEntityList)。儲存所有的資源路徑,並載入他們的第一個資源的縮圖,提供給路徑切換元件。
  • 正在檢視的資源路徑(AssetPathEntity currentPathEntity)
  • 正在檢視的資源路徑的所有資源(List<AssetEntity> currentAssets)
  • 已經選中的資源(List<AssetEntity> selectedAssets)

  看上去很複雜,但上述狀態均為必要的內容,從而保證我們的選擇器能夠正常工作。

  這裡分享一個知識點:在model中我們常常需要儲存一些集合資料(Map/Set/List),在Selector進行比較時,由於比對的仍然是同一個物件,比較的時候prev == next,無法得出正確結果。這時我們需要使用集合的from方法,例如Map.fromList.from生成新的集合物件,就可以讓Selector正常的比較前後變化啦~舉個?

set selectedAssets(List<AssetEntity> value) {
  assert(value != null);
  if (value == _selectedAssets) {
    return;
  }
  _selectedAssets = List<AssetEntity>.from(value);
  notifyListeners();
}
複製程式碼

  狀態管理好了,我們還需要把選擇器使用到的方法,一同放進model,結合資料一同使用。這裡不再贅述原始碼,包含的方法有:獲取所有的資源路徑獲取指定路徑下的資源獲取指定路徑下的第一個資源的縮略資料選中&取消選中資源切換路徑

  至此繼續調整我們的靜態方法,在方法中構造model並傳入元件:

static Future<List<AssetEntity>> pickAssets(
  BuildContext context, {
  int maxAssets = 9,
  int pathThumbSize = 200,
  int gridCount = 4,
  RequestType requestType = RequestType.image,
  List<AssetEntity> selectedAssets,
  Color themeColor = C.themeColor,
  TextDelegate textDelegate,
}) async {
  final bool isPermissionGranted = await PhotoManager.requestPermission(); // 呼叫前檢查許可權,通過才拉起
  if (isPermissionGranted) {
    final AssetPickerProvider provider = AssetPickerProvider( // 構建model
      maxAssets: maxAssets,
      pathThumbSize: pathThumbSize,
      selectedAssets: selectedAssets,
      requestType: requestType,
    );
    final WidgetBuilder picker = (BuildContext _) => AssetPicker( // 構建元件
      provider: provider,
      gridCount: gridCount,
      textDelegate: textDelegate,
    );
    final List<AssetEntity> result = await Navigator.of(context).push<List<AssetEntity>>( // 構建路由
        Platform.isAndroid
            ? MaterialPageRoute<List<AssetEntity>>(builder: picker)
            : CupertinoPageRoute<List<AssetEntity>>(builder: picker),
    );
    return result;
  } else {
    return null;
  }
}
複製程式碼

文字代理構建

  在選擇器中我們必定有各處文字提示,或是在按鈕裡,或是在佈局填充裡。為了增加可定製程度,在此我定義了文字代理抽象類TextDelegate,用於構建各處文字。聞其名而知其意,上程式碼:

abstract class TextDelegate {
  /// 確認按鈕的欄位
  String confirm;

  /// 返回按鈕的欄位
  String cancel;

  /// 編輯按鈕的欄位
  String edit;

  /// 選擇器沒有可顯示的內容時的佔位欄位
  String emptyPlaceHolder;

  /// GIF指示的欄位
  String gifIndicator;

  /// HEIC型別資源載入失敗的欄位
  String heicNotSupported;

  /// 資源載入失敗時的欄位
  String loadFailed;

  /// 選擇是否原圖的欄位
  String original;

  /// 預覽按鈕的欄位
  String preview;

  /// 選擇按鈕的欄位
  String select;

  /// 未支援的資源型別的欄位
  String unSupportedAssetType;

  /// 該欄位用在選擇器視訊部件上,用於顯示視訊資源的時長。
  String videoIndicatorBuilder(Duration duration);
}
複製程式碼

  預設還提供了DefaultTextDelegate,作為預設的文字實現。

結語

  開發Flutter已經走過了一年的時間,慢慢地開始學會自己動手豐衣足食。下一篇我們將繼續分析外掛的介面開發內容~(我也不知道什麼時候有下一篇?)

  最後歡迎加入Flutter Candies,一起生產可愛的Flutter小糖果 (QQ群:181398081) FlutterCandies

幹一個Flutter元件:動動小手磨出一個資源多選外掛(1)——基礎構建篇

相關文章