Статьи по Теме
- Создание компонента 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)