Skip to content

Commit a62a40b

Browse files
authored
⚡️ Improve specialItemBuilder (#314)
1 parent 5223d63 commit a62a40b

File tree

1 file changed

+67
-69
lines changed

1 file changed

+67
-69
lines changed

lib/src/delegates/asset_picker_builder_delegate.dart

Lines changed: 67 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,11 @@ abstract class AssetPickerBuilderDelegate<Asset, Path> {
8787
final ThemeData? pickerTheme;
8888

8989
/// Allow users set a special item in the picker with several positions.
90-
/// 允许用户在选择器中添加一个自定义item,并指定位置
90+
/// 允许用户在选择器中添加一个自定义 item,并指定位置
9191
final SpecialItemPosition specialItemPosition;
9292

9393
/// The widget builder for the the special item.
94-
/// 自定义item的构造方法
94+
/// 自定义 item 的构造方法
9595
final SpecialItemBuilder<Path>? specialItemBuilder;
9696

9797
/// Indicates the loading status for the builder.
@@ -154,6 +154,12 @@ abstract class AssetPickerBuilderDelegate<Asset, Path> {
154154
/// 选择器是否为单选模式
155155
bool get isSingleAssetMode;
156156

157+
/// Whether the delegate should build the special item.
158+
/// 是否需要构建自定义 item
159+
bool get shouldBuildSpecialItem =>
160+
specialItemPosition != SpecialItemPosition.none &&
161+
specialItemBuilder != null;
162+
157163
/// Space between assets item widget.
158164
/// 资源部件之间的间隔
159165
double get itemSpacing => 2;
@@ -931,15 +937,9 @@ class DefaultAssetPickerBuilderDelegate
931937
return AssetPickerAppBarWrapper(
932938
appBar: appBar(context),
933939
body: Consumer<DefaultAssetPickerProvider>(
934-
builder: (BuildContext context, DefaultAssetPickerProvider p, __) {
935-
final Widget? _specialItem = specialItemBuilder?.call(
936-
context,
937-
p.currentPath,
938-
p.currentAssets.length,
939-
);
940-
final bool shouldDisplayAssets = p.hasAssetsToDisplay ||
941-
(_specialItem != null &&
942-
specialItemPosition != SpecialItemPosition.none);
940+
builder: (BuildContext context, DefaultAssetPickerProvider p, _) {
941+
final bool shouldDisplayAssets =
942+
p.hasAssetsToDisplay || shouldBuildSpecialItem;
943943
return AnimatedSwitcher(
944944
duration: switchingPathDuration,
945945
child: shouldDisplayAssets
@@ -995,14 +995,8 @@ class DefaultAssetPickerBuilderDelegate
995995
child: Consumer<DefaultAssetPickerProvider>(
996996
builder: (_, DefaultAssetPickerProvider p, __) {
997997
final Widget _child;
998-
final Widget? _specialItem = specialItemBuilder?.call(
999-
context,
1000-
p.currentPath,
1001-
p.currentAssets.length,
1002-
);
1003-
final bool shouldDisplayAssets = p.hasAssetsToDisplay ||
1004-
(_specialItem != null &&
1005-
specialItemPosition != SpecialItemPosition.none);
998+
final bool shouldDisplayAssets =
999+
p.hasAssetsToDisplay || shouldBuildSpecialItem;
10061000
if (shouldDisplayAssets) {
10071001
_child = Stack(
10081002
children: <Widget>[
@@ -1042,13 +1036,26 @@ class DefaultAssetPickerBuilderDelegate
10421036
Widget assetsGridBuilder(BuildContext context) {
10431037
return Selector<DefaultAssetPickerProvider, AssetPathEntity?>(
10441038
selector: (_, DefaultAssetPickerProvider p) => p.currentPath,
1045-
builder: (_, AssetPathEntity? path, __) {
1039+
builder: (BuildContext context, AssetPathEntity? path, __) {
10461040
// First, we need the count of the assets.
10471041
int totalCount = path?.assetCount ?? 0;
1042+
final Widget? _specialItem;
10481043
// If user chose a special item's position, add 1 count.
1049-
if (specialItemPosition != SpecialItemPosition.none &&
1050-
path?.isAll == true) {
1051-
totalCount += 1;
1044+
if (specialItemPosition != SpecialItemPosition.none) {
1045+
_specialItem = specialItemBuilder?.call(
1046+
context,
1047+
path,
1048+
totalCount,
1049+
);
1050+
if (_specialItem != null) {
1051+
totalCount += 1;
1052+
}
1053+
} else {
1054+
_specialItem = null;
1055+
}
1056+
if (totalCount == 0 && _specialItem == null) {
1057+
return loadingIndicatorBuilder?.call(context, true) ??
1058+
Center(child: emptyIndicator(context));
10521059
}
10531060
// Then we use the [totalCount] to calculate placeholders we need.
10541061
final int placeholderCount;
@@ -1068,11 +1075,11 @@ class DefaultAssetPickerBuilderDelegate
10681075
final double dividedSpacing = itemSpacing / gridCount;
10691076
final double topPadding = context.topPadding + kToolbarHeight;
10701077

1071-
Widget _sliverGrid(BuildContext ctx, List<AssetEntity> assets) {
1078+
Widget _sliverGrid(BuildContext context, List<AssetEntity> assets) {
10721079
return SliverGrid(
10731080
delegate: SliverChildBuilderDelegate(
10741081
(_, int index) => Builder(
1075-
builder: (BuildContext c) {
1082+
builder: (BuildContext context) {
10761083
if (effectiveShouldRevertGrid) {
10771084
if (index < placeholderCount) {
10781085
return const SizedBox.shrink();
@@ -1082,15 +1089,21 @@ class DefaultAssetPickerBuilderDelegate
10821089
return MergeSemantics(
10831090
child: Directionality(
10841091
textDirection: Directionality.of(context),
1085-
child: assetGridItemBuilder(c, index, assets),
1092+
child: assetGridItemBuilder(
1093+
context,
1094+
index,
1095+
assets,
1096+
specialItem: _specialItem,
1097+
),
10861098
),
10871099
);
10881100
},
10891101
),
10901102
childCount: assetsGridItemCount(
1091-
context: ctx,
1103+
context: context,
10921104
assets: assets,
10931105
placeholderCount: placeholderCount,
1106+
specialItem: _specialItem,
10941107
),
10951108
findChildIndexCallback: (Key? key) {
10961109
if (key is ValueKey<String>) {
@@ -1181,17 +1194,13 @@ class DefaultAssetPickerBuilderDelegate
11811194
}
11821195

11831196
/// There are several conditions within this builder:
1184-
/// * Return [specialItemBuilder] while the current path is all and
1185-
/// [specialItemPosition] is not equal to [SpecialItemPosition.none].
11861197
/// * Return item builder according to the asset's type.
11871198
/// * [AssetType.audio] -> [audioItemBuilder]
11881199
/// * [AssetType.image], [AssetType.video] -> [imageAndVideoItemBuilder]
11891200
/// * Load more assets when the index reached at third line counting
11901201
/// backwards.
11911202
///
11921203
/// 资源构建有几个条件:
1193-
/// * 当前路径是全部资源且 [specialItemPosition] 不等于
1194-
/// [SpecialItemPosition.none] 时,将会通过 [specialItemBuilder] 构建内容。
11951204
/// * 根据资源类型返回对应类型的构建:
11961205
/// * [AssetType.audio] -> [audioItemBuilder] 音频类型
11971206
/// * [AssetType.image], [AssetType.video] -> [imageAndVideoItemBuilder]
@@ -1201,25 +1210,15 @@ class DefaultAssetPickerBuilderDelegate
12011210
Widget assetGridItemBuilder(
12021211
BuildContext context,
12031212
int index,
1204-
List<AssetEntity> currentAssets,
1205-
) {
1213+
List<AssetEntity> currentAssets, {
1214+
Widget? specialItem,
1215+
}) {
12061216
final int _length = currentAssets.length;
12071217
final AssetPathEntity? currentPathEntity =
12081218
context.select<DefaultAssetPickerProvider, AssetPathEntity?>(
12091219
(DefaultAssetPickerProvider p) => p.currentPath,
12101220
);
12111221

1212-
final Widget? specialItem;
1213-
if (specialItemPosition == SpecialItemPosition.none) {
1214-
specialItem = null;
1215-
} else {
1216-
specialItem = specialItemBuilder?.call(
1217-
context,
1218-
currentPathEntity,
1219-
_length,
1220-
);
1221-
}
1222-
12231222
if (specialItem != null) {
12241223
if ((index == 0 && specialItemPosition == SpecialItemPosition.prepend) ||
12251224
(index == _length &&
@@ -1236,23 +1235,14 @@ class DefaultAssetPickerBuilderDelegate
12361235
currentIndex = index;
12371236
}
12381237

1239-
if (specialItemPosition != SpecialItemPosition.none) {
1240-
final Widget? specialItem = specialItemBuilder?.call(
1241-
context,
1242-
currentPathEntity,
1243-
_length,
1244-
);
1245-
if (specialItem != null) {}
1246-
}
1247-
12481238
if (currentPathEntity == null) {
12491239
return const SizedBox.shrink();
12501240
}
12511241

1252-
if (index == _length - gridCount * 3 &&
1253-
context.select<DefaultAssetPickerProvider, bool>(
1254-
(DefaultAssetPickerProvider p) => p.hasMoreToLoad,
1255-
)) {
1242+
final bool hasMoreToLoad = context.select<DefaultAssetPickerProvider, bool>(
1243+
(DefaultAssetPickerProvider p) => p.hasMoreToLoad,
1244+
);
1245+
if (index == _length - gridCount * 3 && hasMoreToLoad) {
12561246
context.read<DefaultAssetPickerProvider>().loadMoreAssets();
12571247
}
12581248

@@ -1384,20 +1374,21 @@ class DefaultAssetPickerBuilderDelegate
13841374
required BuildContext context,
13851375
required List<AssetEntity> assets,
13861376
int placeholderCount = 0,
1377+
Widget? specialItem,
13871378
}) {
13881379
final AssetPathEntity? currentPathEntity =
13891380
context.select<DefaultAssetPickerProvider, AssetPathEntity?>(
13901381
(DefaultAssetPickerProvider p) => p.currentPath,
13911382
);
1383+
final int _length = assets.length + placeholderCount;
13921384

1393-
if (currentPathEntity == null &&
1394-
specialItemPosition != SpecialItemPosition.none) {
1395-
return 1;
1385+
// Return 1 if the [specialItem] build something.
1386+
if (currentPathEntity == null && specialItem != null) {
1387+
return placeholderCount + 1;
13961388
}
13971389

1398-
/// Return actual length if current path is all.
1399-
/// 如果当前目录是全部内容,则返回实际的内容数量。
1400-
final int _length = assets.length + placeholderCount;
1390+
// Return actual length if the current path is all.
1391+
// 如果当前目录是全部内容,则返回实际的内容数量。
14011392
if (currentPathEntity?.isAll != true) {
14021393
return _length;
14031394
}
@@ -1550,18 +1541,22 @@ class DefaultAssetPickerBuilderDelegate
15501541
);
15511542
}
15521543

1544+
Widget emptyIndicator(BuildContext context) {
1545+
return ScaleText(
1546+
textDelegate.emptyList,
1547+
maxScaleFactor: 1.5,
1548+
semanticsLabel: semanticsTextDelegate.emptyList,
1549+
);
1550+
}
1551+
15531552
@override
15541553
Widget loadingIndicator(BuildContext context) {
15551554
return Center(
15561555
child: Selector<DefaultAssetPickerProvider, bool>(
15571556
selector: (_, DefaultAssetPickerProvider p) => p.isAssetsEmpty,
1558-
builder: (_, bool isAssetsEmpty, __) {
1557+
builder: (BuildContext context, bool isAssetsEmpty, __) {
15591558
if (isAssetsEmpty) {
1560-
return ScaleText(
1561-
textDelegate.emptyList,
1562-
maxScaleFactor: 1.5,
1563-
semanticsLabel: semanticsTextDelegate.emptyList,
1564-
);
1559+
return emptyIndicator(context);
15651560
}
15661561
return PlatformProgressIndicator(
15671562
color: theme.iconTheme.color,
@@ -1725,6 +1720,9 @@ class DefaultAssetPickerBuilderDelegate
17251720
return UnconstrainedBox(
17261721
child: GestureDetector(
17271722
onTap: () {
1723+
if (provider.currentPath == null) {
1724+
return;
1725+
}
17281726
Feedback.forTap(context);
17291727
if (isPermissionLimited && provider.isAssetsEmpty) {
17301728
PhotoManager.presentLimited();

0 commit comments

Comments
 (0)