Создание компонента Flutter: создание плагина с множественным выбором ресурсов (1) — базовая конструкция

Flutter
Создание компонента Flutter: создание плагина с множественным выбором ресурсов (1) — базовая конструкция

Статьи по Теме

  • Создание компонента Flutter: создание плагина с множественным выбором ресурсов (1) — базовая конструкция
  • Сделать компонент Flutter: отшлифовать плагин множественного выбора ресурсов (2) — разработка интерфейса (в процессе подготовки)

задний план

Раньше во Flutter были очень полезные компоненты с множественным выбором, такие какSh1d0wизmulti_image_picker, но все они имеют более или менее проблемы, такие как отсутствие поддержки выбора GIF, отсутствие поддержки выбора видео или аудио, недостаточно высокая настройка, использование собственных компонентов, а не чистых компонентов Dart и т. д.

С непрерывным развитием Flutter появляется все больше и больше пакетов, и итерация проекта делаетmulti_image_pickerПостепенно он перестал удовлетворять потребности, а отношение автора к родной зависимости iOS от направления с открытым исходным кодом также сделало эту библиотеку не стабильной.У меня лично есть идея настроить плагин . Поэтому я использовал свободное время за неделю до Цинминского фестиваля до сегодняшнего дня, чтобы объединить свои собственные проекты.OpenJMUНастроен компонент выбора ресурсов WeChat, похожий на Dart.На этот раз в основном используются три важные зависимости:photo_managerотФинансовый драконHand, предоставляет полный API для получения информации о ресурсах и обеспечивает основу для запуска пользовательских компонентов выбора ресурсов 🤣;extended_imageотФранцузскийРука, как мощный компонент отображения изображения, опыт +++++; и известныйproviderИспользуется для поддержания состояния селекторов и различных компонентов.

Введение

wechat_assets_pickerЭто селектор ресурсов с множественным выбором для WeChat, который на 99% близок к работе родного WeChat, написан на чистом Dart, поддерживает выбор и предварительный просмотр ресурсов. Содержание, описанное в этой статье, кратко объясняется в соответствующих комментариях в исходном коде. Если вы являетесь исходным игроком, перейдите в репозиторий. На момент публикации на пабе вышла версия 1.3.0, которая поддерживает следующие функции:

  • Поддержка ресурсов изображений
  • Поддержка видео ресурсов
  • Международная поддержка
  • Поддержка пользовательского текста

Рендеринг:

Процесс реализации

Ниже приведен конкретный процесс реализации, который будет объяснен для версии 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Что касается модели селектора, управляющего соответствующим состоянием, так как он предполагает отображение большого количества ресурсов, если локальное управление не выполняется, производительность будет сильно снижена. Далее приступайте к проектированиюAssetPickerProvider.

Какое состояние должен поддерживать селектор? После несложного анализа наверняка потребуется несколько состояний:

  • Есть ли файл ресурса на устройстве (bool isAssetsEmpty). После загрузки, если на устройстве нет ресурсов, показать пустой макет.
  • Есть ли ресурсы для отображения по выбранному пути (bool hasAssetsToDisplay). После загрузки пути переключения, если по пути нет ресурса, будет отображаться пустой макет.
  • Выполняется ли выбор пути (bool isSwitchingPath). Когда выполняется операция переключения пути, отображается компонент переключения пути и соответствующийWidget.
  • Эскизы данных для всех путей ресурсов и их первого ресурса (Map<AssetPathEntity, Uint8List> pathEntityList). Сохраняет все пути к ресурсам и загружает их первую миниатюру ресурса, которая предоставляется переключателю пути.
  • Просматриваемый путь ресурса (AssetPathEntity currentPathEntity).
  • Все ресурсы просматриваемого пути ресурсов (List<AssetEntity> currentAssets).
  • Выбранный ресурс (List<AssetEntity> selectedAssets).

Это выглядит сложно, но все вышеперечисленные состояния необходимы для правильной работы нашего селектора.

Вот знание, которым можно поделиться: в модели нам часто нужно хранить некоторые данные коллекции (Map/Set/List),существуетSelectorПри сравнении,Так как сравнение это все тот же объект, то при сравненииprev == next, правильный результат не может быть получен. В этом случае нам нужно использовать коллекциюfromтакие методы, какMap.from,List.fromЧтобы сгенерировать новый объект коллекции, вы можете позволитьSelectorНормальное сравнение изменилось до и после~ Например 🌰

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

После управления состоянием нам также нужно поместить методы, используемые селектором, в модель и использовать их вместе с данными. Исходный код здесь не повторяется, включены следующие методы:Получить все пути к ресурсам,Получить ресурсы по указанному пути,Получить сокращенные данные первого ресурса по указанному пути,Отметить и снять отметку с ресурсов,переключить путь.

Пока что мы продолжаем настраивать наш статический метод, конструируем модель в методе и передаем компонент:

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