From be8fdf5eedfa64b1dabe7df9bf6d811ae09b285e Mon Sep 17 00:00:00 2001 From: yujune Date: Fri, 27 Sep 2024 10:45:35 +0800 Subject: [PATCH 01/23] =?UTF-8?q?=E2=9C=A8=20Support=20multiple=20special?= =?UTF-8?q?=20items.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README-ZH.md | 3 +- README.md | 3 +- example/lib/constants/picker_method.dart | 233 +++++++++++------- .../pickers/directory_file_asset_picker.dart | 17 +- .../customs/pickers/insta_asset_picker.dart | 1 - example/lib/l10n/app_en.arb | 2 + example/lib/l10n/app_zh.arb | 2 + example/lib/l10n/gen/app_localizations.dart | 12 + .../lib/l10n/gen/app_localizations_en.dart | 7 + .../lib/l10n/gen/app_localizations_zh.dart | 7 + example/lib/pages/multi_assets_page.dart | 1 + example/lib/pages/single_assets_page.dart | 1 + lib/src/constants/config.dart | 23 +- lib/src/constants/enums.dart | 4 - lib/src/constants/typedefs.dart | 8 + .../asset_picker_builder_delegate.dart | 134 +++++----- lib/src/delegates/asset_picker_delegate.dart | 3 +- lib/src/models/special_item.dart | 20 ++ lib/wechat_assets_picker.dart | 2 +- test/test_utils.dart | 3 +- 20 files changed, 302 insertions(+), 184 deletions(-) create mode 100644 lib/src/models/special_item.dart diff --git a/README-ZH.md b/README-ZH.md index fa053f0c..072c6177 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -295,8 +295,7 @@ final List? result = await AssetPicker.pickAssets( | themeColor | `Color?` | 选择器的主题色 | `Color(0xff00bc56)` | | pickerTheme | `ThemeData?` | 选择器的主题提供,包括查看器 | `null` | | textDelegate | `AssetPickerTextDelegate?` | 选择器的文本代理构建,用于自定义文本 | `AssetPickerTextDelegate()` | -| specialItemPosition | `SpecialItemPosition` | 允许用户在选择器中添加一个自定义item,并指定位置。 | `SpecialPosition.none` | -| specialItemBuilder | `SpecialItemBuilder?` | 自定义item的构造方法 | `null` | +| specialItems | `List` | 自定义item列表 | `const []` | | loadingIndicatorBuilder | `IndicatorBuilder?` | 加载器的实现 | `null` | | selectPredicate | `AssetSelectPredicate` | 判断资源可否被选择 | `null` | | shouldRevertGrid | `bool?` | 判断资源网格是否需要倒序排列 | `null` | diff --git a/README.md b/README.md index 10a4bb1e..242e8757 100644 --- a/README.md +++ b/README.md @@ -304,8 +304,7 @@ Fields in `AssetPickerConfig`: | themeColor | `Color?` | Main theme color for the picker. | `Color(0xff00bc56)` | | pickerTheme | `ThemeData?` | Theme data provider for the picker and the viewer. | `null` | | textDelegate | `AssetPickerTextDelegate?` | Text delegate for the picker, for customize the texts. | `AssetPickerTextDelegate()` | -| specialItemPosition | `SpecialItemPosition` | Allow users set a special item in the picker with several positions. | `SpecialItemPosition.none` | -| specialItemBuilder | `SpecialItemBuilder?` | The widget builder for the special item. | `null` | +| specialItems | `List` | List of special items. | `const []` | | loadingIndicatorBuilder | `IndicatorBuilder?` | Indicates the loading status for the builder. | `null` | | selectPredicate | `AssetSelectPredicate` | Predicate whether an asset can be selected or unselected. | `null` | | shouldRevertGrid | `bool?` | Whether the assets grid should revert. | `null` | diff --git a/example/lib/constants/picker_method.dart b/example/lib/constants/picker_method.dart index 00857410..a9e77197 100644 --- a/example/lib/constants/picker_method.dart +++ b/example/lib/constants/picker_method.dart @@ -135,39 +135,45 @@ class PickMethod { pickerConfig: AssetPickerConfig( maxAssets: maxAssetsCount, selectedAssets: assets, - specialItemPosition: SpecialItemPosition.prepend, - specialItemBuilder: ( - BuildContext context, - AssetPathEntity? path, - int length, - ) { - if (path?.isAll != true) { - return null; - } - return Semantics( - label: textDelegate.sActionUseCameraHint, - button: true, - onTapHint: textDelegate.sActionUseCameraHint, - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () async { - Feedback.forTap(context); - final AssetEntity? result = await _pickFromCamera(context); - if (result != null) { - handleResult(context, result); - } - }, - child: Container( - padding: const EdgeInsets.all(28.0), - color: Theme.of(context).dividerColor, - child: const FittedBox( - fit: BoxFit.fill, - child: Icon(Icons.camera_enhance), + specialItems: [ + SpecialItem( + position: SpecialItemPosition.prepend, + builder: ( + BuildContext context, + AssetPathEntity? path, + int length, + bool isPermissionLimited, + ) { + if (path?.isAll != true) { + return null; + } + return Semantics( + label: textDelegate.sActionUseCameraHint, + button: true, + onTapHint: textDelegate.sActionUseCameraHint, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () async { + Feedback.forTap(context); + final AssetEntity? result = + await _pickFromCamera(context); + if (result != null) { + handleResult(context, result); + } + }, + child: Container( + padding: const EdgeInsets.all(28.0), + color: Theme.of(context).dividerColor, + child: const FittedBox( + fit: BoxFit.fill, + child: Icon(Icons.camera_enhance), + ), + ), ), - ), - ), - ); - }, + ); + }, + ), + ], ), ); }, @@ -186,50 +192,56 @@ class PickMethod { pickerConfig: AssetPickerConfig( maxAssets: maxAssetsCount, selectedAssets: assets, - specialItemPosition: SpecialItemPosition.prepend, - specialItemBuilder: ( - BuildContext context, - AssetPathEntity? path, - int length, - ) { - if (path?.isAll != true) { - return null; - } - return Semantics( - label: textDelegate.sActionUseCameraHint, - button: true, - onTapHint: textDelegate.sActionUseCameraHint, - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () async { - final AssetEntity? result = await _pickFromCamera(context); - if (result == null) { - return; - } - final picker = context.findAncestorWidgetOfExactType< - AssetPicker>()!; - final builder = - picker.builder as DefaultAssetPickerBuilderDelegate; - final p = builder.provider; - await p.switchPath( - PathWrapper( - path: - await p.currentPath!.path.obtainForNewProperties(), + specialItems: [ + SpecialItem( + position: SpecialItemPosition.prepend, + builder: ( + BuildContext context, + AssetPathEntity? path, + int length, + bool isPermissionLimited, + ) { + if (path?.isAll != true) { + return null; + } + return Semantics( + label: textDelegate.sActionUseCameraHint, + button: true, + onTapHint: textDelegate.sActionUseCameraHint, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () async { + final AssetEntity? result = + await _pickFromCamera(context); + if (result == null) { + return; + } + final picker = context.findAncestorWidgetOfExactType< + AssetPicker>()!; + final builder = + picker.builder as DefaultAssetPickerBuilderDelegate; + final p = builder.provider; + await p.switchPath( + PathWrapper( + path: await p.currentPath!.path + .obtainForNewProperties(), + ), + ); + p.selectAsset(result); + }, + child: Container( + padding: const EdgeInsets.all(28.0), + color: Theme.of(context).dividerColor, + child: const FittedBox( + fit: BoxFit.fill, + child: Icon(Icons.camera_enhance), + ), ), - ); - p.selectAsset(result); - }, - child: Container( - padding: const EdgeInsets.all(28.0), - color: Theme.of(context).dividerColor, - child: const FittedBox( - fit: BoxFit.fill, - child: Icon(Icons.camera_enhance), ), - ), - ), - ); - }, + ); + }, + ), + ], ), ); }, @@ -295,16 +307,69 @@ class PickMethod { pickerConfig: AssetPickerConfig( maxAssets: maxAssetsCount, selectedAssets: assets, - specialItemPosition: SpecialItemPosition.prepend, - specialItemBuilder: ( - BuildContext context, - AssetPathEntity? path, - int length, - ) { - return const Center( - child: Text('Custom Widget', textAlign: TextAlign.center), - ); - }, + specialItems: [ + SpecialItem( + position: SpecialItemPosition.prepend, + builder: ( + BuildContext context, + AssetPathEntity? path, + int length, + bool isPermissionLimited, + ) { + return const Center( + child: Text('Custom Widget', textAlign: TextAlign.center), + ); + }, + ), + ], + ), + ); + }, + ); + } + + factory PickMethod.multiSpecialItems( + BuildContext context, + int maxAssetsCount, + ) { + return PickMethod( + icon: '💡', + name: context.l10n.pickMethodMultiSpecialItemsName, + description: context.l10n.pickMethodMultiSpecialItemsDescription, + method: (BuildContext context, List assets) { + return AssetPicker.pickAssets( + context, + pickerConfig: AssetPickerConfig( + maxAssets: maxAssetsCount, + selectedAssets: assets, + specialItems: [ + SpecialItem( + position: SpecialItemPosition.prepend, + builder: ( + BuildContext context, + AssetPathEntity? path, + int length, + bool isPermissionLimited, + ) { + return const Center( + child: Text('Prepand Widget', textAlign: TextAlign.center), + ); + }, + ), + SpecialItem( + position: SpecialItemPosition.append, + builder: ( + BuildContext context, + AssetPathEntity? path, + int length, + bool isPermissionLimited, + ) { + return const Center( + child: Text('Append Widget', textAlign: TextAlign.center), + ); + }, + ), + ], ), ); }, diff --git a/example/lib/customs/pickers/directory_file_asset_picker.dart b/example/lib/customs/pickers/directory_file_asset_picker.dart index 535cb316..d04826bf 100644 --- a/example/lib/customs/pickers/directory_file_asset_picker.dart +++ b/example/lib/customs/pickers/directory_file_asset_picker.dart @@ -584,7 +584,7 @@ class FileAssetPickerBuilder Widget assetsGridBuilder(BuildContext context) { appBarPreferredSize ??= appBar(context).preferredSize; int totalCount = provider.currentAssets.length; - if (specialItemPosition != SpecialItemPosition.none) { + if (specialItems.isNotEmpty) { totalCount += 1; } final int placeholderCount; @@ -701,10 +701,10 @@ class FileAssetPickerBuilder int index, List currentAssets, ) { - final int currentIndex = switch (specialItemPosition) { - SpecialItemPosition.none || SpecialItemPosition.append => index, - SpecialItemPosition.prepend => index - 1, - }; + int currentIndex = index; + + currentIndex = index - prependSpecialItems.length; + final File asset = currentAssets.elementAt(currentIndex); final Widget builder = imageAndVideoItemBuilder( context, @@ -737,12 +737,7 @@ class FileAssetPickerBuilder required List assets, int placeholderCount = 0, }) { - final int length = switch (specialItemPosition) { - SpecialItemPosition.none => assets.length, - SpecialItemPosition.prepend || - SpecialItemPosition.append => - assets.length + 1, - }; + final int length = assets.length + specialItems.length; return length + placeholderCount; } diff --git a/example/lib/customs/pickers/insta_asset_picker.dart b/example/lib/customs/pickers/insta_asset_picker.dart index 879fd87d..c0ed62f3 100644 --- a/example/lib/customs/pickers/insta_asset_picker.dart +++ b/example/lib/customs/pickers/insta_asset_picker.dart @@ -297,7 +297,6 @@ class InstaAssetPickerBuilder extends DefaultAssetPickerBuilderDelegate { super.keepScrollOffset, }) : super( shouldRevertGrid: false, - specialItemPosition: SpecialItemPosition.none, ); /// Save last position of the grid view scroll controller diff --git a/example/lib/l10n/app_en.arb b/example/lib/l10n/app_en.arb index d40489ed..029e46a9 100644 --- a/example/lib/l10n/app_en.arb +++ b/example/lib/l10n/app_en.arb @@ -28,6 +28,8 @@ "pickMethodCustomFilterOptionsDescription": "Add filter options for the picker.", "pickMethodPrependItemName": "Prepend special item", "pickMethodPrependItemDescription": "A special item will prepend to the assets grid.", + "pickMethodMultiSpecialItemsName": "Multiple special items", + "pickMethodMultiSpecialItemsDescription": "Multiple special items will prepend or append to the assets grid", "pickMethodNoPreviewName": "No preview", "pickMethodNoPreviewDescription": "You cannot preview assets during the picking, the behavior is like the WhatsApp/MegaTok pattern.", "pickMethodKeepScrollOffsetName": "Keep scroll offset", diff --git a/example/lib/l10n/app_zh.arb b/example/lib/l10n/app_zh.arb index 79df1bd6..151514b2 100644 --- a/example/lib/l10n/app_zh.arb +++ b/example/lib/l10n/app_zh.arb @@ -28,6 +28,8 @@ "pickMethodCustomFilterOptionsDescription": "为选择器添加自定义过滤条件。", "pickMethodPrependItemName": "往网格前插入 widget", "pickMethodPrependItemDescription": "网格的靠前位置会添加一个自定义的 widget。", + "pickMethodMultiSpecialItemsName": "多个特殊 widget", + "pickMethodMultiSpecialItemsDescription": "网格的靠前或靠后位置会可以多个自定义的 widget。", "pickMethodNoPreviewName": "禁止预览", "pickMethodNoPreviewDescription": "无法预览选择的资源,与 WhatsApp/MegaTok 的行为类似。", "pickMethodKeepScrollOffsetName": "保持滚动位置", diff --git a/example/lib/l10n/gen/app_localizations.dart b/example/lib/l10n/gen/app_localizations.dart index 9cf300a3..fbdf6b06 100644 --- a/example/lib/l10n/gen/app_localizations.dart +++ b/example/lib/l10n/gen/app_localizations.dart @@ -266,6 +266,18 @@ abstract class AppLocalizations { /// **'A special item will prepend to the assets grid.'** String get pickMethodPrependItemDescription; + /// No description provided for @pickMethodMultiSpecialItemsName. + /// + /// In en, this message translates to: + /// **'Multiple special items'** + String get pickMethodMultiSpecialItemsName; + + /// No description provided for @pickMethodMultiSpecialItemsDescription. + /// + /// In en, this message translates to: + /// **'Multiple special items will prepend or append to the assets grid'** + String get pickMethodMultiSpecialItemsDescription; + /// No description provided for @pickMethodNoPreviewName. /// /// In en, this message translates to: diff --git a/example/lib/l10n/gen/app_localizations_en.dart b/example/lib/l10n/gen/app_localizations_en.dart index 6419ff94..9d0bfce7 100644 --- a/example/lib/l10n/gen/app_localizations_en.dart +++ b/example/lib/l10n/gen/app_localizations_en.dart @@ -101,6 +101,13 @@ class AppLocalizationsEn extends AppLocalizations { String get pickMethodPrependItemDescription => 'A special item will prepend to the assets grid.'; + @override + String get pickMethodMultiSpecialItemsName => 'Multiple special items'; + + @override + String get pickMethodMultiSpecialItemsDescription => + 'Multiple special items will prepend or append to the assets grid'; + @override String get pickMethodNoPreviewName => 'No preview'; diff --git a/example/lib/l10n/gen/app_localizations_zh.dart b/example/lib/l10n/gen/app_localizations_zh.dart index 3306b449..e5147914 100644 --- a/example/lib/l10n/gen/app_localizations_zh.dart +++ b/example/lib/l10n/gen/app_localizations_zh.dart @@ -95,6 +95,13 @@ class AppLocalizationsZh extends AppLocalizations { @override String get pickMethodPrependItemDescription => '网格的靠前位置会添加一个自定义的 widget。'; + @override + String get pickMethodMultiSpecialItemsName => '多个特殊 widget'; + + @override + String get pickMethodMultiSpecialItemsDescription => + '网格的靠前或靠后位置会可以多个自定义的 widget。'; + @override String get pickMethodNoPreviewName => '禁止预览'; diff --git a/example/lib/pages/multi_assets_page.dart b/example/lib/pages/multi_assets_page.dart index 016486da..4d0b30cc 100644 --- a/example/lib/pages/multi_assets_page.dart +++ b/example/lib/pages/multi_assets_page.dart @@ -46,6 +46,7 @@ class _MultiAssetsPageState extends State PickMethod.changeLanguages(context, maxAssetsCount), PickMethod.threeItemsGrid(context, maxAssetsCount), PickMethod.prependItem(context, maxAssetsCount), + PickMethod.multiSpecialItems(context, maxAssetsCount), PickMethod( icon: '🎭', name: context.l10n.pickMethodWeChatMomentName, diff --git a/example/lib/pages/single_assets_page.dart b/example/lib/pages/single_assets_page.dart index 612277dd..ff5a39fb 100644 --- a/example/lib/pages/single_assets_page.dart +++ b/example/lib/pages/single_assets_page.dart @@ -46,6 +46,7 @@ class _SingleAssetPageState extends State PickMethod.changeLanguages(context, maxAssetsCount), PickMethod.threeItemsGrid(context, maxAssetsCount), PickMethod.prependItem(context, maxAssetsCount), + PickMethod.multiSpecialItems(context, maxAssetsCount), PickMethod.customFilterOptions(context, maxAssetsCount), PickMethod.preventGIFPicked(context, maxAssetsCount), PickMethod.noPreview(context, maxAssetsCount), diff --git a/lib/src/constants/config.dart b/lib/src/constants/config.dart index 939f5fa8..b22bfd2d 100644 --- a/lib/src/constants/config.dart +++ b/lib/src/constants/config.dart @@ -8,6 +8,7 @@ import 'package:photo_manager/photo_manager.dart'; import '../constants/typedefs.dart'; import '../delegates/asset_picker_text_delegate.dart'; import '../delegates/sort_path_delegate.dart'; +import '../models/special_item.dart'; import 'constants.dart'; import 'enums.dart'; @@ -29,8 +30,6 @@ class AssetPickerConfig { this.themeColor, this.pickerTheme, this.textDelegate, - this.specialItemPosition = SpecialItemPosition.none, - this.specialItemBuilder, this.loadingIndicatorBuilder, this.selectPredicate, this.shouldRevertGrid, @@ -39,6 +38,7 @@ class AssetPickerConfig { this.assetsChangeCallback, this.assetsChangeRefreshPredicate, this.shouldAutoplayPreview = false, + this.specialItems = const [], }) : assert( pickerTheme == null || themeColor == null, 'pickerTheme and themeColor cannot be set at the same time.', @@ -55,13 +55,6 @@ class AssetPickerConfig { requestType == RequestType.common, 'SpecialPickerType.wechatMoment and requestType ' 'cannot be set at the same time.', - ), - assert( - (specialItemBuilder == null && - identical(specialItemPosition, SpecialItemPosition.none)) || - (specialItemBuilder != null && - !identical(specialItemPosition, SpecialItemPosition.none)), - 'Custom item did not set properly.', ); /// Selected assets. @@ -167,14 +160,6 @@ class AssetPickerConfig { final AssetPickerTextDelegate? textDelegate; - /// Allow users set a special item in the picker with several positions. - /// 允许用户在选择器中添加一个自定义item,并指定位置 - final SpecialItemPosition specialItemPosition; - - /// The widget builder for the the special item. - /// 自定义item的构造方法 - final SpecialItemBuilder? specialItemBuilder; - /// Indicates the loading status for the builder. /// 指示目前加载的状态 final LoadingIndicatorBuilder? loadingIndicatorBuilder; @@ -205,4 +190,8 @@ class AssetPickerConfig { /// Whether the preview should auto play. /// 预览是否自动播放 final bool shouldAutoplayPreview; + + /// List of special items. + /// 自定义item列表 + final List> specialItems; } diff --git a/lib/src/constants/enums.dart b/lib/src/constants/enums.dart index 686c563d..ba3bdff9 100644 --- a/lib/src/constants/enums.dart +++ b/lib/src/constants/enums.dart @@ -30,10 +30,6 @@ enum SpecialPickerType { /// Provide an item slot for custom widget insertion. /// 提供一个自定义位置供特殊item放入资源列表中。 enum SpecialItemPosition { - /// Not insert to the list. - /// 不放入列表 - none, - /// Add as leading of the list. /// 在列表前放入 prepend, diff --git a/lib/src/constants/typedefs.dart b/lib/src/constants/typedefs.dart index e8e5c689..8fc40470 100644 --- a/lib/src/constants/typedefs.dart +++ b/lib/src/constants/typedefs.dart @@ -9,6 +9,8 @@ import 'package:flutter/widgets.dart'; import 'package:photo_manager/photo_manager.dart' show PermissionState; import 'package:provider/provider.dart'; +import './enums.dart'; + /// Mirroring [ChangeNotifierProvider]. typedef CNP = ChangeNotifierProvider; @@ -29,6 +31,7 @@ typedef SpecialItemBuilder = Widget? Function( BuildContext context, Path? path, int length, + bool isPermissionLimited, ); /// {@template wechat_assets_picker.AssetSelectPredicate} @@ -75,3 +78,8 @@ typedef AssetsChangeRefreshPredicate = bool Function( MethodCall call, Path? path, ); + +typedef SpecialItemModel = ({ + SpecialItemPosition position, + Widget item, +}); diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index aa85ec03..982a8e39 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -21,6 +21,7 @@ import '../constants/typedefs.dart'; import '../delegates/asset_picker_text_delegate.dart'; import '../internals/singleton.dart'; import '../models/path_wrapper.dart'; +import '../models/special_item.dart'; import '../provider/asset_picker_provider.dart'; import '../widget/asset_picker.dart'; import '../widget/asset_picker_app_bar.dart'; @@ -38,8 +39,7 @@ abstract class AssetPickerBuilderDelegate { required this.initialPermission, this.gridCount = 4, this.pickerTheme, - this.specialItemPosition = SpecialItemPosition.none, - this.specialItemBuilder, + this.specialItems = const [], this.loadingIndicatorBuilder, this.selectPredicate, this.shouldRevertGrid, @@ -57,7 +57,12 @@ abstract class AssetPickerBuilderDelegate { ), themeColor = pickerTheme?.colorScheme.secondary ?? themeColor ?? - defaultThemeColorWeChat { + defaultThemeColorWeChat, + prependSpecialItems = specialItems + .where( + (item) => item.position == SpecialItemPosition.prepend, + ) + .toList() { Singleton.textDelegate = textDelegate ?? assetPickerTextDelegateFromLocale(locale); } @@ -84,13 +89,9 @@ abstract class AssetPickerBuilderDelegate { /// 但某些情况下开发者需要亮色或自定义主题。 final ThemeData? pickerTheme; - /// Allow users set a special item in the picker with several positions. - /// 允许用户在选择器中添加一个自定义 item,并指定位置 - final SpecialItemPosition specialItemPosition; - - /// The widget builder for the the special item. - /// 自定义 item 的构造方法 - final SpecialItemBuilder? specialItemBuilder; + /// List of special items. + /// 自定义item列表 + final List> specialItems; /// Indicates the loading status for the builder. /// 指示目前加载的状态 @@ -130,6 +131,10 @@ abstract class AssetPickerBuilderDelegate { final AssetsChangeRefreshPredicate? assetsChangeRefreshPredicate; + /// List of prepend special items. + /// 前置自定义item列表 + final List> prependSpecialItems; + /// [ThemeData] for the picker. /// 选择器使用的主题 ThemeData get theme => pickerTheme ?? AssetPicker.themeData(themeColor); @@ -170,9 +175,7 @@ abstract class AssetPickerBuilderDelegate { /// Whether the delegate should build the special item. /// 是否需要构建自定义 item - bool get shouldBuildSpecialItem => - specialItemPosition != SpecialItemPosition.none && - specialItemBuilder != null; + bool get shouldBuildSpecialItem => specialItems.isNotEmpty; /// Space between assets item widget. /// 资源部件之间的间隔 @@ -743,8 +746,6 @@ class DefaultAssetPickerBuilderDelegate required super.initialPermission, super.gridCount, super.pickerTheme, - super.specialItemPosition, - super.specialItemBuilder, super.loadingIndicatorBuilder, super.selectPredicate, super.shouldRevertGrid, @@ -760,6 +761,7 @@ class DefaultAssetPickerBuilderDelegate this.specialPickerType, this.keepScrollOffset = false, this.shouldAutoplayPreview = false, + super.specialItems = const [], }) { // Add the listener if [keepScrollOffset] is true. if (keepScrollOffset) { @@ -1231,21 +1233,25 @@ class DefaultAssetPickerBuilderDelegate builder: (context, wrapper, _) { // First, we need the count of the assets. int totalCount = wrapper?.assetCount ?? 0; - final Widget? specialItem; - // If user chose a special item's position, add 1 count. - if (specialItemPosition != SpecialItemPosition.none) { - specialItem = specialItemBuilder?.call( - context, - wrapper?.path, - totalCount, - ); - if (specialItem != null) { - totalCount += 1; - } - } else { - specialItem = null; - } - if (totalCount == 0 && specialItem == null) { + + final List specialItemModels = specialItems + .map((item) { + return ( + position: item.position, + item: item.builder?.call( + context, + wrapper?.path, + totalCount, + isPermissionLimited, + ) + ); + }) + .whereType() + .toList(); + + totalCount += specialItemModels.length; + + if (totalCount == 0 && specialItemModels.isEmpty) { return loadingIndicator(context); } // Then we use the [totalCount] to calculate placeholders we need. @@ -1285,7 +1291,7 @@ class DefaultAssetPickerBuilderDelegate context, index, assets, - specialItem: specialItem, + specialItemModels: specialItemModels, ), ), ); @@ -1294,7 +1300,7 @@ class DefaultAssetPickerBuilderDelegate context: context, assets: assets, placeholderCount: placeholderCount, - specialItem: specialItem, + specialItemModels: specialItemModels, ), findChildIndexCallback: (Key? key) { if (key is ValueKey) { @@ -1404,7 +1410,7 @@ class DefaultAssetPickerBuilderDelegate BuildContext context, int index, List currentAssets, { - Widget? specialItem, + List specialItemModels = const [], }) { final DefaultAssetPickerProvider p = context.read(); @@ -1412,21 +1418,30 @@ class DefaultAssetPickerBuilderDelegate final PathWrapper? currentWrapper = p.currentPath; final AssetPathEntity? currentPathEntity = currentWrapper?.path; - if (specialItem != null) { - if ((index == 0 && specialItemPosition == SpecialItemPosition.prepend) || - (index == length && - specialItemPosition == SpecialItemPosition.append)) { - return specialItem; + final prepandSpecialItemModels = specialItemModels.where( + (model) => model.position == SpecialItemPosition.prepend, + ); + final appendSpecialItemModels = specialItemModels.where( + (model) => model.position == SpecialItemPosition.append, + ); + + if (specialItemModels.isNotEmpty) { + if (prepandSpecialItemModels.isNotEmpty) { + if (index < prepandSpecialItemModels.length) { + return specialItemModels[index].item; + } + } + + if (appendSpecialItemModels.isNotEmpty) { + if (index >= length + prepandSpecialItemModels.length) { + return specialItemModels[index - length].item; + } } } - final int currentIndex; - if (specialItem != null && - specialItemPosition == SpecialItemPosition.prepend) { - currentIndex = index - 1; - } else { - currentIndex = index; - } + int currentIndex = index; + + currentIndex = index - prepandSpecialItemModels.length; if (currentPathEntity == null) { return const SizedBox.shrink(); @@ -1461,9 +1476,10 @@ class DefaultAssetPickerBuilderDelegate } int semanticIndex(int index) { - if (specialItemPosition != SpecialItemPosition.prepend) { - return index + 1; + if (prependSpecialItems.isNotEmpty) { + return index - prependSpecialItems.length + 1; } + return index; } @@ -1556,9 +1572,9 @@ class DefaultAssetPickerBuilderDelegate int placeholderCount = 0, }) { int index = assets.indexWhere((AssetEntity e) => e.id == id); - if (specialItemPosition == SpecialItemPosition.prepend) { - index += 1; - } + + index += prependSpecialItems.length; + index += placeholderCount; return index; } @@ -1568,7 +1584,7 @@ class DefaultAssetPickerBuilderDelegate required BuildContext context, required List assets, int placeholderCount = 0, - Widget? specialItem, + List specialItemModels = const [], }) { final PathWrapper? currentWrapper = context .select?>( @@ -1578,19 +1594,21 @@ class DefaultAssetPickerBuilderDelegate final int length = assets.length + placeholderCount; // Return 1 if the [specialItem] build something. - if (currentPathEntity == null && specialItem != null) { - return placeholderCount + 1; + if (currentPathEntity == null && specialItemModels.isNotEmpty) { + return placeholderCount + specialItemModels.length; } // Return actual length if the current path is all. // 如果当前目录是全部内容,则返回实际的内容数量。 - if (currentPathEntity?.isAll != true && specialItem == null) { + if (currentPathEntity?.isAll != true && specialItemModels.isEmpty) { return length; } - return switch (specialItemPosition) { - SpecialItemPosition.none => length, - SpecialItemPosition.prepend || SpecialItemPosition.append => length + 1, - }; + + if (specialItemModels.isEmpty) { + return length; + } + + return length + specialItemModels.length; } @override diff --git a/lib/src/delegates/asset_picker_delegate.dart b/lib/src/delegates/asset_picker_delegate.dart index 3f788b3d..c6943fe4 100644 --- a/lib/src/delegates/asset_picker_delegate.dart +++ b/lib/src/delegates/asset_picker_delegate.dart @@ -105,8 +105,6 @@ class AssetPickerDelegate { gridThumbnailSize: pickerConfig.gridThumbnailSize, previewThumbnailSize: pickerConfig.previewThumbnailSize, specialPickerType: pickerConfig.specialPickerType, - specialItemPosition: pickerConfig.specialItemPosition, - specialItemBuilder: pickerConfig.specialItemBuilder, loadingIndicatorBuilder: pickerConfig.loadingIndicatorBuilder, selectPredicate: pickerConfig.selectPredicate, shouldRevertGrid: pickerConfig.shouldRevertGrid, @@ -119,6 +117,7 @@ class AssetPickerDelegate { themeColor: pickerConfig.themeColor, locale: Localizations.maybeLocaleOf(context), shouldAutoplayPreview: pickerConfig.shouldAutoplayPreview, + specialItems: pickerConfig.specialItems, ), ); final List? result = await Navigator.maybeOf( diff --git a/lib/src/models/special_item.dart b/lib/src/models/special_item.dart new file mode 100644 index 00000000..10b9c169 --- /dev/null +++ b/lib/src/models/special_item.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:wechat_assets_picker/wechat_assets_picker.dart'; + +/// Allow users to set a special item in the picker grid with specified [position]. +/// 允许用户在选择器中添加一个自定义item,并指定其位置。 +@immutable +class SpecialItem { + const SpecialItem({ + required this.builder, + required this.position, + }); + + /// The widget builder for the the special item. + /// 自定义item构建。 + final SpecialItemBuilder? builder; + + /// Define how the item will be positioned. + /// 定义如何摆放item。 + final SpecialItemPosition position; +} diff --git a/lib/wechat_assets_picker.dart b/lib/wechat_assets_picker.dart index ca03acff..dd2afb69 100644 --- a/lib/wechat_assets_picker.dart +++ b/lib/wechat_assets_picker.dart @@ -12,7 +12,6 @@ export 'src/constants/config.dart'; export 'src/constants/constants.dart' hide packageName; export 'src/constants/enums.dart'; export 'src/constants/typedefs.dart'; - export 'src/delegates/asset_picker_builder_delegate.dart'; export 'src/delegates/asset_picker_delegate.dart'; export 'src/delegates/asset_picker_text_delegate.dart'; @@ -20,6 +19,7 @@ export 'src/delegates/asset_picker_viewer_builder_delegate.dart'; export 'src/delegates/sort_path_delegate.dart'; export 'src/models/path_wrapper.dart'; +export 'src/models/special_item.dart'; export 'src/provider/asset_picker_provider.dart'; export 'src/provider/asset_picker_viewer_provider.dart'; diff --git a/test/test_utils.dart b/test/test_utils.dart index c5d0a670..91eb9fdd 100644 --- a/test/test_utils.dart +++ b/test/test_utils.dart @@ -131,8 +131,7 @@ class TestAssetPickerDelegate extends AssetPickerDelegate { gridThumbnailSize: pickerConfig.gridThumbnailSize, previewThumbnailSize: pickerConfig.previewThumbnailSize, specialPickerType: pickerConfig.specialPickerType, - specialItemPosition: pickerConfig.specialItemPosition, - specialItemBuilder: pickerConfig.specialItemBuilder, + specialItems: pickerConfig.specialItems, loadingIndicatorBuilder: pickerConfig.loadingIndicatorBuilder, selectPredicate: pickerConfig.selectPredicate, shouldRevertGrid: pickerConfig.shouldRevertGrid, From 72960c0eb7662048d3b1259e9311eb4255f88f02 Mon Sep 17 00:00:00 2001 From: yujune Date: Thu, 10 Oct 2024 21:34:01 +0800 Subject: [PATCH 02/23] Add permission state in special item builder. --- example/lib/constants/picker_method.dart | 10 +++++----- lib/src/constants/typedefs.dart | 2 +- lib/src/delegates/asset_picker_builder_delegate.dart | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/example/lib/constants/picker_method.dart b/example/lib/constants/picker_method.dart index a9e77197..e990a4dd 100644 --- a/example/lib/constants/picker_method.dart +++ b/example/lib/constants/picker_method.dart @@ -142,7 +142,7 @@ class PickMethod { BuildContext context, AssetPathEntity? path, int length, - bool isPermissionLimited, + PermissionState permissionState, ) { if (path?.isAll != true) { return null; @@ -199,7 +199,7 @@ class PickMethod { BuildContext context, AssetPathEntity? path, int length, - bool isPermissionLimited, + PermissionState permissionState, ) { if (path?.isAll != true) { return null; @@ -314,7 +314,7 @@ class PickMethod { BuildContext context, AssetPathEntity? path, int length, - bool isPermissionLimited, + PermissionState permissionState, ) { return const Center( child: Text('Custom Widget', textAlign: TextAlign.center), @@ -349,7 +349,7 @@ class PickMethod { BuildContext context, AssetPathEntity? path, int length, - bool isPermissionLimited, + PermissionState permissionState, ) { return const Center( child: Text('Prepand Widget', textAlign: TextAlign.center), @@ -362,7 +362,7 @@ class PickMethod { BuildContext context, AssetPathEntity? path, int length, - bool isPermissionLimited, + PermissionState permissionState, ) { return const Center( child: Text('Append Widget', textAlign: TextAlign.center), diff --git a/lib/src/constants/typedefs.dart b/lib/src/constants/typedefs.dart index 8fc40470..129514fb 100644 --- a/lib/src/constants/typedefs.dart +++ b/lib/src/constants/typedefs.dart @@ -31,7 +31,7 @@ typedef SpecialItemBuilder = Widget? Function( BuildContext context, Path? path, int length, - bool isPermissionLimited, + PermissionState permissionState, ); /// {@template wechat_assets_picker.AssetSelectPredicate} diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index 982a8e39..6fc4d212 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -1242,7 +1242,7 @@ class DefaultAssetPickerBuilderDelegate context, wrapper?.path, totalCount, - isPermissionLimited, + permissionNotifier.value, ) ); }) From 3834423f914756ab86df44268d61dd11c515713f Mon Sep 17 00:00:00 2001 From: yujune Date: Wed, 16 Oct 2024 16:19:43 +0800 Subject: [PATCH 03/23] Add specialItem count instead of hardcoded 1. --- example/lib/customs/pickers/directory_file_asset_picker.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/lib/customs/pickers/directory_file_asset_picker.dart b/example/lib/customs/pickers/directory_file_asset_picker.dart index d04826bf..a59a77a2 100644 --- a/example/lib/customs/pickers/directory_file_asset_picker.dart +++ b/example/lib/customs/pickers/directory_file_asset_picker.dart @@ -585,7 +585,7 @@ class FileAssetPickerBuilder appBarPreferredSize ??= appBar(context).preferredSize; int totalCount = provider.currentAssets.length; if (specialItems.isNotEmpty) { - totalCount += 1; + totalCount += specialItems.length; } final int placeholderCount; if (isAppleOS(context) && totalCount % gridCount != 0) { From 5283b0c0785a0da7324ebaa37a2e9e63bacff13f Mon Sep 17 00:00:00 2001 From: Tee Yu June <56582497+yujune@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:20:34 +0800 Subject: [PATCH 04/23] Update example/lib/customs/pickers/directory_file_asset_picker.dart Co-authored-by: Alex Li --- example/lib/customs/pickers/directory_file_asset_picker.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/example/lib/customs/pickers/directory_file_asset_picker.dart b/example/lib/customs/pickers/directory_file_asset_picker.dart index a59a77a2..9312c333 100644 --- a/example/lib/customs/pickers/directory_file_asset_picker.dart +++ b/example/lib/customs/pickers/directory_file_asset_picker.dart @@ -701,10 +701,7 @@ class FileAssetPickerBuilder int index, List currentAssets, ) { - int currentIndex = index; - - currentIndex = index - prependSpecialItems.length; - + final currentIndex = index - prependSpecialItems.length; final File asset = currentAssets.elementAt(currentIndex); final Widget builder = imageAndVideoItemBuilder( context, From add13d3c0de0d2d2da839b213d5abd1d1f765b69 Mon Sep 17 00:00:00 2001 From: yujune Date: Wed, 16 Oct 2024 16:22:10 +0800 Subject: [PATCH 05/23] Move out SpecialItemModel from typedefs. --- lib/src/constants/typedefs.dart | 7 ------- lib/src/delegates/asset_picker_builder_delegate.dart | 5 +++++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/src/constants/typedefs.dart b/lib/src/constants/typedefs.dart index 129514fb..601b86e6 100644 --- a/lib/src/constants/typedefs.dart +++ b/lib/src/constants/typedefs.dart @@ -9,8 +9,6 @@ import 'package:flutter/widgets.dart'; import 'package:photo_manager/photo_manager.dart' show PermissionState; import 'package:provider/provider.dart'; -import './enums.dart'; - /// Mirroring [ChangeNotifierProvider]. typedef CNP = ChangeNotifierProvider; @@ -78,8 +76,3 @@ typedef AssetsChangeRefreshPredicate = bool Function( MethodCall call, Path? path, ); - -typedef SpecialItemModel = ({ - SpecialItemPosition position, - Widget item, -}); diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index 6fc4d212..b2f212c6 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -2476,3 +2476,8 @@ class DefaultAssetPickerBuilderDelegate ); } } + +typedef SpecialItemModel = ({ + SpecialItemPosition position, + Widget item, +}); From 352270c12168228777c6c7d0fb12008290c43f70 Mon Sep 17 00:00:00 2001 From: Tee Yu June <56582497+yujune@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:23:29 +0800 Subject: [PATCH 06/23] Update lib/src/delegates/asset_picker_builder_delegate.dart Co-authored-by: Alex Li --- lib/src/delegates/asset_picker_builder_delegate.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index b2f212c6..733a7659 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -1439,9 +1439,7 @@ class DefaultAssetPickerBuilderDelegate } } - int currentIndex = index; - - currentIndex = index - prepandSpecialItemModels.length; + final currentIndex = index - prepandSpecialItemModels.length; if (currentPathEntity == null) { return const SizedBox.shrink(); From a086f04da3e15b61a23b3053ef7bb3d3ca5a93c3 Mon Sep 17 00:00:00 2001 From: Tee Yu June <56582497+yujune@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:24:13 +0800 Subject: [PATCH 07/23] Update lib/src/delegates/asset_picker_builder_delegate.dart Co-authored-by: Alex Li --- .../delegates/asset_picker_builder_delegate.dart | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index 733a7659..5b057719 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -1418,12 +1418,16 @@ class DefaultAssetPickerBuilderDelegate final PathWrapper? currentWrapper = p.currentPath; final AssetPathEntity? currentPathEntity = currentWrapper?.path; - final prepandSpecialItemModels = specialItemModels.where( - (model) => model.position == SpecialItemPosition.prepend, - ); - final appendSpecialItemModels = specialItemModels.where( - (model) => model.position == SpecialItemPosition.append, - ); + final prependItems = []; + final appendItems = []; + for (final model in specialItemModels) { + switch (model.position) { + case SpecialItemPosition.prepend: + prependItems.add(model); + case SpecialItemPosition.append: + appendItems.add(model); + } + } if (specialItemModels.isNotEmpty) { if (prepandSpecialItemModels.isNotEmpty) { From 73aa6adf417373a8f1c1600393731ea7e21c0a97 Mon Sep 17 00:00:00 2001 From: Tee Yu June <56582497+yujune@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:24:31 +0800 Subject: [PATCH 08/23] Update lib/src/delegates/asset_picker_builder_delegate.dart Co-authored-by: Alex Li --- lib/src/delegates/asset_picker_builder_delegate.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index 5b057719..8277fc64 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -1606,10 +1606,6 @@ class DefaultAssetPickerBuilderDelegate return length; } - if (specialItemModels.isEmpty) { - return length; - } - return length + specialItemModels.length; } From d15ee2814cd403abe0420a8507be4f6ec7dfa8f6 Mon Sep 17 00:00:00 2001 From: yujune Date: Wed, 16 Oct 2024 16:28:00 +0800 Subject: [PATCH 09/23] Remove redundant codes. --- .../asset_picker_builder_delegate.dart | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index 8277fc64..a2a83f95 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -1429,21 +1429,19 @@ class DefaultAssetPickerBuilderDelegate } } - if (specialItemModels.isNotEmpty) { - if (prepandSpecialItemModels.isNotEmpty) { - if (index < prepandSpecialItemModels.length) { - return specialItemModels[index].item; - } + if (prependItems.isNotEmpty) { + if (index < prependItems.length) { + return specialItemModels[index].item; } + } - if (appendSpecialItemModels.isNotEmpty) { - if (index >= length + prepandSpecialItemModels.length) { - return specialItemModels[index - length].item; - } + if (appendItems.isNotEmpty) { + if (index >= length + prependItems.length) { + return specialItemModels[index - length].item; } } - final currentIndex = index - prepandSpecialItemModels.length; + final currentIndex = index - prependItems.length; if (currentPathEntity == null) { return const SizedBox.shrink(); From 560c455b52ebd636a1125912fc2d3b5edc453125 Mon Sep 17 00:00:00 2001 From: yujune Date: Wed, 16 Oct 2024 16:35:16 +0800 Subject: [PATCH 10/23] Remove incorrect -1 logic. --- lib/src/delegates/asset_picker_builder_delegate.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index a2a83f95..1f3b0793 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -1477,7 +1477,7 @@ class DefaultAssetPickerBuilderDelegate int semanticIndex(int index) { if (prependSpecialItems.isNotEmpty) { - return index - prependSpecialItems.length + 1; + return index - prependSpecialItems.length; } return index; From 281a9f27ac689a6aecd1ce8f2d858698a5a66ce4 Mon Sep 17 00:00:00 2001 From: yujune Date: Wed, 16 Oct 2024 16:38:42 +0800 Subject: [PATCH 11/23] Remove redundant checking. --- lib/src/delegates/asset_picker_builder_delegate.dart | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index 1f3b0793..5fa18644 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -1476,11 +1476,7 @@ class DefaultAssetPickerBuilderDelegate } int semanticIndex(int index) { - if (prependSpecialItems.isNotEmpty) { - return index - prependSpecialItems.length; - } - - return index; + return index - prependSpecialItems.length; } @override From d6cb626ec291295f43bb2983536b3ce8674aa35d Mon Sep 17 00:00:00 2001 From: yujune Date: Wed, 16 Oct 2024 20:48:10 +0800 Subject: [PATCH 12/23] Add appendSpecialItems. --- lib/src/delegates/asset_picker_builder_delegate.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index 5fa18644..bd980138 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -62,6 +62,11 @@ abstract class AssetPickerBuilderDelegate { .where( (item) => item.position == SpecialItemPosition.prepend, ) + .toList(), + appendSpecialItems = specialItems + .where( + (item) => item.position == SpecialItemPosition.append, + ) .toList() { Singleton.textDelegate = textDelegate ?? assetPickerTextDelegateFromLocale(locale); @@ -135,6 +140,10 @@ abstract class AssetPickerBuilderDelegate { /// 前置自定义item列表 final List> prependSpecialItems; + /// List of append special items. + /// 附加自定义item列表 + final List> appendSpecialItems; + /// [ThemeData] for the picker. /// 选择器使用的主题 ThemeData get theme => pickerTheme ?? AssetPicker.themeData(themeColor); From 5ea0cafa365138c0818555545e97462c1a8880a7 Mon Sep 17 00:00:00 2001 From: Tee Yu June <56582497+yujune@users.noreply.github.com> Date: Wed, 16 Oct 2024 20:49:35 +0800 Subject: [PATCH 13/23] Update lib/src/delegates/asset_picker_builder_delegate.dart Co-authored-by: Alex Li --- lib/src/delegates/asset_picker_builder_delegate.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index bd980138..aa610e37 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -1577,9 +1577,7 @@ class DefaultAssetPickerBuilderDelegate int placeholderCount = 0, }) { int index = assets.indexWhere((AssetEntity e) => e.id == id); - index += prependSpecialItems.length; - index += placeholderCount; return index; } From c8686a7c0e8e2952133d18535081762cff59fe39 Mon Sep 17 00:00:00 2001 From: yujune Date: Wed, 16 Oct 2024 20:51:25 +0800 Subject: [PATCH 14/23] Move _SpecialItemModel to top. --- .../asset_picker_builder_delegate.dart | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index aa610e37..a69151c8 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -28,6 +28,11 @@ import '../widget/asset_picker_app_bar.dart'; import '../widget/asset_picker_viewer.dart'; import '../widget/builder/asset_entity_grid_item_builder.dart'; +typedef _SpecialItemModel = ({ + SpecialItemPosition position, + Widget item, +}); + /// The delegate to build the whole picker's components. /// /// By extending the delegate, you can customize every components on you own. @@ -1243,7 +1248,7 @@ class DefaultAssetPickerBuilderDelegate // First, we need the count of the assets. int totalCount = wrapper?.assetCount ?? 0; - final List specialItemModels = specialItems + final List<_SpecialItemModel> specialItemModels = specialItems .map((item) { return ( position: item.position, @@ -1255,7 +1260,7 @@ class DefaultAssetPickerBuilderDelegate ) ); }) - .whereType() + .whereType<_SpecialItemModel>() .toList(); totalCount += specialItemModels.length; @@ -1419,7 +1424,7 @@ class DefaultAssetPickerBuilderDelegate BuildContext context, int index, List currentAssets, { - List specialItemModels = const [], + List<_SpecialItemModel> specialItemModels = const [], }) { final DefaultAssetPickerProvider p = context.read(); @@ -1427,8 +1432,8 @@ class DefaultAssetPickerBuilderDelegate final PathWrapper? currentWrapper = p.currentPath; final AssetPathEntity? currentPathEntity = currentWrapper?.path; - final prependItems = []; - final appendItems = []; + final prependItems = <_SpecialItemModel>[]; + final appendItems = <_SpecialItemModel>[]; for (final model in specialItemModels) { switch (model.position) { case SpecialItemPosition.prepend: @@ -1587,7 +1592,7 @@ class DefaultAssetPickerBuilderDelegate required BuildContext context, required List assets, int placeholderCount = 0, - List specialItemModels = const [], + List<_SpecialItemModel> specialItemModels = const [], }) { final PathWrapper? currentWrapper = context .select?>( @@ -2475,8 +2480,3 @@ class DefaultAssetPickerBuilderDelegate ); } } - -typedef SpecialItemModel = ({ - SpecialItemPosition position, - Widget item, -}); From fc43aab5e8d3fd64641657bdf30b0505893ea64d Mon Sep 17 00:00:00 2001 From: yujune Date: Thu, 17 Oct 2024 13:42:13 +0800 Subject: [PATCH 15/23] Fix wrong special item calculation and naming. --- .../pickers/directory_file_asset_picker.dart | 65 +++++++++- .../asset_picker_builder_delegate.dart | 122 ++++++++++-------- 2 files changed, 126 insertions(+), 61 deletions(-) diff --git a/example/lib/customs/pickers/directory_file_asset_picker.dart b/example/lib/customs/pickers/directory_file_asset_picker.dart index 9312c333..b6a82f3b 100644 --- a/example/lib/customs/pickers/directory_file_asset_picker.dart +++ b/example/lib/customs/pickers/directory_file_asset_picker.dart @@ -584,9 +584,28 @@ class FileAssetPickerBuilder Widget assetsGridBuilder(BuildContext context) { appBarPreferredSize ??= appBar(context).preferredSize; int totalCount = provider.currentAssets.length; - if (specialItems.isNotEmpty) { - totalCount += specialItems.length; - } + + final List specialItemResults = specialItems + .map((item) { + final specialItem = item.builder?.call( + context, + provider.currentPath?.path, + totalCount, + permissionNotifier.value, + ); + if (specialItem != null) { + return SpecialItemResult( + position: item.position, + item: specialItem, + ); + } + return null; + }) + .whereType() + .toList(); + + totalCount += specialItemResults.length; + final int placeholderCount; if (isAppleOS(context) && totalCount % gridCount != 0) { placeholderCount = gridCount - totalCount % gridCount; @@ -626,6 +645,11 @@ class FileAssetPickerBuilder id: key.value, assets: assets, placeholderCount: placeholderCount, + prependSpecialItemResults: specialItemResults + .where( + (item) => item.position == SpecialItemPosition.prepend, + ) + .toList(), ); } return null; @@ -699,9 +723,36 @@ class FileAssetPickerBuilder Widget assetGridItemBuilder( BuildContext context, int index, - List currentAssets, - ) { - final currentIndex = index - prependSpecialItems.length; + List currentAssets, { + List specialItemResults = const [], + }) { + final int length = currentAssets.length; + + final prependItems = []; + final appendItems = []; + for (final model in specialItemResults) { + switch (model.position) { + case SpecialItemPosition.prepend: + prependItems.add(model); + case SpecialItemPosition.append: + appendItems.add(model); + } + } + + if (prependItems.isNotEmpty) { + if (index < prependItems.length) { + return specialItemResults[index].item; + } + } + + if (appendItems.isNotEmpty) { + if (index >= length + prependItems.length) { + return specialItemResults[index - length].item; + } + } + + final currentIndex = index - prependItems.length; + final File asset = currentAssets.elementAt(currentIndex); final Widget builder = imageAndVideoItemBuilder( context, @@ -724,6 +775,7 @@ class FileAssetPickerBuilder int index, File asset, Widget child, + List prependSpecialItemResults, ) { return Semantics(child: child); } @@ -1161,6 +1213,7 @@ class FileAssetPickerBuilder int findChildIndexBuilder({ required String id, required List assets, + required List prependSpecialItemResults, int placeholderCount = 0, }) { return assets.indexWhere((File file) => file.path == id); diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index a69151c8..fe85bdb9 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -28,10 +28,17 @@ import '../widget/asset_picker_app_bar.dart'; import '../widget/asset_picker_viewer.dart'; import '../widget/builder/asset_entity_grid_item_builder.dart'; -typedef _SpecialItemModel = ({ - SpecialItemPosition position, - Widget item, -}); +/// Class which contains non-null special item widget and its position which derived from the [SpecialItem] +/// 包含非空自定义item,并指定其位置。 +class SpecialItemResult { + SpecialItemResult({ + required this.position, + required this.item, + }); + + SpecialItemPosition position; + Widget item; +} /// The delegate to build the whole picker's components. /// @@ -62,17 +69,7 @@ abstract class AssetPickerBuilderDelegate { ), themeColor = pickerTheme?.colorScheme.secondary ?? themeColor ?? - defaultThemeColorWeChat, - prependSpecialItems = specialItems - .where( - (item) => item.position == SpecialItemPosition.prepend, - ) - .toList(), - appendSpecialItems = specialItems - .where( - (item) => item.position == SpecialItemPosition.append, - ) - .toList() { + defaultThemeColorWeChat { Singleton.textDelegate = textDelegate ?? assetPickerTextDelegateFromLocale(locale); } @@ -141,14 +138,6 @@ abstract class AssetPickerBuilderDelegate { final AssetsChangeRefreshPredicate? assetsChangeRefreshPredicate; - /// List of prepend special items. - /// 前置自定义item列表 - final List> prependSpecialItems; - - /// List of append special items. - /// 附加自定义item列表 - final List> appendSpecialItems; - /// [ThemeData] for the picker. /// 选择器使用的主题 ThemeData get theme => pickerTheme ?? AssetPicker.themeData(themeColor); @@ -341,6 +330,7 @@ abstract class AssetPickerBuilderDelegate { int? findChildIndexBuilder({ required String id, required List assets, + required List prependSpecialItemResults, int placeholderCount = 0, }) => null; @@ -368,6 +358,7 @@ abstract class AssetPickerBuilderDelegate { int index, Asset asset, Widget child, + List prependSpecialItemResults, ); /// The item builder for audio type of asset. @@ -1248,24 +1239,28 @@ class DefaultAssetPickerBuilderDelegate // First, we need the count of the assets. int totalCount = wrapper?.assetCount ?? 0; - final List<_SpecialItemModel> specialItemModels = specialItems + final List specialItemResults = specialItems .map((item) { - return ( - position: item.position, - item: item.builder?.call( - context, - wrapper?.path, - totalCount, - permissionNotifier.value, - ) + final specialItem = item.builder?.call( + context, + wrapper?.path, + totalCount, + permissionNotifier.value, ); + if (specialItem != null) { + return SpecialItemResult( + position: item.position, + item: specialItem, + ); + } + return null; }) - .whereType<_SpecialItemModel>() + .whereType() .toList(); - totalCount += specialItemModels.length; + totalCount += specialItemResults.length; - if (totalCount == 0 && specialItemModels.isEmpty) { + if (totalCount == 0 && specialItemResults.isEmpty) { return loadingIndicator(context); } // Then we use the [totalCount] to calculate placeholders we need. @@ -1305,7 +1300,7 @@ class DefaultAssetPickerBuilderDelegate context, index, assets, - specialItemModels: specialItemModels, + specialItemResults: specialItemResults, ), ), ); @@ -1314,7 +1309,7 @@ class DefaultAssetPickerBuilderDelegate context: context, assets: assets, placeholderCount: placeholderCount, - specialItemModels: specialItemModels, + specialItemResults: specialItemResults, ), findChildIndexCallback: (Key? key) { if (key is ValueKey) { @@ -1322,6 +1317,12 @@ class DefaultAssetPickerBuilderDelegate id: key.value, assets: assets, placeholderCount: placeholderCount, + prependSpecialItemResults: specialItemResults + .where( + (item) => + item.position == SpecialItemPosition.prepend, + ) + .toList(), ); } return null; @@ -1424,7 +1425,7 @@ class DefaultAssetPickerBuilderDelegate BuildContext context, int index, List currentAssets, { - List<_SpecialItemModel> specialItemModels = const [], + List specialItemResults = const [], }) { final DefaultAssetPickerProvider p = context.read(); @@ -1432,9 +1433,9 @@ class DefaultAssetPickerBuilderDelegate final PathWrapper? currentWrapper = p.currentPath; final AssetPathEntity? currentPathEntity = currentWrapper?.path; - final prependItems = <_SpecialItemModel>[]; - final appendItems = <_SpecialItemModel>[]; - for (final model in specialItemModels) { + final prependItems = []; + final appendItems = []; + for (final model in specialItemResults) { switch (model.position) { case SpecialItemPosition.prepend: prependItems.add(model); @@ -1445,13 +1446,13 @@ class DefaultAssetPickerBuilderDelegate if (prependItems.isNotEmpty) { if (index < prependItems.length) { - return specialItemModels[index].item; + return specialItemResults[index].item; } } if (appendItems.isNotEmpty) { if (index >= length + prependItems.length) { - return specialItemModels[index - length].item; + return specialItemResults[index - length].item; } } @@ -1486,11 +1487,20 @@ class DefaultAssetPickerBuilderDelegate itemBannedIndicator(context, asset), ], ); - return assetGridItemSemanticsBuilder(context, index, asset, content); + return assetGridItemSemanticsBuilder( + context, + index, + asset, + content, + prependItems, + ); } - int semanticIndex(int index) { - return index - prependSpecialItems.length; + int semanticIndex( + int index, + List prependSpecialItemResults, + ) { + return index - prependSpecialItemResults.length; } @override @@ -1499,6 +1509,7 @@ class DefaultAssetPickerBuilderDelegate int index, AssetEntity asset, Widget child, + List prependSpecialItemResults, ) { return ValueListenableBuilder( valueListenable: isSwitchingPath, @@ -1532,7 +1543,7 @@ class DefaultAssetPickerBuilderDelegate excludeSemantics: true, focusable: !isSwitchingPath, label: '${semanticsTextDelegate.semanticTypeLabel(asset.type)}' - '${semanticIndex(index)}, ' + '${semanticIndex(index, prependSpecialItemResults)}, ' '${asset.createDateTime.toString().replaceAll('.000', '')}', hidden: isSwitchingPath, hint: hint, @@ -1550,7 +1561,7 @@ class DefaultAssetPickerBuilderDelegate onLongPressHint: semanticsTextDelegate.sActionPreviewHint, selected: isSelected, sortKey: OrdinalSortKey( - semanticIndex(index).toDouble(), + semanticIndex(index, prependSpecialItemResults).toDouble(), name: 'GridItem', ), value: selectedIndex > 0 ? '$selectedIndex' : null, @@ -1563,7 +1574,7 @@ class DefaultAssetPickerBuilderDelegate } : null, child: IndexedSemantics( - index: semanticIndex(index), + index: semanticIndex(index, prependSpecialItemResults), child: child, ), ), @@ -1579,10 +1590,11 @@ class DefaultAssetPickerBuilderDelegate int findChildIndexBuilder({ required String id, required List assets, + required List prependSpecialItemResults, int placeholderCount = 0, }) { int index = assets.indexWhere((AssetEntity e) => e.id == id); - index += prependSpecialItems.length; + index += prependSpecialItemResults.length; index += placeholderCount; return index; } @@ -1592,7 +1604,7 @@ class DefaultAssetPickerBuilderDelegate required BuildContext context, required List assets, int placeholderCount = 0, - List<_SpecialItemModel> specialItemModels = const [], + List specialItemResults = const [], }) { final PathWrapper? currentWrapper = context .select?>( @@ -1602,17 +1614,17 @@ class DefaultAssetPickerBuilderDelegate final int length = assets.length + placeholderCount; // Return 1 if the [specialItem] build something. - if (currentPathEntity == null && specialItemModels.isNotEmpty) { - return placeholderCount + specialItemModels.length; + if (currentPathEntity == null && specialItemResults.isNotEmpty) { + return placeholderCount + specialItemResults.length; } // Return actual length if the current path is all. // 如果当前目录是全部内容,则返回实际的内容数量。 - if (currentPathEntity?.isAll != true && specialItemModels.isEmpty) { + if (currentPathEntity?.isAll != true && specialItemResults.isEmpty) { return length; } - return length + specialItemModels.length; + return length + specialItemResults.length; } @override From 78bd4dbcdc8025084d50259b8b33b4a12850b4b5 Mon Sep 17 00:00:00 2001 From: yujune Date: Thu, 17 Oct 2024 13:56:35 +0800 Subject: [PATCH 16/23] Add null builder in example. --- example/lib/constants/picker_method.dart | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/example/lib/constants/picker_method.dart b/example/lib/constants/picker_method.dart index e990a4dd..97b84f6c 100644 --- a/example/lib/constants/picker_method.dart +++ b/example/lib/constants/picker_method.dart @@ -369,6 +369,29 @@ class PickMethod { ); }, ), + //builder which return null will not be shown. + SpecialItem( + position: SpecialItemPosition.append, + builder: ( + BuildContext context, + AssetPathEntity? path, + int length, + PermissionState permissionState, + ) { + return null; + }, + ), + SpecialItem( + position: SpecialItemPosition.prepend, + builder: ( + BuildContext context, + AssetPathEntity? path, + int length, + PermissionState permissionState, + ) { + return null; + }, + ), ], ), ); From 38cc19ff783d7413134e9c14232379b80d56b282 Mon Sep 17 00:00:00 2001 From: yujune Date: Sun, 20 Oct 2024 21:09:27 +0800 Subject: [PATCH 17/23] Rename SpecialItemResult ot SpecialItemModel. --- .../pickers/directory_file_asset_picker.dart | 26 ++++----- .../asset_picker_builder_delegate.dart | 56 +++++++++---------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/example/lib/customs/pickers/directory_file_asset_picker.dart b/example/lib/customs/pickers/directory_file_asset_picker.dart index b6a82f3b..ef487b64 100644 --- a/example/lib/customs/pickers/directory_file_asset_picker.dart +++ b/example/lib/customs/pickers/directory_file_asset_picker.dart @@ -585,7 +585,7 @@ class FileAssetPickerBuilder appBarPreferredSize ??= appBar(context).preferredSize; int totalCount = provider.currentAssets.length; - final List specialItemResults = specialItems + final List specialItemModels = specialItems .map((item) { final specialItem = item.builder?.call( context, @@ -594,17 +594,17 @@ class FileAssetPickerBuilder permissionNotifier.value, ); if (specialItem != null) { - return SpecialItemResult( + return SpecialItemModel( position: item.position, item: specialItem, ); } return null; }) - .whereType() + .whereType() .toList(); - totalCount += specialItemResults.length; + totalCount += specialItemModels.length; final int placeholderCount; if (isAppleOS(context) && totalCount % gridCount != 0) { @@ -645,7 +645,7 @@ class FileAssetPickerBuilder id: key.value, assets: assets, placeholderCount: placeholderCount, - prependSpecialItemResults: specialItemResults + prependSpecialItemResults: specialItemModels .where( (item) => item.position == SpecialItemPosition.prepend, ) @@ -724,13 +724,13 @@ class FileAssetPickerBuilder BuildContext context, int index, List currentAssets, { - List specialItemResults = const [], + List specialItemModels = const [], }) { final int length = currentAssets.length; - final prependItems = []; - final appendItems = []; - for (final model in specialItemResults) { + final prependItems = []; + final appendItems = []; + for (final model in specialItemModels) { switch (model.position) { case SpecialItemPosition.prepend: prependItems.add(model); @@ -741,13 +741,13 @@ class FileAssetPickerBuilder if (prependItems.isNotEmpty) { if (index < prependItems.length) { - return specialItemResults[index].item; + return specialItemModels[index].item; } } if (appendItems.isNotEmpty) { if (index >= length + prependItems.length) { - return specialItemResults[index - length].item; + return specialItemModels[index - length].item; } } @@ -775,7 +775,7 @@ class FileAssetPickerBuilder int index, File asset, Widget child, - List prependSpecialItemResults, + List prependSpecialItemResults, ) { return Semantics(child: child); } @@ -1213,7 +1213,7 @@ class FileAssetPickerBuilder int findChildIndexBuilder({ required String id, required List assets, - required List prependSpecialItemResults, + required List prependSpecialItemResults, int placeholderCount = 0, }) { return assets.indexWhere((File file) => file.path == id); diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index fe85bdb9..c1bcec3c 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -30,14 +30,14 @@ import '../widget/builder/asset_entity_grid_item_builder.dart'; /// Class which contains non-null special item widget and its position which derived from the [SpecialItem] /// 包含非空自定义item,并指定其位置。 -class SpecialItemResult { - SpecialItemResult({ +class SpecialItemModel { + const SpecialItemModel({ required this.position, required this.item, }); - SpecialItemPosition position; - Widget item; + final SpecialItemPosition position; + final Widget item; } /// The delegate to build the whole picker's components. @@ -330,7 +330,7 @@ abstract class AssetPickerBuilderDelegate { int? findChildIndexBuilder({ required String id, required List assets, - required List prependSpecialItemResults, + required List prependSpecialItemResults, int placeholderCount = 0, }) => null; @@ -358,7 +358,7 @@ abstract class AssetPickerBuilderDelegate { int index, Asset asset, Widget child, - List prependSpecialItemResults, + List prependSpecialItemResults, ); /// The item builder for audio type of asset. @@ -1239,7 +1239,7 @@ class DefaultAssetPickerBuilderDelegate // First, we need the count of the assets. int totalCount = wrapper?.assetCount ?? 0; - final List specialItemResults = specialItems + final List specialItemModels = specialItems .map((item) { final specialItem = item.builder?.call( context, @@ -1248,19 +1248,19 @@ class DefaultAssetPickerBuilderDelegate permissionNotifier.value, ); if (specialItem != null) { - return SpecialItemResult( + return SpecialItemModel( position: item.position, item: specialItem, ); } return null; }) - .whereType() + .whereType() .toList(); - totalCount += specialItemResults.length; + totalCount += specialItemModels.length; - if (totalCount == 0 && specialItemResults.isEmpty) { + if (totalCount == 0 && specialItemModels.isEmpty) { return loadingIndicator(context); } // Then we use the [totalCount] to calculate placeholders we need. @@ -1300,7 +1300,7 @@ class DefaultAssetPickerBuilderDelegate context, index, assets, - specialItemResults: specialItemResults, + specialItemModels: specialItemModels, ), ), ); @@ -1309,7 +1309,7 @@ class DefaultAssetPickerBuilderDelegate context: context, assets: assets, placeholderCount: placeholderCount, - specialItemResults: specialItemResults, + specialItemModels: specialItemModels, ), findChildIndexCallback: (Key? key) { if (key is ValueKey) { @@ -1317,7 +1317,7 @@ class DefaultAssetPickerBuilderDelegate id: key.value, assets: assets, placeholderCount: placeholderCount, - prependSpecialItemResults: specialItemResults + prependSpecialItemResults: specialItemModels .where( (item) => item.position == SpecialItemPosition.prepend, @@ -1425,7 +1425,7 @@ class DefaultAssetPickerBuilderDelegate BuildContext context, int index, List currentAssets, { - List specialItemResults = const [], + List specialItemModels = const [], }) { final DefaultAssetPickerProvider p = context.read(); @@ -1433,9 +1433,9 @@ class DefaultAssetPickerBuilderDelegate final PathWrapper? currentWrapper = p.currentPath; final AssetPathEntity? currentPathEntity = currentWrapper?.path; - final prependItems = []; - final appendItems = []; - for (final model in specialItemResults) { + final prependItems = []; + final appendItems = []; + for (final model in specialItemModels) { switch (model.position) { case SpecialItemPosition.prepend: prependItems.add(model); @@ -1446,13 +1446,13 @@ class DefaultAssetPickerBuilderDelegate if (prependItems.isNotEmpty) { if (index < prependItems.length) { - return specialItemResults[index].item; + return specialItemModels[index].item; } } if (appendItems.isNotEmpty) { if (index >= length + prependItems.length) { - return specialItemResults[index - length].item; + return specialItemModels[index - length].item; } } @@ -1498,7 +1498,7 @@ class DefaultAssetPickerBuilderDelegate int semanticIndex( int index, - List prependSpecialItemResults, + List prependSpecialItemResults, ) { return index - prependSpecialItemResults.length; } @@ -1509,7 +1509,7 @@ class DefaultAssetPickerBuilderDelegate int index, AssetEntity asset, Widget child, - List prependSpecialItemResults, + List prependSpecialItemResults, ) { return ValueListenableBuilder( valueListenable: isSwitchingPath, @@ -1590,7 +1590,7 @@ class DefaultAssetPickerBuilderDelegate int findChildIndexBuilder({ required String id, required List assets, - required List prependSpecialItemResults, + required List prependSpecialItemResults, int placeholderCount = 0, }) { int index = assets.indexWhere((AssetEntity e) => e.id == id); @@ -1604,7 +1604,7 @@ class DefaultAssetPickerBuilderDelegate required BuildContext context, required List assets, int placeholderCount = 0, - List specialItemResults = const [], + List specialItemModels = const [], }) { final PathWrapper? currentWrapper = context .select?>( @@ -1614,17 +1614,17 @@ class DefaultAssetPickerBuilderDelegate final int length = assets.length + placeholderCount; // Return 1 if the [specialItem] build something. - if (currentPathEntity == null && specialItemResults.isNotEmpty) { - return placeholderCount + specialItemResults.length; + if (currentPathEntity == null && specialItemModels.isNotEmpty) { + return placeholderCount + specialItemModels.length; } // Return actual length if the current path is all. // 如果当前目录是全部内容,则返回实际的内容数量。 - if (currentPathEntity?.isAll != true && specialItemResults.isEmpty) { + if (currentPathEntity?.isAll != true && specialItemModels.isEmpty) { return length; } - return length + specialItemResults.length; + return length + specialItemModels.length; } @override From f31055098141ccc060e790bdc2aef3ce23cac768 Mon Sep 17 00:00:00 2001 From: yujune Date: Sun, 20 Oct 2024 21:19:04 +0800 Subject: [PATCH 18/23] Use specialItemModels as args. --- .../pickers/directory_file_asset_picker.dart | 6 ++-- .../asset_picker_builder_delegate.dart | 35 ++++++++++--------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/example/lib/customs/pickers/directory_file_asset_picker.dart b/example/lib/customs/pickers/directory_file_asset_picker.dart index ef487b64..0fc2d2ef 100644 --- a/example/lib/customs/pickers/directory_file_asset_picker.dart +++ b/example/lib/customs/pickers/directory_file_asset_picker.dart @@ -645,7 +645,7 @@ class FileAssetPickerBuilder id: key.value, assets: assets, placeholderCount: placeholderCount, - prependSpecialItemResults: specialItemModels + specialItemModels: specialItemModels .where( (item) => item.position == SpecialItemPosition.prepend, ) @@ -775,7 +775,7 @@ class FileAssetPickerBuilder int index, File asset, Widget child, - List prependSpecialItemResults, + List specialItemModels, ) { return Semantics(child: child); } @@ -1213,7 +1213,7 @@ class FileAssetPickerBuilder int findChildIndexBuilder({ required String id, required List assets, - required List prependSpecialItemResults, + required List specialItemModels, int placeholderCount = 0, }) { return assets.indexWhere((File file) => file.path == id); diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index c1bcec3c..41333b7a 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -330,7 +330,7 @@ abstract class AssetPickerBuilderDelegate { int? findChildIndexBuilder({ required String id, required List assets, - required List prependSpecialItemResults, + required List specialItemModels, int placeholderCount = 0, }) => null; @@ -358,7 +358,7 @@ abstract class AssetPickerBuilderDelegate { int index, Asset asset, Widget child, - List prependSpecialItemResults, + List specialItemModels, ); /// The item builder for audio type of asset. @@ -1317,12 +1317,7 @@ class DefaultAssetPickerBuilderDelegate id: key.value, assets: assets, placeholderCount: placeholderCount, - prependSpecialItemResults: specialItemModels - .where( - (item) => - item.position == SpecialItemPosition.prepend, - ) - .toList(), + specialItemModels: specialItemModels, ); } return null; @@ -1492,15 +1487,18 @@ class DefaultAssetPickerBuilderDelegate index, asset, content, - prependItems, + specialItemModels, ); } int semanticIndex( int index, - List prependSpecialItemResults, + List specialItemModels, ) { - return index - prependSpecialItemResults.length; + final prependSpecialItemModels = specialItemModels.where( + (SpecialItemModel model) => model.position == SpecialItemPosition.prepend, + ); + return index - prependSpecialItemModels.length; } @override @@ -1509,7 +1507,7 @@ class DefaultAssetPickerBuilderDelegate int index, AssetEntity asset, Widget child, - List prependSpecialItemResults, + List specialItemModels, ) { return ValueListenableBuilder( valueListenable: isSwitchingPath, @@ -1543,7 +1541,7 @@ class DefaultAssetPickerBuilderDelegate excludeSemantics: true, focusable: !isSwitchingPath, label: '${semanticsTextDelegate.semanticTypeLabel(asset.type)}' - '${semanticIndex(index, prependSpecialItemResults)}, ' + '${semanticIndex(index, specialItemModels)}, ' '${asset.createDateTime.toString().replaceAll('.000', '')}', hidden: isSwitchingPath, hint: hint, @@ -1561,7 +1559,7 @@ class DefaultAssetPickerBuilderDelegate onLongPressHint: semanticsTextDelegate.sActionPreviewHint, selected: isSelected, sortKey: OrdinalSortKey( - semanticIndex(index, prependSpecialItemResults).toDouble(), + semanticIndex(index, specialItemModels).toDouble(), name: 'GridItem', ), value: selectedIndex > 0 ? '$selectedIndex' : null, @@ -1574,7 +1572,7 @@ class DefaultAssetPickerBuilderDelegate } : null, child: IndexedSemantics( - index: semanticIndex(index, prependSpecialItemResults), + index: semanticIndex(index, specialItemModels), child: child, ), ), @@ -1590,11 +1588,14 @@ class DefaultAssetPickerBuilderDelegate int findChildIndexBuilder({ required String id, required List assets, - required List prependSpecialItemResults, + required List specialItemModels, int placeholderCount = 0, }) { + final prependSpecialItemModels = specialItemModels.where( + (SpecialItemModel model) => model.position == SpecialItemPosition.prepend, + ); int index = assets.indexWhere((AssetEntity e) => e.id == id); - index += prependSpecialItemResults.length; + index += prependSpecialItemModels.length; index += placeholderCount; return index; } From 2ae888cc09aac5042a48050ccdc1de194961c650 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Sat, 16 Aug 2025 22:17:43 +0800 Subject: [PATCH 19/23] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Reorg=20special=20it?= =?UTF-8?q?em=20builders=20and=20counters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/lib/constants/picker_method.dart | 7 -- .../pickers/directory_file_asset_picker.dart | 1 - .../customs/pickers/insta_asset_picker.dart | 10 +- .../pickers/multi_tabs_assets_picker.dart | 11 +- lib/src/constants/typedefs.dart | 1 - ...asset_grid_drag_selection_coordinator.dart | 7 ++ .../asset_picker_builder_delegate.dart | 113 +++++++++--------- 7 files changed, 80 insertions(+), 70 deletions(-) diff --git a/example/lib/constants/picker_method.dart b/example/lib/constants/picker_method.dart index dee833ff..63efbae4 100644 --- a/example/lib/constants/picker_method.dart +++ b/example/lib/constants/picker_method.dart @@ -141,7 +141,6 @@ class PickMethod { builder: ( BuildContext context, AssetPathEntity? path, - int length, PermissionState permissionState, ) { if (path?.isAll != true) { @@ -198,7 +197,6 @@ class PickMethod { builder: ( BuildContext context, AssetPathEntity? path, - int length, PermissionState permissionState, ) { if (path?.isAll != true) { @@ -312,7 +310,6 @@ class PickMethod { builder: ( BuildContext context, AssetPathEntity? path, - int length, PermissionState permissionState, ) { return const Center( @@ -347,7 +344,6 @@ class PickMethod { builder: ( BuildContext context, AssetPathEntity? path, - int length, PermissionState permissionState, ) { return const Center( @@ -360,7 +356,6 @@ class PickMethod { builder: ( BuildContext context, AssetPathEntity? path, - int length, PermissionState permissionState, ) { return const Center( @@ -374,7 +369,6 @@ class PickMethod { builder: ( BuildContext context, AssetPathEntity? path, - int length, PermissionState permissionState, ) { return null; @@ -385,7 +379,6 @@ class PickMethod { builder: ( BuildContext context, AssetPathEntity? path, - int length, PermissionState permissionState, ) { return null; diff --git a/example/lib/customs/pickers/directory_file_asset_picker.dart b/example/lib/customs/pickers/directory_file_asset_picker.dart index 3205d780..7a1f4056 100644 --- a/example/lib/customs/pickers/directory_file_asset_picker.dart +++ b/example/lib/customs/pickers/directory_file_asset_picker.dart @@ -530,7 +530,6 @@ final class FileAssetPickerBuilder final specialItem = item.builder?.call( context, provider.currentPath?.path, - totalCount, permissionNotifier.value, ); if (specialItem != null) { diff --git a/example/lib/customs/pickers/insta_asset_picker.dart b/example/lib/customs/pickers/insta_asset_picker.dart index d2390eb2..6571f99e 100644 --- a/example/lib/customs/pickers/insta_asset_picker.dart +++ b/example/lib/customs/pickers/insta_asset_picker.dart @@ -676,8 +676,14 @@ final class InstaAssetPickerBuilder extends DefaultAssetPickerBuilderDelegate { appBarPreferredSize ??= appBar(context).preferredSize; return Consumer( builder: (context, p, __) { - final bool shouldDisplayAssets = - p.hasAssetsToDisplay || shouldBuildSpecialItem; + final hasAssetsToDisplay = p.hasAssetsToDisplay; + final shouldBuildSpecialItems = assetsGridSpecialItemModels( + context: context, + path: p.currentPath?.path, + ).isNotEmpty; + final shouldDisplayAssets = + hasAssetsToDisplay || shouldBuildSpecialItems; + _initializePreviewAsset(p, shouldDisplayAssets); return AnimatedSwitcher( diff --git a/example/lib/customs/pickers/multi_tabs_assets_picker.dart b/example/lib/customs/pickers/multi_tabs_assets_picker.dart index 3007b539..89176dda 100644 --- a/example/lib/customs/pickers/multi_tabs_assets_picker.dart +++ b/example/lib/customs/pickers/multi_tabs_assets_picker.dart @@ -523,9 +523,14 @@ final class MultiTabAssetPickerBuilder Widget _buildGrid(BuildContext context) { return Consumer( - builder: (BuildContext context, DefaultAssetPickerProvider p, __) { - final bool shouldDisplayAssets = - p.hasAssetsToDisplay || shouldBuildSpecialItem; + builder: (context, p, __) { + final hasAssetsToDisplay = p.hasAssetsToDisplay; + final shouldBuildSpecialItems = assetsGridSpecialItemModels( + context: context, + path: p.currentPath?.path, + ).isNotEmpty; + final shouldDisplayAssets = + hasAssetsToDisplay || shouldBuildSpecialItems; return AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: shouldDisplayAssets diff --git a/lib/src/constants/typedefs.dart b/lib/src/constants/typedefs.dart index f6a02fbd..5e0ff68e 100644 --- a/lib/src/constants/typedefs.dart +++ b/lib/src/constants/typedefs.dart @@ -28,7 +28,6 @@ typedef LoadingIndicatorBuilder = Widget Function( typedef SpecialItemBuilder = Widget? Function( BuildContext context, Path? path, - int length, PermissionState permissionState, ); diff --git a/lib/src/delegates/asset_grid_drag_selection_coordinator.dart b/lib/src/delegates/asset_grid_drag_selection_coordinator.dart index b04f3503..74fdf89b 100644 --- a/lib/src/delegates/asset_grid_drag_selection_coordinator.dart +++ b/lib/src/delegates/asset_grid_drag_selection_coordinator.dart @@ -123,10 +123,16 @@ class AssetGridDragSelectionCoordinator { totalRows * (itemSize + delegate.itemSpacing) <= gridViewport; final reverted = gridRevert && !onlyOneScreen; + final specialItems = delegate.assetsGridSpecialItemModels( + context: context, + path: provider.currentPath?.path, + ); + final double anchor = delegate.assetGridAnchor( context: context, constraints: constraints, pathWrapper: provider.currentPath, + specialItemModels: specialItems, ); final scrolledOffset = delegate.gridScrollController.offset .abs(); // Offset is negative when reverted. @@ -157,6 +163,7 @@ class AssetGridDragSelectionCoordinator { context: context, pathWrapper: provider.currentPath, onlyOneScreen: onlyOneScreen, + specialItemModels: specialItems, ); // Make the index starts with the bottom if the grid is reverted. if (reverted && placeholderCount > 0 && rowIndex > 0 && anchor < 1.0) { diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index 5e8bee37..87bf2cd6 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -184,10 +184,6 @@ abstract class AssetPickerBuilderDelegate { /// 选择器是否为单选模式 bool get isSingleAssetMode; - /// Whether the delegate should build the special item. - /// 是否需要构建自定义 item - bool get shouldBuildSpecialItem => specialItems.isNotEmpty; - /// Space between assets item widget. /// 资源部件之间的间隔 double get itemSpacing => 2; @@ -355,28 +351,45 @@ abstract class AssetPickerBuilderDelegate { int placeholderCount = 0, }); + List assetsGridSpecialItemModels({ + required BuildContext context, + required Path? path, + }) { + return specialItems + .map((item) { + final specialItem = item.builder?.call( + context, + path, + permissionNotifier.value, + ); + if (specialItem != null) { + return SpecialItemModel( + position: item.position, + item: specialItem, + ); + } + return null; + }) + .nonNulls + .toList(); + } + /// Calculates the placeholder count in the assets grid. int assetsGridItemPlaceholderCount({ required BuildContext context, required PathWrapper? pathWrapper, required bool onlyOneScreen, + required List specialItemModels, }) { if (onlyOneScreen) { return 0; } + final bool gridRevert = effectiveShouldRevertGrid(context); int totalCount = pathWrapper?.assetCount ?? 0; - // If user chose a special item's position, add 1 count. - if (specialItemPosition != SpecialItemPosition.none) { - final specialItem = specialItemBuilder?.call( - context, - pathWrapper?.path, - totalCount, - ); - if (specialItem != null) { - totalCount += 1; - } - } + // Add special items' count. + totalCount += specialItemModels.length; + final int result; if (gridRevert && totalCount % gridCount != 0) { // When there are left items that not filled into one row, @@ -386,6 +399,7 @@ abstract class AssetPickerBuilderDelegate { // Otherwise, we don't need placeholders. result = 0; } + return result; } @@ -394,19 +408,12 @@ abstract class AssetPickerBuilderDelegate { required BuildContext context, required BoxConstraints constraints, required PathWrapper? pathWrapper, + required List specialItemModels, }) { int totalCount = pathWrapper?.assetCount ?? 0; - // If user chose a special item's position, add 1 count. - if (specialItemPosition != SpecialItemPosition.none) { - final specialItem = specialItemBuilder?.call( - context, - pathWrapper?.path, - totalCount, - ); - if (specialItem != null) { - totalCount += 1; - } - } + // Add special items' count. + totalCount += specialItemModels.length; + // Here we got a magic calculation. [itemSpacing] needs to be divided by // [gridCount] since every grid item is squeezed by the [itemSpacing], // and it's actual size is reduced with [itemSpacing / gridCount]. @@ -1234,9 +1241,14 @@ class DefaultAssetPickerBuilderDelegate return AssetPickerAppBarWrapper( appBar: appBar(context), body: Consumer( - builder: (BuildContext context, T p, _) { - final bool shouldDisplayAssets = - p.hasAssetsToDisplay || shouldBuildSpecialItem; + builder: (context, p, _) { + final hasAssetsToDisplay = p.hasAssetsToDisplay; + final shouldBuildSpecialItems = assetsGridSpecialItemModels( + context: context, + path: p.currentPath?.path, + ).isNotEmpty; + final shouldDisplayAssets = + hasAssetsToDisplay || shouldBuildSpecialItems; return AnimatedSwitcher( duration: switchingPathDuration, child: shouldDisplayAssets @@ -1285,10 +1297,15 @@ class DefaultAssetPickerBuilderDelegate children: [ Positioned.fill( child: Consumer( - builder: (_, p, __) { + builder: (context, p, _) { + final hasAssetsToDisplay = p.hasAssetsToDisplay; + final shouldBuildSpecialItems = assetsGridSpecialItemModels( + context: context, + path: p.currentPath?.path, + ).isNotEmpty; + final shouldDisplayAssets = + hasAssetsToDisplay || shouldBuildSpecialItems; final Widget child; - final bool shouldDisplayAssets = - p.hasAssetsToDisplay || shouldBuildSpecialItem; if (shouldDisplayAssets) { child = Stack( children: [ @@ -1351,26 +1368,10 @@ class DefaultAssetPickerBuilderDelegate builder: (context, wrapper, _) { // First, we need the count of the assets. int totalCount = wrapper?.assetCount ?? 0; - - final List specialItemModels = specialItems - .map((item) { - final specialItem = item.builder?.call( - context, - wrapper?.path, - totalCount, - permissionNotifier.value, - ); - if (specialItem != null) { - return SpecialItemModel( - position: item.position, - item: specialItem, - ); - } - return null; - }) - .whereType() - .toList(); - + final specialItemModels = assetsGridSpecialItemModels( + context: context, + path: wrapper?.path, + ); totalCount += specialItemModels.length; if (totalCount == 0 && specialItemModels.isEmpty) { @@ -1392,6 +1393,7 @@ class DefaultAssetPickerBuilderDelegate context: context, pathWrapper: wrapper, onlyOneScreen: onlyOneScreen, + specialItemModels: specialItemModels, ); return SliverGrid( delegate: SliverChildBuilderDelegate( @@ -1539,6 +1541,7 @@ class DefaultAssetPickerBuilderDelegate context: context, constraints: constraints, pathWrapper: wrapper, + specialItemModels: specialItemModels, ); final reverted = gridRevert && !onlyOneScreen; @@ -1709,7 +1712,7 @@ class DefaultAssetPickerBuilderDelegate final int selectedIndex = p.selectedAssets.indexOf(asset) + 1; final labels = [ '${semanticsTextDelegate.semanticTypeLabel(asset.type)}' - '${semanticIndex(index)}', + '${semanticIndex(index, specialItemModels)}', asset.createDateTime.toString().replaceAll('.000', ''), if (asset.type == AssetType.audio || asset.type == AssetType.video) @@ -1723,9 +1726,7 @@ class DefaultAssetPickerBuilderDelegate enabled: !isBanned, excludeSemantics: true, focusable: !isSwitchingPath, - label: '${semanticsTextDelegate.semanticTypeLabel(asset.type)}' - '${semanticIndex(index, specialItemModels)}, ' - '${asset.createDateTime.toString().replaceAll('.000', '')}', + label: labels.join(', '), hidden: isSwitchingPath, image: asset.type == AssetType.image || asset.type == AssetType.video, From 687457f9600985b109a80ba5066372494e355aab Mon Sep 17 00:00:00 2001 From: Alex Li Date: Sat, 16 Aug 2025 22:32:13 +0800 Subject: [PATCH 20/23] =?UTF-8?q?=F0=9F=90=9B=20Fix=20counts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pickers/directory_file_asset_picker.dart | 25 ++++++---- .../asset_picker_builder_delegate.dart | 50 +++++++------------ 2 files changed, 31 insertions(+), 44 deletions(-) diff --git a/example/lib/customs/pickers/directory_file_asset_picker.dart b/example/lib/customs/pickers/directory_file_asset_picker.dart index 7a1f4056..d704b948 100644 --- a/example/lib/customs/pickers/directory_file_asset_picker.dart +++ b/example/lib/customs/pickers/directory_file_asset_picker.dart @@ -569,13 +569,19 @@ final class FileAssetPickerBuilder } return Directionality( textDirection: Directionality.of(context), - child: assetGridItemBuilder(c, index, assets), + child: assetGridItemBuilder( + context: c, + index: index, + currentAssets: assets, + specialItemModels: specialItemModels, + ), ); }, ), childCount: assetsGridItemCount( context: ctx, assets: assets, + specialItemModels: specialItemModels, placeholderCount: placeholderCount, ), findChildIndexCallback: (Key? key) { @@ -584,11 +590,7 @@ final class FileAssetPickerBuilder id: key.value, assets: assets, placeholderCount: placeholderCount, - specialItemModels: specialItemModels - .where( - (item) => item.position == SpecialItemPosition.prepend, - ) - .toList(), + specialItemModels: specialItemModels, ); } return null; @@ -659,11 +661,11 @@ final class FileAssetPickerBuilder } @override - Widget assetGridItemBuilder( - BuildContext context, - int index, - List currentAssets, { - List specialItemModels = const [], + Widget assetGridItemBuilder({ + required BuildContext context, + required int index, + required List currentAssets, + required List specialItemModels, }) { final int length = currentAssets.length; @@ -723,6 +725,7 @@ final class FileAssetPickerBuilder int assetsGridItemCount({ required BuildContext context, required List assets, + required List specialItemModels, int placeholderCount = 0, }) { final int length = assets.length + specialItems.length; diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index 87bf2cd6..860cb2df 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -348,6 +348,7 @@ abstract class AssetPickerBuilderDelegate { int assetsGridItemCount({ required BuildContext context, required List assets, + required List specialItemModels, int placeholderCount = 0, }); @@ -441,11 +442,12 @@ abstract class AssetPickerBuilderDelegate { /// The item builder for the assets' grid. /// 资源列表项的构建 - Widget assetGridItemBuilder( - BuildContext context, - int index, - List currentAssets, - ); + Widget assetGridItemBuilder({ + required BuildContext context, + required int index, + required List currentAssets, + required List specialItemModels, + }); /// The [Semantics] builder for the assets' grid. /// 资源列表项的语义构建 @@ -1406,9 +1408,9 @@ class DefaultAssetPickerBuilderDelegate } Widget child = assetGridItemBuilder( - context, - index, - assets, + context: context, + index: index, + currentAssets: assets, specialItemModels: specialItemModels, ); @@ -1604,11 +1606,11 @@ class DefaultAssetPickerBuilderDelegate /// 图片和视频类型 /// * 在索引到达倒数第三列的时候加载更多资源。 @override - Widget assetGridItemBuilder( - BuildContext context, - int index, - List currentAssets, { - List specialItemModels = const [], + Widget assetGridItemBuilder({ + required BuildContext context, + required int index, + required List currentAssets, + required List specialItemModels, }) { final p = context.read(); final int length = currentAssets.length; @@ -1787,28 +1789,10 @@ class DefaultAssetPickerBuilderDelegate int assetsGridItemCount({ required BuildContext context, required List assets, + required List specialItemModels, int placeholderCount = 0, - List specialItemModels = const [], }) { - final PathWrapper? currentWrapper = - context.select?>( - (T p) => p.currentPath, - ); - final AssetPathEntity? currentPathEntity = currentWrapper?.path; - final int length = assets.length + placeholderCount; - - // Return 1 if the [specialItem] build something. - if (currentPathEntity == null && specialItemModels.isNotEmpty) { - return placeholderCount + specialItemModels.length; - } - - // Return actual length if the current path is all. - // 如果当前目录是全部内容,则返回实际的内容数量。 - if (currentPathEntity?.isAll != true && specialItemModels.isEmpty) { - return length; - } - - return length + specialItemModels.length; + return assets.length + specialItemModels.length + placeholderCount; } @override From 9d87c585fd9be07c25f3a230a9bc7607d4062d72 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Sat, 16 Aug 2025 22:46:38 +0800 Subject: [PATCH 21/23] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20`SpecialItemModel`?= =?UTF-8?q?=20->=20`final=20SpecialItemFinalized`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pickers/directory_file_asset_picker.dart | 18 +++--- .../asset_picker_builder_delegate.dart | 55 +++++++------------ lib/src/models/special_item.dart | 35 ++++++++++-- 3 files changed, 60 insertions(+), 48 deletions(-) diff --git a/example/lib/customs/pickers/directory_file_asset_picker.dart b/example/lib/customs/pickers/directory_file_asset_picker.dart index d704b948..18a8bd65 100644 --- a/example/lib/customs/pickers/directory_file_asset_picker.dart +++ b/example/lib/customs/pickers/directory_file_asset_picker.dart @@ -525,7 +525,7 @@ final class FileAssetPickerBuilder appBarPreferredSize ??= appBar(context).preferredSize; int totalCount = provider.currentAssets.length; - final List specialItemModels = specialItems + final specialItemModels = specialItems .map((item) { final specialItem = item.builder?.call( context, @@ -533,14 +533,14 @@ final class FileAssetPickerBuilder permissionNotifier.value, ); if (specialItem != null) { - return SpecialItemModel( + return SpecialItemFinalized( position: item.position, item: specialItem, ); } return null; }) - .whereType() + .whereType() .toList(); totalCount += specialItemModels.length; @@ -665,12 +665,12 @@ final class FileAssetPickerBuilder required BuildContext context, required int index, required List currentAssets, - required List specialItemModels, + required List specialItemModels, }) { final int length = currentAssets.length; - final prependItems = []; - final appendItems = []; + final prependItems = []; + final appendItems = []; for (final model in specialItemModels) { switch (model.position) { case SpecialItemPosition.prepend: @@ -716,7 +716,7 @@ final class FileAssetPickerBuilder int index, File asset, Widget child, - List specialItemModels, + List specialItemModels, ) { return Semantics(child: child); } @@ -725,7 +725,7 @@ final class FileAssetPickerBuilder int assetsGridItemCount({ required BuildContext context, required List assets, - required List specialItemModels, + required List specialItemModels, int placeholderCount = 0, }) { final int length = assets.length + specialItems.length; @@ -1137,7 +1137,7 @@ final class FileAssetPickerBuilder int findChildIndexBuilder({ required String id, required List assets, - required List specialItemModels, + required List specialItemModels, int placeholderCount = 0, }) { return assets.indexWhere((File file) => file.path == id); diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index 860cb2df..6ab5054a 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -30,18 +30,6 @@ import '../widget/asset_picker_page_route.dart'; import '../widget/asset_picker_viewer.dart'; import '../widget/builder/asset_entity_grid_item_builder.dart'; -/// Class which contains non-null special item widget and its position which derived from the [SpecialItem] -/// 包含非空自定义item,并指定其位置。 -class SpecialItemModel { - const SpecialItemModel({ - required this.position, - required this.item, - }); - - final SpecialItemPosition position; - final Widget item; -} - /// The delegate to build the whole picker's components. /// /// By extending the delegate, you can customize every components on you own. @@ -338,7 +326,7 @@ abstract class AssetPickerBuilderDelegate { int? findChildIndexBuilder({ required String id, required List assets, - required List specialItemModels, + required List specialItemModels, int placeholderCount = 0, }) => null; @@ -348,11 +336,11 @@ abstract class AssetPickerBuilderDelegate { int assetsGridItemCount({ required BuildContext context, required List assets, - required List specialItemModels, + required List specialItemModels, int placeholderCount = 0, }); - List assetsGridSpecialItemModels({ + List assetsGridSpecialItemModels({ required BuildContext context, required Path? path, }) { @@ -364,7 +352,7 @@ abstract class AssetPickerBuilderDelegate { permissionNotifier.value, ); if (specialItem != null) { - return SpecialItemModel( + return SpecialItemFinalized( position: item.position, item: specialItem, ); @@ -380,7 +368,7 @@ abstract class AssetPickerBuilderDelegate { required BuildContext context, required PathWrapper? pathWrapper, required bool onlyOneScreen, - required List specialItemModels, + required List specialItemModels, }) { if (onlyOneScreen) { return 0; @@ -409,7 +397,7 @@ abstract class AssetPickerBuilderDelegate { required BuildContext context, required BoxConstraints constraints, required PathWrapper? pathWrapper, - required List specialItemModels, + required List specialItemModels, }) { int totalCount = pathWrapper?.assetCount ?? 0; // Add special items' count. @@ -446,7 +434,7 @@ abstract class AssetPickerBuilderDelegate { required BuildContext context, required int index, required List currentAssets, - required List specialItemModels, + required List specialItemModels, }); /// The [Semantics] builder for the assets' grid. @@ -456,7 +444,7 @@ abstract class AssetPickerBuilderDelegate { int index, Asset asset, Widget child, - List specialItemModels, + List specialItemModels, ); /// The item builder for audio type of asset. @@ -1610,15 +1598,15 @@ class DefaultAssetPickerBuilderDelegate required BuildContext context, required int index, required List currentAssets, - required List specialItemModels, + required List specialItemModels, }) { final p = context.read(); final int length = currentAssets.length; final PathWrapper? currentWrapper = p.currentPath; final AssetPathEntity? currentPathEntity = currentWrapper?.path; - final prependItems = []; - final appendItems = []; + final prependItems = []; + final appendItems = []; for (final model in specialItemModels) { switch (model.position) { case SpecialItemPosition.prepend: @@ -1682,10 +1670,11 @@ class DefaultAssetPickerBuilderDelegate int semanticIndex( int index, - List specialItemModels, + List specialItemModels, ) { final prependSpecialItemModels = specialItemModels.where( - (SpecialItemModel model) => model.position == SpecialItemPosition.prepend, + (SpecialItemFinalized model) => + model.position == SpecialItemPosition.prepend, ); return index - prependSpecialItemModels.length; } @@ -1696,7 +1685,7 @@ class DefaultAssetPickerBuilderDelegate int index, AssetEntity asset, Widget child, - List specialItemModels, + List specialItemModels, ) { return ValueListenableBuilder( valueListenable: isSwitchingPath, @@ -1773,11 +1762,12 @@ class DefaultAssetPickerBuilderDelegate int findChildIndexBuilder({ required String id, required List assets, - required List specialItemModels, + required List specialItemModels, int placeholderCount = 0, }) { final prependSpecialItemModels = specialItemModels.where( - (SpecialItemModel model) => model.position == SpecialItemPosition.prepend, + (SpecialItemFinalized model) => + model.position == SpecialItemPosition.prepend, ); int index = assets.indexWhere((AssetEntity e) => e.id == id); index += prependSpecialItemModels.length; @@ -1789,7 +1779,7 @@ class DefaultAssetPickerBuilderDelegate int assetsGridItemCount({ required BuildContext context, required List assets, - required List specialItemModels, + required List specialItemModels, int placeholderCount = 0, }) { return assets.length + specialItemModels.length + placeholderCount; @@ -2077,11 +2067,8 @@ class DefaultAssetPickerBuilderDelegate child: Selector>>( selector: (_, T p) => p.paths, builder: (_, List> paths, __) { - final List> filtered = paths - .where( - (PathWrapper p) => p.assetCount != 0, - ) - .toList(); + final filtered = + paths.where((p) => p.assetCount != 0).toList(); return ListView.separated( padding: const EdgeInsetsDirectional.only(top: 1), shrinkWrap: true, diff --git a/lib/src/models/special_item.dart b/lib/src/models/special_item.dart index 10b9c169..fcc1e6e9 100644 --- a/lib/src/models/special_item.dart +++ b/lib/src/models/special_item.dart @@ -1,20 +1,45 @@ import 'package:flutter/material.dart'; -import 'package:wechat_assets_picker/wechat_assets_picker.dart'; -/// Allow users to set a special item in the picker grid with specified [position]. +import '../constants/enums.dart'; +import '../constants/typedefs.dart'; + +/// Allow users to set special items in the picker grid with [position]. /// 允许用户在选择器中添加一个自定义item,并指定其位置。 @immutable class SpecialItem { const SpecialItem({ - required this.builder, required this.position, + required this.builder, }); + /// Define how the item will be positioned. + /// 定义如何摆放item。 + final SpecialItemPosition position; + /// The widget builder for the the special item. /// 自定义item构建。 final SpecialItemBuilder? builder; - /// Define how the item will be positioned. - /// 定义如何摆放item。 + @override + String toString() { + return 'SpecialItem$Path(position: $position, builder: $builder)'; + } +} + +/// A finalized [SpecialItem] which contains its position and the built widget. +/// 已被构建的 [SpecialItem],包含其位置和 widget 信息。 +@immutable +final class SpecialItemFinalized { + const SpecialItemFinalized({ + required this.position, + required this.item, + }); + final SpecialItemPosition position; + final Widget item; + + @override + String toString() { + return 'SpecialItemFinalized$Path(position: $position, item: $item)'; + } } From b792023cc579422cf8dd87b3d73b8d4ae97d25a0 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Sat, 16 Aug 2025 22:57:03 +0800 Subject: [PATCH 22/23] =?UTF-8?q?=F0=9F=9A=9A=20Correct=20names?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README-ZH.md | 2 +- README.md | 2 +- .../pickers/directory_file_asset_picker.dart | 30 +++---- .../customs/pickers/insta_asset_picker.dart | 2 +- .../pickers/multi_tabs_assets_picker.dart | 2 +- ...asset_grid_drag_selection_coordinator.dart | 6 +- .../asset_picker_builder_delegate.dart | 82 +++++++++---------- 7 files changed, 63 insertions(+), 63 deletions(-) diff --git a/README-ZH.md b/README-ZH.md index 94d93955..cfee1ba7 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -294,7 +294,7 @@ final List? result = await AssetPicker.pickAssets( | themeColor | `Color?` | 选择器的主题色 | `Color(0xff00bc56)` | | pickerTheme | `ThemeData?` | 选择器的主题提供,包括查看器 | `null` | | textDelegate | `AssetPickerTextDelegate?` | 选择器的文本代理构建,用于自定义文本 | `AssetPickerTextDelegate()` | -| specialItems | `List` | 自定义item列表 | `const []` | +| specialItems | `List` | 自定义item列表 | `const []` | | loadingIndicatorBuilder | `IndicatorBuilder?` | 加载器的实现 | `null` | | selectPredicate | `AssetSelectPredicate` | 判断资源可否被选择 | `null` | | shouldRevertGrid | `bool?` | 判断资源网格是否需要倒序排列 | `null` | diff --git a/README.md b/README.md index 1b61ade7..75c6dfbe 100644 --- a/README.md +++ b/README.md @@ -303,7 +303,7 @@ Fields in `AssetPickerConfig`: | themeColor | `Color?` | Main theme color for the picker. | `Color(0xff00bc56)` | | pickerTheme | `ThemeData?` | Theme data provider for the picker and the viewer. | `null` | | textDelegate | `AssetPickerTextDelegate?` | Text delegate for the picker, for customize the texts. | `AssetPickerTextDelegate()` | -| specialItems | `List` | List of special items. | `const []` | +| specialItems | `List` | List of special items. | `const []` | | loadingIndicatorBuilder | `IndicatorBuilder?` | Indicates the loading status for the builder. | `null` | | selectPredicate | `AssetSelectPredicate` | Predicate whether an asset can be selected or unselected. | `null` | | shouldRevertGrid | `bool?` | Whether the assets grid should revert. | `null` | diff --git a/example/lib/customs/pickers/directory_file_asset_picker.dart b/example/lib/customs/pickers/directory_file_asset_picker.dart index 18a8bd65..43efa803 100644 --- a/example/lib/customs/pickers/directory_file_asset_picker.dart +++ b/example/lib/customs/pickers/directory_file_asset_picker.dart @@ -525,7 +525,7 @@ final class FileAssetPickerBuilder appBarPreferredSize ??= appBar(context).preferredSize; int totalCount = provider.currentAssets.length; - final specialItemModels = specialItems + final specialItemsFinalized = specialItems .map((item) { final specialItem = item.builder?.call( context, @@ -543,7 +543,7 @@ final class FileAssetPickerBuilder .whereType() .toList(); - totalCount += specialItemModels.length; + totalCount += specialItemsFinalized.length; final int placeholderCount; if (isAppleOS(context) && totalCount % gridCount != 0) { @@ -573,7 +573,7 @@ final class FileAssetPickerBuilder context: c, index: index, currentAssets: assets, - specialItemModels: specialItemModels, + specialItemsFinalized: specialItemsFinalized, ), ); }, @@ -581,7 +581,7 @@ final class FileAssetPickerBuilder childCount: assetsGridItemCount( context: ctx, assets: assets, - specialItemModels: specialItemModels, + specialItemsFinalized: specialItemsFinalized, placeholderCount: placeholderCount, ), findChildIndexCallback: (Key? key) { @@ -590,7 +590,7 @@ final class FileAssetPickerBuilder id: key.value, assets: assets, placeholderCount: placeholderCount, - specialItemModels: specialItemModels, + specialItemsFinalized: specialItemsFinalized, ); } return null; @@ -665,30 +665,30 @@ final class FileAssetPickerBuilder required BuildContext context, required int index, required List currentAssets, - required List specialItemModels, + required List specialItemsFinalized, }) { final int length = currentAssets.length; final prependItems = []; final appendItems = []; - for (final model in specialItemModels) { - switch (model.position) { + for (final item in specialItemsFinalized) { + switch (item.position) { case SpecialItemPosition.prepend: - prependItems.add(model); + prependItems.add(item); case SpecialItemPosition.append: - appendItems.add(model); + appendItems.add(item); } } if (prependItems.isNotEmpty) { if (index < prependItems.length) { - return specialItemModels[index].item; + return specialItemsFinalized[index].item; } } if (appendItems.isNotEmpty) { if (index >= length + prependItems.length) { - return specialItemModels[index - length].item; + return specialItemsFinalized[index - length].item; } } @@ -716,7 +716,7 @@ final class FileAssetPickerBuilder int index, File asset, Widget child, - List specialItemModels, + List specialItemsFinalized, ) { return Semantics(child: child); } @@ -725,7 +725,7 @@ final class FileAssetPickerBuilder int assetsGridItemCount({ required BuildContext context, required List assets, - required List specialItemModels, + required List specialItemsFinalized, int placeholderCount = 0, }) { final int length = assets.length + specialItems.length; @@ -1137,7 +1137,7 @@ final class FileAssetPickerBuilder int findChildIndexBuilder({ required String id, required List assets, - required List specialItemModels, + required List specialItemsFinalized, int placeholderCount = 0, }) { return assets.indexWhere((File file) => file.path == id); diff --git a/example/lib/customs/pickers/insta_asset_picker.dart b/example/lib/customs/pickers/insta_asset_picker.dart index 6571f99e..3a02300f 100644 --- a/example/lib/customs/pickers/insta_asset_picker.dart +++ b/example/lib/customs/pickers/insta_asset_picker.dart @@ -677,7 +677,7 @@ final class InstaAssetPickerBuilder extends DefaultAssetPickerBuilderDelegate { return Consumer( builder: (context, p, __) { final hasAssetsToDisplay = p.hasAssetsToDisplay; - final shouldBuildSpecialItems = assetsGridSpecialItemModels( + final shouldBuildSpecialItems = assetsGridSpecialItemsFinalized( context: context, path: p.currentPath?.path, ).isNotEmpty; diff --git a/example/lib/customs/pickers/multi_tabs_assets_picker.dart b/example/lib/customs/pickers/multi_tabs_assets_picker.dart index 89176dda..7cf1e59a 100644 --- a/example/lib/customs/pickers/multi_tabs_assets_picker.dart +++ b/example/lib/customs/pickers/multi_tabs_assets_picker.dart @@ -525,7 +525,7 @@ final class MultiTabAssetPickerBuilder return Consumer( builder: (context, p, __) { final hasAssetsToDisplay = p.hasAssetsToDisplay; - final shouldBuildSpecialItems = assetsGridSpecialItemModels( + final shouldBuildSpecialItems = assetsGridSpecialItemsFinalized( context: context, path: p.currentPath?.path, ).isNotEmpty; diff --git a/lib/src/delegates/asset_grid_drag_selection_coordinator.dart b/lib/src/delegates/asset_grid_drag_selection_coordinator.dart index 74fdf89b..a422a3cf 100644 --- a/lib/src/delegates/asset_grid_drag_selection_coordinator.dart +++ b/lib/src/delegates/asset_grid_drag_selection_coordinator.dart @@ -123,7 +123,7 @@ class AssetGridDragSelectionCoordinator { totalRows * (itemSize + delegate.itemSpacing) <= gridViewport; final reverted = gridRevert && !onlyOneScreen; - final specialItems = delegate.assetsGridSpecialItemModels( + final specialItems = delegate.assetsGridSpecialItemsFinalized( context: context, path: provider.currentPath?.path, ); @@ -132,7 +132,7 @@ class AssetGridDragSelectionCoordinator { context: context, constraints: constraints, pathWrapper: provider.currentPath, - specialItemModels: specialItems, + specialItemsFinalized: specialItems, ); final scrolledOffset = delegate.gridScrollController.offset .abs(); // Offset is negative when reverted. @@ -163,7 +163,7 @@ class AssetGridDragSelectionCoordinator { context: context, pathWrapper: provider.currentPath, onlyOneScreen: onlyOneScreen, - specialItemModels: specialItems, + specialItemsFinalized: specialItems, ); // Make the index starts with the bottom if the grid is reverted. if (reverted && placeholderCount > 0 && rowIndex > 0 && anchor < 1.0) { diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index 6ab5054a..6e48074d 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -326,7 +326,7 @@ abstract class AssetPickerBuilderDelegate { int? findChildIndexBuilder({ required String id, required List assets, - required List specialItemModels, + required List specialItemsFinalized, int placeholderCount = 0, }) => null; @@ -336,11 +336,11 @@ abstract class AssetPickerBuilderDelegate { int assetsGridItemCount({ required BuildContext context, required List assets, - required List specialItemModels, + required List specialItemsFinalized, int placeholderCount = 0, }); - List assetsGridSpecialItemModels({ + List assetsGridSpecialItemsFinalized({ required BuildContext context, required Path? path, }) { @@ -368,7 +368,7 @@ abstract class AssetPickerBuilderDelegate { required BuildContext context, required PathWrapper? pathWrapper, required bool onlyOneScreen, - required List specialItemModels, + required List specialItemsFinalized, }) { if (onlyOneScreen) { return 0; @@ -377,7 +377,7 @@ abstract class AssetPickerBuilderDelegate { final bool gridRevert = effectiveShouldRevertGrid(context); int totalCount = pathWrapper?.assetCount ?? 0; // Add special items' count. - totalCount += specialItemModels.length; + totalCount += specialItemsFinalized.length; final int result; if (gridRevert && totalCount % gridCount != 0) { @@ -397,11 +397,11 @@ abstract class AssetPickerBuilderDelegate { required BuildContext context, required BoxConstraints constraints, required PathWrapper? pathWrapper, - required List specialItemModels, + required List specialItemsFinalized, }) { int totalCount = pathWrapper?.assetCount ?? 0; // Add special items' count. - totalCount += specialItemModels.length; + totalCount += specialItemsFinalized.length; // Here we got a magic calculation. [itemSpacing] needs to be divided by // [gridCount] since every grid item is squeezed by the [itemSpacing], @@ -434,7 +434,7 @@ abstract class AssetPickerBuilderDelegate { required BuildContext context, required int index, required List currentAssets, - required List specialItemModels, + required List specialItemsFinalized, }); /// The [Semantics] builder for the assets' grid. @@ -444,7 +444,7 @@ abstract class AssetPickerBuilderDelegate { int index, Asset asset, Widget child, - List specialItemModels, + List specialItemsFinalized, ); /// The item builder for audio type of asset. @@ -1233,7 +1233,7 @@ class DefaultAssetPickerBuilderDelegate body: Consumer( builder: (context, p, _) { final hasAssetsToDisplay = p.hasAssetsToDisplay; - final shouldBuildSpecialItems = assetsGridSpecialItemModels( + final shouldBuildSpecialItems = assetsGridSpecialItemsFinalized( context: context, path: p.currentPath?.path, ).isNotEmpty; @@ -1289,7 +1289,7 @@ class DefaultAssetPickerBuilderDelegate child: Consumer( builder: (context, p, _) { final hasAssetsToDisplay = p.hasAssetsToDisplay; - final shouldBuildSpecialItems = assetsGridSpecialItemModels( + final shouldBuildSpecialItems = assetsGridSpecialItemsFinalized( context: context, path: p.currentPath?.path, ).isNotEmpty; @@ -1358,13 +1358,13 @@ class DefaultAssetPickerBuilderDelegate builder: (context, wrapper, _) { // First, we need the count of the assets. int totalCount = wrapper?.assetCount ?? 0; - final specialItemModels = assetsGridSpecialItemModels( + final specialItemsFinalized = assetsGridSpecialItemsFinalized( context: context, path: wrapper?.path, ); - totalCount += specialItemModels.length; + totalCount += specialItemsFinalized.length; - if (totalCount == 0 && specialItemModels.isEmpty) { + if (totalCount == 0 && specialItemsFinalized.isEmpty) { return loadingIndicator(context); } @@ -1383,7 +1383,7 @@ class DefaultAssetPickerBuilderDelegate context: context, pathWrapper: wrapper, onlyOneScreen: onlyOneScreen, - specialItemModels: specialItemModels, + specialItemsFinalized: specialItemsFinalized, ); return SliverGrid( delegate: SliverChildBuilderDelegate( @@ -1399,7 +1399,7 @@ class DefaultAssetPickerBuilderDelegate context: context, index: index, currentAssets: assets, - specialItemModels: specialItemModels, + specialItemsFinalized: specialItemsFinalized, ); // Enables drag-to-select when: @@ -1490,7 +1490,7 @@ class DefaultAssetPickerBuilderDelegate context: context, assets: assets, placeholderCount: placeholderCount, - specialItemModels: specialItemModels, + specialItemsFinalized: specialItemsFinalized, ), findChildIndexCallback: (Key? key) { if (key is ValueKey) { @@ -1498,7 +1498,7 @@ class DefaultAssetPickerBuilderDelegate id: key.value, assets: assets, placeholderCount: placeholderCount, - specialItemModels: specialItemModels, + specialItemsFinalized: specialItemsFinalized, ); } return null; @@ -1531,7 +1531,7 @@ class DefaultAssetPickerBuilderDelegate context: context, constraints: constraints, pathWrapper: wrapper, - specialItemModels: specialItemModels, + specialItemsFinalized: specialItemsFinalized, ); final reverted = gridRevert && !onlyOneScreen; @@ -1598,7 +1598,7 @@ class DefaultAssetPickerBuilderDelegate required BuildContext context, required int index, required List currentAssets, - required List specialItemModels, + required List specialItemsFinalized, }) { final p = context.read(); final int length = currentAssets.length; @@ -1607,7 +1607,7 @@ class DefaultAssetPickerBuilderDelegate final prependItems = []; final appendItems = []; - for (final model in specialItemModels) { + for (final model in specialItemsFinalized) { switch (model.position) { case SpecialItemPosition.prepend: prependItems.add(model); @@ -1618,13 +1618,13 @@ class DefaultAssetPickerBuilderDelegate if (prependItems.isNotEmpty) { if (index < prependItems.length) { - return specialItemModels[index].item; + return specialItemsFinalized[index].item; } } if (appendItems.isNotEmpty) { if (index >= length + prependItems.length) { - return specialItemModels[index - length].item; + return specialItemsFinalized[index - length].item; } } @@ -1664,19 +1664,18 @@ class DefaultAssetPickerBuilderDelegate index, asset, content, - specialItemModels, + specialItemsFinalized, ); } - int semanticIndex( + int assetGridItemSemanticIndex( int index, - List specialItemModels, + List specialItemsFinalized, ) { - final prependSpecialItemModels = specialItemModels.where( - (SpecialItemFinalized model) => - model.position == SpecialItemPosition.prepend, + final prependItems = specialItemsFinalized.where( + (model) => model.position == SpecialItemPosition.prepend, ); - return index - prependSpecialItemModels.length; + return index - prependItems.length; } @override @@ -1685,7 +1684,7 @@ class DefaultAssetPickerBuilderDelegate int index, AssetEntity asset, Widget child, - List specialItemModels, + List specialItemsFinalized, ) { return ValueListenableBuilder( valueListenable: isSwitchingPath, @@ -1703,7 +1702,7 @@ class DefaultAssetPickerBuilderDelegate final int selectedIndex = p.selectedAssets.indexOf(asset) + 1; final labels = [ '${semanticsTextDelegate.semanticTypeLabel(asset.type)}' - '${semanticIndex(index, specialItemModels)}', + '${assetGridItemSemanticIndex(index, specialItemsFinalized)}', asset.createDateTime.toString().replaceAll('.000', ''), if (asset.type == AssetType.audio || asset.type == AssetType.video) @@ -1733,7 +1732,8 @@ class DefaultAssetPickerBuilderDelegate onLongPressHint: semanticsTextDelegate.sActionPreviewHint, selected: isSelected, sortKey: OrdinalSortKey( - semanticIndex(index, specialItemModels).toDouble(), + assetGridItemSemanticIndex(index, specialItemsFinalized) + .toDouble(), name: 'GridItem', ), value: selectedIndex > 0 ? '$selectedIndex' : null, @@ -1746,7 +1746,8 @@ class DefaultAssetPickerBuilderDelegate } : null, child: IndexedSemantics( - index: semanticIndex(index, specialItemModels), + index: + assetGridItemSemanticIndex(index, specialItemsFinalized), child: child, ), ), @@ -1762,15 +1763,14 @@ class DefaultAssetPickerBuilderDelegate int findChildIndexBuilder({ required String id, required List assets, - required List specialItemModels, + required List specialItemsFinalized, int placeholderCount = 0, }) { - final prependSpecialItemModels = specialItemModels.where( - (SpecialItemFinalized model) => - model.position == SpecialItemPosition.prepend, + final prependItems = specialItemsFinalized.where( + (model) => model.position == SpecialItemPosition.prepend, ); int index = assets.indexWhere((AssetEntity e) => e.id == id); - index += prependSpecialItemModels.length; + index += prependItems.length; index += placeholderCount; return index; } @@ -1779,10 +1779,10 @@ class DefaultAssetPickerBuilderDelegate int assetsGridItemCount({ required BuildContext context, required List assets, - required List specialItemModels, + required List specialItemsFinalized, int placeholderCount = 0, }) { - return assets.length + specialItemModels.length + placeholderCount; + return assets.length + specialItemsFinalized.length + placeholderCount; } @override From 205b0c5e066f6ff584b89eaa2b949ec93d74ab1a Mon Sep 17 00:00:00 2001 From: Alex Li Date: Sat, 16 Aug 2025 22:59:48 +0800 Subject: [PATCH 23/23] =?UTF-8?q?=F0=9F=93=9D=20CHANGELOG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b158adce..198cf78d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ that can be found in the LICENSE file. --> ## Unreleased +**New features** + +- Support multiple append/prepend specials items. + +**Improvements** + - Make delegate respect generic types as much as possible. This is a breaking change for users who use custom delegates and providers.