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

相關文章

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

背景

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

  隨着Flutter的不斷髮展,愈來愈多的packages涌現,項目迭代使得multi_image_picker已逐漸不能知足需求,且其依賴的iOS原生依賴做者在開源方向上的態度也使得這個庫再也不穩定,我我的便萌生了本身定製插件的想法。因而利用清明前一週開始至今的閒時,結合本身的項目OpenJMU定製了一個純Dart的仿微信的資源選擇組件,本次主要使用了三個重要依賴:photo_manager出自財經龍大佬之手,提供完整的API獲取資源信息,爲定製資源選擇組件提供了啓動基礎🤣;extended_image出自法佬之手,做爲強力的圖片展現組件,體驗+++++;以及你們熟知的provider用於維護選擇器及各類部件的狀態。github

簡介

  wechat_assets_picker是一個對標微信的多選資源選擇器,99%接近於原生微信的操做,純Dart編寫,支持選擇的同時也支持預覽資源。本篇闡述的內容,在源碼中均有對應的註釋進行簡易說明。若是你是源碼選手,請移步repo。截止發文已於pub發佈1.3.0版本,支持以下功能:微信

  • 圖片資源支持
  • 視頻資源支持
  • 國際化支持
  • 自定義文本支持

  效果圖: markdown

實現過程

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

調用方法

  調用一個組件的方法是一切的開始,既然這個組件是一個純Dart組件,那麼也應該基於Flutter的上下文(context)進行調用,用於路由跳轉。因此咱們很快的寫出一個組件的靜態調用方法:async

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

  做爲一個多選組件,固然須要知道我能選幾個資源,因此加入int maxAssets指定最大的資源可選數量,默認爲9ide

  用戶能夠自定義網格數量,因此加入int gridCount指定網格每行格子數,默認爲4佈局

  用戶能夠指定縮略圖的清晰度,因此加入int pageThumbSize指定選擇器中縮略圖加載的像素,默認爲200性能

  再加億點......ui

  最後咱們的靜態調用方法以下:

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

相關文章
相關標籤/搜索