Skip to content

✨ Support Multiple Special Items #635

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 24 commits into
base: feat/v10
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
be8fdf5
✨ Support multiple special items.
yujunetee Sep 27, 2024
72960c0
Add permission state in special item builder.
yujunetee Oct 10, 2024
3834423
Add specialItem count instead of hardcoded 1.
yujunetee Oct 16, 2024
5283b0c
Update example/lib/customs/pickers/directory_file_asset_picker.dart
yujune Oct 16, 2024
add13d3
Move out SpecialItemModel from typedefs.
yujunetee Oct 16, 2024
352270c
Update lib/src/delegates/asset_picker_builder_delegate.dart
yujune Oct 16, 2024
a086f04
Update lib/src/delegates/asset_picker_builder_delegate.dart
yujune Oct 16, 2024
73aa6ad
Update lib/src/delegates/asset_picker_builder_delegate.dart
yujune Oct 16, 2024
d15ee28
Remove redundant codes.
yujunetee Oct 16, 2024
560c455
Remove incorrect -1 logic.
yujunetee Oct 16, 2024
281a9f2
Remove redundant checking.
yujunetee Oct 16, 2024
d6cb626
Add appendSpecialItems.
yujunetee Oct 16, 2024
5ea0caf
Update lib/src/delegates/asset_picker_builder_delegate.dart
yujune Oct 16, 2024
c8686a7
Move _SpecialItemModel to top.
yujunetee Oct 16, 2024
fc43aab
Fix wrong special item calculation and naming.
yujunetee Oct 17, 2024
78bd4db
Add null builder in example.
yujunetee Oct 17, 2024
38cc19f
Rename SpecialItemResult ot SpecialItemModel.
yujunetee Oct 20, 2024
f310550
Use specialItemModels as args.
yujunetee Oct 20, 2024
b80b76b
Merge branch 'refs/heads/feat/v10' into multi-special-item-builder
AlexV525 Aug 12, 2025
2ae888c
♻️ Reorg special item builders and counters
AlexV525 Aug 16, 2025
687457f
🐛 Fix counts
AlexV525 Aug 16, 2025
9d87c58
♻️ `SpecialItemModel` -> `final SpecialItemFinalized`
AlexV525 Aug 16, 2025
b792023
🚚 Correct names
AlexV525 Aug 16, 2025
205b0c5
📝 CHANGELOG
AlexV525 Aug 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
3 changes: 1 addition & 2 deletions README-ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,7 @@ final List<AssetEntity>? 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<SpecialItem>` | 自定义item列表 | `const <SpecialItem>[]` |
| loadingIndicatorBuilder | `IndicatorBuilder?` | 加载器的实现 | `null` |
| selectPredicate | `AssetSelectPredicate` | 判断资源可否被选择 | `null` |
| shouldRevertGrid | `bool?` | 判断资源网格是否需要倒序排列 | `null` |
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +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()` |
| 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<SpecialItem>` | List of special items. | `const <SpecialItem>[]` |
| 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` |
Expand Down
247 changes: 164 additions & 83 deletions example/lib/constants/picker_method.dart
Original file line number Diff line number Diff line change
Expand Up @@ -135,39 +135,44 @@ 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,
PermissionState permissionState,
) {
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),
),
),
),
),
),
);
},
);
},
),
],
),
);
},
Expand All @@ -186,49 +191,54 @@ 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<AssetEntity, AssetPathEntity,
DefaultAssetPickerBuilderDelegate>>()!;
final p = picker.builder.provider;
await p.switchPath(
PathWrapper<AssetPathEntity>(
path:
await p.currentPath!.path.obtainForNewProperties(),
specialItems: [
SpecialItem(
position: SpecialItemPosition.prepend,
builder: (
BuildContext context,
AssetPathEntity? path,
PermissionState permissionState,
) {
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<AssetEntity, AssetPathEntity,
DefaultAssetPickerBuilderDelegate>>()!;
final p = picker.builder.provider;
await p.switchPath(
PathWrapper<AssetPathEntity>(
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),
),
),
),
);
},
);
},
),
],
),
);
},
Expand Down Expand Up @@ -294,16 +304,87 @@ 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,
PermissionState permissionState,
) {
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<AssetEntity> assets) {
return AssetPicker.pickAssets(
context,
pickerConfig: AssetPickerConfig(
maxAssets: maxAssetsCount,
selectedAssets: assets,
specialItems: [
SpecialItem(
position: SpecialItemPosition.prepend,
builder: (
BuildContext context,
AssetPathEntity? path,
PermissionState permissionState,
) {
return const Center(
child: Text('Prepand Widget', textAlign: TextAlign.center),
);
},
),
SpecialItem(
position: SpecialItemPosition.append,
builder: (
BuildContext context,
AssetPathEntity? path,
PermissionState permissionState,
) {
return const Center(
child: Text('Append Widget', textAlign: TextAlign.center),
);
},
),
//builder which return null will not be shown.
SpecialItem(
position: SpecialItemPosition.append,
builder: (
BuildContext context,
AssetPathEntity? path,
PermissionState permissionState,
) {
return null;
},
),
SpecialItem(
position: SpecialItemPosition.prepend,
builder: (
BuildContext context,
AssetPathEntity? path,
PermissionState permissionState,
) {
return null;
},
),
],
),
);
},
Expand Down
Loading