Skip to content

Commit

Permalink
✨ Support multiple special items.
Browse files Browse the repository at this point in the history
  • Loading branch information
yujunetee committed Sep 27, 2024
1 parent 6228379 commit 41b5dd0
Show file tree
Hide file tree
Showing 20 changed files with 304 additions and 181 deletions.
3 changes: 1 addition & 2 deletions README-ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,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 []` |
| 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 @@ -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<SpecialItem>` | 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` |
Expand Down
233 changes: 149 additions & 84 deletions example/lib/constants/picker_method.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(
itemPosition: SpecialItemPosition.prepend,
itemBuilder: (
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),
),
),
),
),
),
);
},
);
},
),
],
),
);
},
Expand All @@ -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<AssetEntity, AssetPathEntity>>()!;
final builder =
picker.builder as DefaultAssetPickerBuilderDelegate;
final p = builder.provider;
await p.switchPath(
PathWrapper<AssetPathEntity>(
path:
await p.currentPath!.path.obtainForNewProperties(),
specialItems: [
SpecialItem(
itemPosition: SpecialItemPosition.prepend,
itemBuilder: (
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<AssetEntity, AssetPathEntity>>()!;
final builder =
picker.builder as DefaultAssetPickerBuilderDelegate;
final p = 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 @@ -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(
itemPosition: SpecialItemPosition.prepend,
itemBuilder: (
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<AssetEntity> assets) {
return AssetPicker.pickAssets(
context,
pickerConfig: AssetPickerConfig(
maxAssets: maxAssetsCount,
selectedAssets: assets,
specialItems: [
SpecialItem(
itemPosition: SpecialItemPosition.prepend,
itemBuilder: (
BuildContext context,
AssetPathEntity? path,
int length,
bool isPermissionLimited,
) {
return const Center(
child: Text('Prepand Widget', textAlign: TextAlign.center),
);
},
),
SpecialItem(
itemPosition: SpecialItemPosition.append,
itemBuilder: (
BuildContext context,
AssetPathEntity? path,
int length,
bool isPermissionLimited,
) {
return const Center(
child: Text('Append Widget', textAlign: TextAlign.center),
);
},
),
],
),
);
},
Expand Down
19 changes: 8 additions & 11 deletions example/lib/customs/pickers/directory_file_asset_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -701,10 +701,12 @@ class FileAssetPickerBuilder
int index,
List<File> currentAssets,
) {
final int currentIndex = switch (specialItemPosition) {
SpecialItemPosition.none || SpecialItemPosition.append => index,
SpecialItemPosition.prepend => index - 1,
};
int currentIndex = index;

if (prepandSpecialItems.isNotEmpty) {
currentIndex = index - prepandSpecialItems.length;
}

final File asset = currentAssets.elementAt(currentIndex);
final Widget builder = imageAndVideoItemBuilder(
context,
Expand Down Expand Up @@ -737,12 +739,7 @@ class FileAssetPickerBuilder
required List<File> 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;
}

Expand Down
1 change: 0 additions & 1 deletion example/lib/customs/pickers/insta_asset_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions example/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions example/lib/l10n/app_zh.arb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
"pickMethodCustomFilterOptionsDescription": "为选择器添加自定义过滤条件。",
"pickMethodPrependItemName": "往网格前插入 widget",
"pickMethodPrependItemDescription": "网格的靠前位置会添加一个自定义的 widget。",
"pickMethodMultiSpecialItemsName": "多个特殊item",
"pickMethodMultiSpecialItemsDescription": "网格的靠前或靠后位置会添加多个自定义的 widget。",
"pickMethodNoPreviewName": "禁止预览",
"pickMethodNoPreviewDescription": "无法预览选择的资源,与 WhatsApp/MegaTok 的行为类似。",
"pickMethodKeepScrollOffsetName": "保持滚动位置",
Expand Down
12 changes: 12 additions & 0 deletions example/lib/l10n/gen/app_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,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:
Expand Down
7 changes: 7 additions & 0 deletions example/lib/l10n/gen/app_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,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';

Expand Down
7 changes: 7 additions & 0 deletions example/lib/l10n/gen/app_localizations_zh.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get pickMethodPrependItemDescription => '网格的靠前位置会添加一个自定义的 widget。';

@override
String get pickMethodMultiSpecialItemsName => '多个特殊item';

@override
String get pickMethodMultiSpecialItemsDescription =>
'网格的靠前或靠后位置会添加多个自定义的 widget。';

@override
String get pickMethodNoPreviewName => '禁止预览';

Expand Down
1 change: 1 addition & 0 deletions example/lib/pages/multi_assets_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class _MultiAssetsPageState extends State<MultiAssetsPage>
PickMethod.changeLanguages(context, maxAssetsCount),
PickMethod.threeItemsGrid(context, maxAssetsCount),
PickMethod.prependItem(context, maxAssetsCount),
PickMethod.multiSpecialItems(context, maxAssetsCount),
PickMethod(
icon: '🎭',
name: context.l10n.pickMethodWeChatMomentName,
Expand Down
Loading

0 comments on commit 41b5dd0

Please sign in to comment.