Skip to content

Commit 20d8050

Browse files
committed
🚧 Refactoring grid layout for iOS reverted grid
1 parent 0cbd815 commit 20d8050

File tree

3 files changed

+227
-68
lines changed

3 files changed

+227
-68
lines changed

example/lib/customs/pickers/directory_file_asset_picker.dart

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,63 @@ class FileAssetPickerBuilder
532532
);
533533
}
534534

535+
@override
536+
Widget assetsGridBuilder(BuildContext context) {
537+
return ColoredBox(
538+
color: theme.canvasColor,
539+
child: Selector<FileAssetPickerProvider, List<File>>(
540+
selector: (_, FileAssetPickerProvider provider) =>
541+
provider.currentAssets,
542+
builder: (_, List<File> currentAssets, __) => CustomScrollView(
543+
controller: gridScrollController,
544+
slivers: <Widget>[
545+
if (isAppleOS)
546+
SliverToBoxAdapter(
547+
child: SizedBox(
548+
height: Screens.topSafeHeight + kToolbarHeight,
549+
),
550+
),
551+
SliverGrid(
552+
delegate: SliverChildBuilderDelegate(
553+
(_, int index) => Builder(
554+
builder: (BuildContext c) => assetGridItemBuilder(
555+
c,
556+
index,
557+
currentAssets,
558+
),
559+
),
560+
childCount: assetsGridItemCount(
561+
context: _,
562+
assets: currentAssets,
563+
),
564+
findChildIndexCallback: (Key? key) {
565+
if (key is ValueKey<String>) {
566+
return findChildIndexBuilder(
567+
id: key.value,
568+
assets: currentAssets,
569+
);
570+
}
571+
return null;
572+
},
573+
),
574+
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
575+
crossAxisCount: gridCount,
576+
mainAxisSpacing: itemSpacing,
577+
crossAxisSpacing: itemSpacing,
578+
),
579+
),
580+
if (isAppleOS)
581+
SliverToBoxAdapter(
582+
child: SizedBox(
583+
height: Screens.bottomSafeHeight + bottomActionBarHeight,
584+
),
585+
),
586+
],
587+
),
588+
),
589+
);
590+
}
591+
535592
@override
536593
Widget assetGridItemBuilder(
537594
BuildContext context,
@@ -566,18 +623,22 @@ class FileAssetPickerBuilder
566623
}
567624

568625
@override
569-
int assetsGridItemCount(BuildContext context, List<File> currentAssets) {
626+
int assetsGridItemCount({
627+
required BuildContext context,
628+
required List<File> assets,
629+
int placeholderCount = 0,
630+
}) {
570631
int length;
571632
switch (specialItemPosition) {
572633
case SpecialItemPosition.none:
573-
length = currentAssets.length;
634+
length = assets.length;
574635
break;
575636
case SpecialItemPosition.prepend:
576637
case SpecialItemPosition.append:
577-
length = currentAssets.length + 1;
638+
length = assets.length + 1;
578639
break;
579640
}
580-
return length;
641+
return length + placeholderCount;
581642
}
582643

583644
@override
@@ -1046,8 +1107,12 @@ class FileAssetPickerBuilder
10461107
}
10471108

10481109
@override
1049-
int findChildIndexBuilder(String id, List<File> currentAssets) {
1050-
return currentAssets.indexWhere((File file) => file.path == id);
1110+
int findChildIndexBuilder({
1111+
required String id,
1112+
required List<File> assets,
1113+
int placeholderCount = 0,
1114+
}) {
1115+
return assets.indexWhere((File file) => file.path == id);
10511116
}
10521117
}
10531118

lib/src/delegates/asset_picker_builder_delegate.dart

Lines changed: 155 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -269,68 +269,42 @@ abstract class AssetPickerBuilderDelegate<A, P> {
269269
);
270270
}
271271

