Skip to content

Commit 776ab18

Browse files
committed
✨ Support multiple special items.
1 parent de46826 commit 776ab18

20 files changed

+302
-184
lines changed

README-ZH.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,7 @@ final List<AssetEntity>? result = await AssetPicker.pickAssets(
295295
| themeColor | `Color?` | 选择器的主题色 | `Color(0xff00bc56)` |
296296
| pickerTheme | `ThemeData?` | 选择器的主题提供,包括查看器 | `null` |
297297
| textDelegate | `AssetPickerTextDelegate?` | 选择器的文本代理构建,用于自定义文本 | `AssetPickerTextDelegate()` |
298-
| specialItemPosition | `SpecialItemPosition` | 允许用户在选择器中添加一个自定义item,并指定位置。 | `SpecialPosition.none` |
299-
| specialItemBuilder | `SpecialItemBuilder?` | 自定义item的构造方法 | `null` |
298+
| specialItems | `List<SpecialItem>` | 自定义item列表 | `const []` |
300299
| loadingIndicatorBuilder | `IndicatorBuilder?` | 加载器的实现 | `null` |
301300
| selectPredicate | `AssetSelectPredicate` | 判断资源可否被选择 | `null` |
302301
| shouldRevertGrid | `bool?` | 判断资源网格是否需要倒序排列 | `null` |

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,7 @@ Fields in `AssetPickerConfig`:
304304
| themeColor | `Color?` | Main theme color for the picker. | `Color(0xff00bc56)` |
305305
| pickerTheme | `ThemeData?` | Theme data provider for the picker and the viewer. | `null` |
306306
| textDelegate | `AssetPickerTextDelegate?` | Text delegate for the picker, for customize the texts. | `AssetPickerTextDelegate()` |
307-
| specialItemPosition | `SpecialItemPosition` | Allow users set a special item in the picker with several positions. | `SpecialItemPosition.none` |
308-
| specialItemBuilder | `SpecialItemBuilder?` | The widget builder for the special item. | `null` |
307+
| specialItems | `List<SpecialItem>` | List of special items. | `const []` |
309308
| loadingIndicatorBuilder | `IndicatorBuilder?` | Indicates the loading status for the builder. | `null` |
310309
| selectPredicate | `AssetSelectPredicate` | Predicate whether an asset can be selected or unselected. | `null` |
311310
| shouldRevertGrid | `bool?` | Whether the assets grid should revert. | `null` |

example/lib/constants/picker_method.dart

Lines changed: 149 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -135,39 +135,45 @@ class PickMethod {
135135
pickerConfig: AssetPickerConfig(
136136
maxAssets: maxAssetsCount,
137137
selectedAssets: assets,
138-
specialItemPosition: SpecialItemPosition.prepend,
139-
specialItemBuilder: (
140-
BuildContext context,
141-
AssetPathEntity? path,
142-
int length,
143-
) {
144-
if (path?.isAll != true) {
145-
return null;
146-
}
147-
return Semantics(
148-
label: textDelegate.sActionUseCameraHint,
149-
button: true,
150-
onTapHint: textDelegate.sActionUseCameraHint,
151-
child: GestureDetector(
152-
behavior: HitTestBehavior.opaque,
153-
onTap: () async {
154-
Feedback.forTap(context);
155-
final AssetEntity? result = await _pickFromCamera(context);
156-
if (result != null) {
157-
handleResult(context, result);
158-
}
159-
},
160-
child: Container(
161-
padding: const EdgeInsets.all(28.0),
162-
color: Theme.of(context).dividerColor,
163-
child: const FittedBox(
164-
fit: BoxFit.fill,
165-
child: Icon(Icons.camera_enhance),
138+
specialItems: [
139+
SpecialItem(
140+
position: SpecialItemPosition.prepend,
141+
builder: (
142+
BuildContext context,
143+
AssetPathEntity? path,
144+
int length,
145+
bool isPermissionLimited,
146+
) {
147+
if (path?.isAll != true) {
148+
return null;
149+
}
150+
return Semantics(
151+
label: textDelegate.sActionUseCameraHint,
152+
button: true,
153+
onTapHint: textDelegate.sActionUseCameraHint,
154+
child: GestureDetector(
155+
behavior: HitTestBehavior.opaque,
156+
onTap: () async {
157+
Feedback.forTap(context);
158+
final AssetEntity? result =
159+
await _pickFromCamera(context);
160+
if (result != null) {
161+
handleResult(context, result);
162+
}
163+
},
164+
child: Container(
165+
padding: const EdgeInsets.all(28.0),
166+
color: Theme.of(context).dividerColor,
167+
child: const FittedBox(
168+
fit: BoxFit.fill,
169+
child: Icon(Icons.camera_enhance),
170+
),
171+
),
166172
),
167-
),
168-
),
169-
);
170-
},
173+
);
174+
},
175+
),
176+
],
171177
),
172178
);
173179
},
@@ -186,50 +192,56 @@ class PickMethod {
186192
pickerConfig: AssetPickerConfig(
187193
maxAssets: maxAssetsCount,
188194
selectedAssets: assets,
189-
specialItemPosition: SpecialItemPosition.prepend,
190-
specialItemBuilder: (
191-
BuildContext context,
192-
AssetPathEntity? path,
193-
int length,
194-
) {
195-
if (path?.isAll != true) {
196-
return null;
197-
}
198-
return Semantics(
199-
label: textDelegate.sActionUseCameraHint,
200-
button: true,
201-
onTapHint: textDelegate.sActionUseCameraHint,
202-
child: GestureDetector(
203-
behavior: HitTestBehavior.opaque,
204-
onTap: () async {
205-
final AssetEntity? result = await _pickFromCamera(context);
206-
if (result == null) {
207-
return;
208-
}
209-
final picker = context.findAncestorWidgetOfExactType<
210-
AssetPicker<AssetEntity, AssetPathEntity>>()!;
211-
final builder =
212-
picker.builder as DefaultAssetPickerBuilderDelegate;
213-
final p = builder.provider;
214-
await p.switchPath(
215-
PathWrapper<AssetPathEntity>(
216-
path:
217-
await p.currentPath!.path.obtainForNewProperties(),
195+
specialItems: [
196+
SpecialItem(
197+
position: SpecialItemPosition.prepend,
198+
builder: (
199+
BuildContext context,
200+
AssetPathEntity? path,
201+
int length,
202+
bool isPermissionLimited,
203+
) {
204+
if (path?.isAll != true) {
205+
return null;
206+
}
207+
return Semantics(
208+
label: textDelegate.sActionUseCameraHint,
209+
button: true,
210+
onTapHint: textDelegate.sActionUseCameraHint,
211+
child: GestureDetector(
212+
behavior: HitTestBehavior.opaque,
213+
onTap: () async {
214+
final AssetEntity? result =
215+
await _pickFromCamera(context);
216+
if (result == null) {
217+
return;
218+
}
219+
final picker = context.findAncestorWidgetOfExactType<
220+
AssetPicker<AssetEntity, AssetPathEntity>>()!;
221+
final builder =
222+
picker.builder as DefaultAssetPickerBuilderDelegate;
223+
final p = builder.provider;
224+
await p.switchPath(
225+
PathWrapper<AssetPathEntity>(
226+
path: await p.currentPath!.path
227+
.obtainForNewProperties(),
228+
),
229+
);
230+
p.selectAsset(result);
231+
},
232+
child: Container(
233+
padding: const EdgeInsets.all(28.0),
234+
color: Theme.of(context).dividerColor,
235+
child: const FittedBox(
236+
fit: BoxFit.fill,
237+
child: Icon(Icons.camera_enhance),
238+
),
218239
),
219-
);
220-
p.selectAsset(result);
221-
},
222-
child: Container(
223-
padding: const EdgeInsets.all(28.0),
224-
color: Theme.of(context).dividerColor,
225-
child: const FittedBox(
226-
fit: BoxFit.fill,
227-
child: Icon(Icons.camera_enhance),
228240
),
229-
),
230-
),
231-
);
232-
},
241+
);
242+
},
243+
),
244+
],
233245
),
234246
);
235247
},
@@ -295,16 +307,69 @@ class PickMethod {
295307
pickerConfig: AssetPickerConfig(
296308
maxAssets: maxAssetsCount,
297309
selectedAssets: assets,
298-
specialItemPosition: SpecialItemPosition.prepend,
299-
specialItemBuilder: (
300-
BuildContext context,
301-
AssetPathEntity? path,
302-
int length,
303-
) {
304-
return const Center(
305-
child: Text('Custom Widget', textAlign: TextAlign.center),
306-
);
307-
},
310+
specialItems: [
311+
SpecialItem(
312+
position: SpecialItemPosition.prepend,
313+
builder: (
314+
BuildContext context,
315+
AssetPathEntity? path,
316+
int length,
317+
bool isPermissionLimited,
318+
) {
319+
return const Center(
320+
child: Text('Custom Widget', textAlign: TextAlign.center),
321+
);
322+
},
323+
),
324+
],
325+
),
326+
);
327+
},
328+
);
329+
}
330+
331+
factory PickMethod.multiSpecialItems(
332+
BuildContext context,
333+
int maxAssetsCount,
334+
) {
335+
return PickMethod(
336+
icon: '💡',
337+
name: context.l10n.pickMethodMultiSpecialItemsName,
338+
description: context.l10n.pickMethodMultiSpecialItemsDescription,
339+
method: (BuildContext context, List<AssetEntity> assets) {
340+
return AssetPicker.pickAssets(
341+
context,
342+
pickerConfig: AssetPickerConfig(
343+
maxAssets: maxAssetsCount,
344+
selectedAssets: assets,
345+
specialItems: [
346+
SpecialItem(
347+
position: SpecialItemPosition.prepend,
348+
builder: (
349+
BuildContext context,
350+
AssetPathEntity? path,
351+
int length,
352+
bool isPermissionLimited,
353+
) {
354+
return const Center(
355+
child: Text('Prepand Widget', textAlign: TextAlign.center),
356+
);
357+
},
358+
),
359+
SpecialItem(
360+
position: SpecialItemPosition.append,
361+
builder: (
362+
BuildContext context,
363+
AssetPathEntity? path,
364+
int length,
365+
bool isPermissionLimited,
366+
) {
367+
return const Center(
368+
child: Text('Append Widget', textAlign: TextAlign.center),
369+
);
370+
},
371+
),
372+
],
308373
),
309374
);
310375
},

example/lib/customs/pickers/directory_file_asset_picker.dart

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ class FileAssetPickerBuilder
584584
Widget assetsGridBuilder(BuildContext context) {
585585
appBarPreferredSize ??= appBar(context).preferredSize;
586586
int totalCount = provider.currentAssets.length;
587-
if (specialItemPosition != SpecialItemPosition.none) {
587+
if (specialItems.isNotEmpty) {
588588
totalCount += 1;
589589
}
590590
final int placeholderCount;
@@ -701,10 +701,10 @@ class FileAssetPickerBuilder
701701
int index,
702702
List<File> currentAssets,
703703
) {
704-
final int currentIndex = switch (specialItemPosition) {
705-
SpecialItemPosition.none || SpecialItemPosition.append => index,
706-
SpecialItemPosition.prepend => index - 1,
707-
};
704+
int currentIndex = index;
705+
706+
currentIndex = index - prependSpecialItems.length;
707+
708708
final File asset = currentAssets.elementAt(currentIndex);
709709
final Widget builder = imageAndVideoItemBuilder(
710710
context,
@@ -737,12 +737,7 @@ class FileAssetPickerBuilder
737737
required List<File> assets,
738738
int placeholderCount = 0,
739739
}) {
740-
final int length = switch (specialItemPosition) {
741-
SpecialItemPosition.none => assets.length,
742-
SpecialItemPosition.prepend ||
743-
SpecialItemPosition.append =>
744-
assets.length + 1,
745-
};
740+
final int length = assets.length + specialItems.length;
746741
return length + placeholderCount;
747742
}
748743

example/lib/customs/pickers/insta_asset_picker.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,6 @@ class InstaAssetPickerBuilder extends DefaultAssetPickerBuilderDelegate {
297297
super.keepScrollOffset,
298298
}) : super(
299299
shouldRevertGrid: false,
300-
specialItemPosition: SpecialItemPosition.none,
301300
);
302301

303302
/// Save last position of the grid view scroll controller

example/lib/l10n/app_en.arb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
"pickMethodCustomFilterOptionsDescription": "Add filter options for the picker.",
2929
"pickMethodPrependItemName": "Prepend special item",
3030
"pickMethodPrependItemDescription": "A special item will prepend to the assets grid.",
31+
"pickMethodMultiSpecialItemsName": "Multiple special items",
32+
"pickMethodMultiSpecialItemsDescription": "Multiple special items will prepend or append to the assets grid",
3133
"pickMethodNoPreviewName": "No preview",
3234
"pickMethodNoPreviewDescription": "You cannot preview assets during the picking, the behavior is like the WhatsApp/MegaTok pattern.",
3335
"pickMethodKeepScrollOffsetName": "Keep scroll offset",

example/lib/l10n/app_zh.arb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
"pickMethodCustomFilterOptionsDescription": "为选择器添加自定义过滤条件。",
2929
"pickMethodPrependItemName": "往网格前插入 widget",
3030
"pickMethodPrependItemDescription": "网格的靠前位置会添加一个自定义的 widget。",
31+
"pickMethodMultiSpecialItemsName": "多个特殊 widget",
32+
"pickMethodMultiSpecialItemsDescription": "网格的靠前或靠后位置会可以多个自定义的 widget。",
3133
"pickMethodNoPreviewName": "禁止预览",
3234
"pickMethodNoPreviewDescription": "无法预览选择的资源,与 WhatsApp/MegaTok 的行为类似。",
3335
"pickMethodKeepScrollOffsetName": "保持滚动位置",

example/lib/l10n/gen/app_localizations.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,18 @@ abstract class AppLocalizations {
264264
/// **'A special item will prepend to the assets grid.'**
265265
String get pickMethodPrependItemDescription;
266266

267+
/// No description provided for @pickMethodMultiSpecialItemsName.
268+
///
269+
/// In en, this message translates to:
270+
/// **'Multiple special items'**
271+
String get pickMethodMultiSpecialItemsName;
272+
273+
/// No description provided for @pickMethodMultiSpecialItemsDescription.
274+
///
275+
/// In en, this message translates to:
276+
/// **'Multiple special items will prepend or append to the assets grid'**
277+
String get pickMethodMultiSpecialItemsDescription;
278+
267279
/// No description provided for @pickMethodNoPreviewName.
268280
///
269281
/// In en, this message translates to:

example/lib/l10n/gen/app_localizations_en.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ class AppLocalizationsEn extends AppLocalizations {
9999
String get pickMethodPrependItemDescription =>
100100
'A special item will prepend to the assets grid.';
101101

102+
@override
103+
String get pickMethodMultiSpecialItemsName => 'Multiple special items';
104+
105+
@override
106+
String get pickMethodMultiSpecialItemsDescription =>
107+
'Multiple special items will prepend or append to the assets grid';
108+
102109
@override
103110
String get pickMethodNoPreviewName => 'No preview';
104111

example/lib/l10n/gen/app_localizations_zh.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ class AppLocalizationsZh extends AppLocalizations {
9393
@override
9494
String get pickMethodPrependItemDescription => '网格的靠前位置会添加一个自定义的 widget。';
9595

96+
@override
97+
String get pickMethodMultiSpecialItemsName => '多个特殊 widget';
98+
99+
@override
100+
String get pickMethodMultiSpecialItemsDescription =>
101+
'网格的靠前或靠后位置会可以多个自定义的 widget。';
102+
96103
@override
97104
String get pickMethodNoPreviewName => '禁止预览';
98105

example/lib/pages/multi_assets_page.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class _MultiAssetsPageState extends State<MultiAssetsPage>
4646
PickMethod.changeLanguages(context, maxAssetsCount),
4747
PickMethod.threeItemsGrid(context, maxAssetsCount),
4848
PickMethod.prependItem(context, maxAssetsCount),
49+
PickMethod.multiSpecialItems(context, maxAssetsCount),
4950
PickMethod(
5051
icon: '🎭',
5152
name: context.l10n.pickMethodWeChatMomentName,

0 commit comments

Comments
 (0)