272+
/// The effective direction for the assets grid.
273+
/// 网格实际的方向
274+
///
275+
/// By default, the direction will be reversed if it's iOS/macOS.
276+
/// 默认情况下,在 iOS/macOS 上方向会反向。
277+
TextDirection effectiveGridDirection(BuildContext context) {
278+
final TextDirection _od = Directionality.of(context);
279+
if (isAppleOS) {
280+
if (_od == TextDirection.ltr) {
281+
return TextDirection.rtl;
282+
}
283+
return TextDirection.ltr;
284+
}
285+
return _od;
286+
}
287+
272288
/// The main grid view builder for assets.
273289
/// 主要的资源查看网格部件
274-
Widget assetsGridBuilder(BuildContext context) {
275-
return ColoredBox(
276-
color: theme.canvasColor,
277-
child: Selector<AssetPickerProvider<A, P>, List<A>>(
278-
selector: (_, AssetPickerProvider<A, P> provider) =>
279-
provider.currentAssets,
280-
builder: (_, List<A> currentAssets, __) => CustomScrollView(
281-
controller: gridScrollController,
282-
slivers: <Widget>[
283-
if (isAppleOS)
284-
SliverToBoxAdapter(
285-
child: SizedBox(
286-
height: Screens.topSafeHeight + kToolbarHeight,
287-
),
288-
),
289-
SliverGrid(
290-
delegate: SliverChildBuilderDelegate(
291-
(_, int index) => Builder(
292-
builder: (BuildContext c) => assetGridItemBuilder(
293-
c,
294-
index,
295-
currentAssets,
296-
),
297-
),
298-
childCount: assetsGridItemCount(_, currentAssets),
299-
findChildIndexCallback: (Key? key) {
300-
if (key is ValueKey<String>) {
301-
return findChildIndexBuilder(
302-
key.value,
303-
currentAssets,
304-
);
305-
}
306-
return null;
307-
},
308-
),
309-
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
310-
crossAxisCount: gridCount,
311-
mainAxisSpacing: itemSpacing,
312-
crossAxisSpacing: itemSpacing,
313-
),
314-
),
315-
if (isAppleOS)
316-
SliverToBoxAdapter(
317-
child: SizedBox(
318-
height: Screens.bottomSafeHeight + bottomActionBarHeight,
319-
),
320-
),
321-
],
322-
),
323-
),
324-
);
325-
}
290+
Widget assetsGridBuilder(BuildContext context);
326291

327292
/// Indicates how would the grid found a reusable [RenderObject] through [id].
328293
/// 为 Grid 布局指示如何找到可复用的 [RenderObject]
329-
int? findChildIndexBuilder(String id, List<A> currentAssets) => null;
294+
int? findChildIndexBuilder({
295+
required String id,
296+
required List<A> assets,
297+
int placeholderCount = 0,
298+
}) =>
299+
null;
330300

331301
/// The function which return items count for the assets' grid.
332302
/// 为资源列表提供内容数量计算的方法
333-
int assetsGridItemCount(BuildContext context, List<A> currentAssets);
303+
int assetsGridItemCount({
304+
required BuildContext context,
305+
required List<A> assets,
306+
int placeholderCount = 0,
307+
});
334308

335309
/// The item builder for the assets' grid.
336310
/// 资源列表项的构建
@@ -680,6 +654,8 @@ class DefaultAssetPickerBuilderDelegate
680654
/// 资源的预览是否启用
681655
bool get isPreviewEnabled => specialPickerType != SpecialPickerType.noPreview;
682656

657+
final GlobalKey _gridRevertKey = GlobalKey();
658+
683659
@override
684660
Widget androidLayout(BuildContext context) {
685661
return FixedAppBarWrapper(
@@ -779,6 +755,118 @@ class DefaultAssetPickerBuilderDelegate
779755
);
780756
}
781757

758+
@override
759+
Widget assetsGridBuilder(BuildContext context) {
760+
// First, we need the count of the assets.
761+
final int totalCount = provider.currentPathEntity!.assetCount;
762+
// Then we use the total count to calculate how many placeholders we need.
763+
final int _placeholderCount;
764+
if (isAppleOS) {
765+
_placeholderCount =
766+
totalCount % gridCount == 0 ? 0 : gridCount - totalCount % gridCount;
767+
} else {
768+
_placeholderCount = 0;
769+
}
770+
771+
Widget _sliverGrid(BuildContext c, List<AssetEntity> assets) {
772+
return SliverGrid(
773+
delegate: SliverChildBuilderDelegate(
774+
(_, int index) => Builder(
775+
builder: (BuildContext c) {
776+
if (isAppleOS) {
777+
if (index < _placeholderCount) {
778+
return const SizedBox.shrink();
779+
}
780+
index -= _placeholderCount;
781+
}
782+
return Directionality(
783+
textDirection: Directionality.of(context),
784+
child: assetGridItemBuilder(c, index, assets),
785+
);
786+
},
787+
),
788+
childCount: assetsGridItemCount(
789+
context: c,
790+
assets: assets,
791+
placeholderCount: _placeholderCount,
792+
),
793+
findChildIndexCallback: (Key? key) {
794+
if (key is ValueKey<String>) {
795+
return findChildIndexBuilder(
796+
id: key.value,
797+
assets: assets,
798+
placeholderCount: _placeholderCount,
799+
);
800+
}
801+
return null;
802+
},
803+
),
804+
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
805+
crossAxisCount: gridCount,
806+
mainAxisSpacing: itemSpacing,
807+
crossAxisSpacing: itemSpacing,
808+
),
809+
);
810+
}
811+
812+
return LayoutBuilder(
813+
builder: (BuildContext c, BoxConstraints constraints) {
814+
final double topPadding = c.mediaQuery.padding.top + kToolbarHeight;
815+
final double bottomPadding =
816+
c.mediaQuery.padding.bottom + bottomActionBarHeight;
817+
final double verticalPadding = topPadding + bottomPadding;
818+
819+
final int _count = totalCount + _placeholderCount;
820+
final double _wWidth = constraints.maxWidth;
821+
final double _wHeight = constraints.maxHeight - topPadding;
822+
final double _itemSize = _wWidth / gridCount;
823+
final double anchor;
824+
if (_count <= gridCount) {
825+
anchor = verticalPadding / _wHeight;
826+
} else {
827+
anchor = math.min(
828+
_count ~/ gridCount * _itemSize / _wHeight,
829+
1,
830+
);
831+
}
832+
833+
return Directionality(
834+
textDirection: effectiveGridDirection(context),
835+
child: ColoredBox(
836+
color: theme.canvasColor,
837+
child: Selector<DefaultAssetPickerProvider, List<AssetEntity>>(
838+
selector: (_, DefaultAssetPickerProvider provider) =>
839+
provider.currentAssets,
840+
builder: (BuildContext c, List<AssetEntity> assets, __) {
841+
return CustomScrollView(
842+
physics: const AlwaysScrollableScrollPhysics(),
843+
controller: gridScrollController,
844+
anchor: isAppleOS ? anchor : 0,
845+
center: isAppleOS ? _gridRevertKey : null,
846+
slivers: <Widget>[
847+
if (isAppleOS)
848+
SliverToBoxAdapter(
849+
child: SizedBox(
850+
height:
851+
context.mediaQuery.padding.top + kToolbarHeight,
852+
),
853+
),
854+
_sliverGrid(c, assets),
855+
if (isAppleOS)
856+
SliverToBoxAdapter(
857+
key: _gridRevertKey,
858+
child: const SizedBox.shrink(),
859+
),
860+
],
861+
);
862+
},
863+
),
864+
),
865+
);
866+
},
867+
);
868+
}
869+
782870
/// There are several conditions within this builder:
783871
/// * Return [specialItemBuilder] while the current path is all and
784872
/// [specialItemPosition] is not equal to [SpecialItemPosition.none].
@@ -873,19 +961,25 @@ class DefaultAssetPickerBuilderDelegate
873961
}
874962

875963
@override
876-
int findChildIndexBuilder(String id, List<AssetEntity> currentAssets) {
877-
int index = currentAssets.indexWhere((AssetEntity e) => e.id == id);
964+
int findChildIndexBuilder({
965+
required String id,
966+
required List<AssetEntity> assets,
967+
int placeholderCount = 0,
968+
}) {
969+
int index = assets.indexWhere((AssetEntity e) => e.id == id);
878970
if (specialItemPosition == SpecialItemPosition.prepend) {
879971
index += 1;
880972
}
973+
index += placeholderCount;
881974
return index;
882975
}
883976

884977
@override
885-
int assetsGridItemCount(
886-
BuildContext context,
887-
List<AssetEntity> currentAssets,
888-
) {
978+
int assetsGridItemCount({
979+
required BuildContext context,
980+
required List<AssetEntity> assets,
981+
int placeholderCount = 0,
982+
}) {
889983
final AssetPathEntity? currentPathEntity =
890984
context.select<DefaultAssetPickerProvider, AssetPathEntity?>(
891985
(DefaultAssetPickerProvider p) => p.currentPathEntity,
@@ -898,7 +992,7 @@ class DefaultAssetPickerBuilderDelegate
898992

899993
/// Return actual length if current path is all.
900994
/// 如果当前目录是全部内容,则返回实际的内容数量。
901-
final int _length = currentAssets.length;
995+
final int _length = assets.length + placeholderCount;
902996
if (!currentPathEntity!.isAll) {
903997
return _length;
904998
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ dependencies:
1212
sdk: flutter
1313

1414
extended_image: ^4.1.0
15-
photo_manager: ^1.2.2
15+
photo_manager: ^1.2.4
1616
provider: ^5.0.0
1717
video_player: ^2.1.6

0 commit comments

Comments
 (0)