Skip to content

Commit 9497f37

Browse files
committed
✨ Add ability to select assets in any position of the picker
Fix #77 .
1 parent 8a6f6b3 commit 9497f37

File tree

3 files changed

+162
-82
lines changed

3 files changed

+162
-82
lines changed

lib/src/delegates/asset_picker_builder_delegate.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,8 @@ class DefaultAssetPickerBuilderDelegate
12311231
previewAssets: provider.currentAssets,
12321232
themeData: theme,
12331233
previewThumbSize: previewThumbSize,
1234+
selectedAssets: provider.selectedAssets,
1235+
selectorProvider: provider as DefaultAssetPickerProvider,
12341236
specialPickerType:
12351237
asset.type == AssetType.video ? specialPickerType : null,
12361238
);

lib/src/delegates/asset_picker_viewer_builder_delegate.dart

Lines changed: 126 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:flutter/services.dart';
1010
import 'package:extended_image/extended_image.dart';
1111

1212
import '../constants/constants.dart';
13+
import '../widget/builder/value_listenable_builder_2.dart';
1314
import '../widget/custom_checkbox.dart';
1415

1516
abstract class AssetPickerViewerBuilderDelegate<A, P> {
@@ -64,13 +65,23 @@ abstract class AssetPickerViewerBuilderDelegate<A, P> {
6465
/// 当前查看的索引
6566
int currentIndex;
6667

68+
/// Whether the viewer is under preview mode for selected assets.
69+
/// 当前是否处于查看已选中资源的模式
70+
late final bool isSelectedPreviewing = selectedAssets == previewAssets;
71+
6772
/// Getter for the current asset.
6873
/// 当前资源的Getter
6974
A get currentAsset => previewAssets.elementAt(currentIndex);
7075

71-
/// Height for bottom detail widget.
72-
/// 底部详情部件的高度
73-
double get bottomDetailHeight => 140.0;
76+
/// Height for bottom preview widget.
77+
/// 底栏预览部件的高度
78+
double get bottomPreviewHeight => 90.0;
79+
80+
/// Height for bottom bar widget.
81+
/// 底栏部件的高度
82+
double get bottomBarHeight => 50.0;
83+
84+
double get bottomDetailHeight => bottomPreviewHeight + bottomBarHeight;
7485

7586
/// Whether the current platform is Apple OS.
7687
/// 当前平台是否为苹果系列系统
@@ -89,6 +100,36 @@ abstract class AssetPickerViewerBuilderDelegate<A, P> {
89100
pageStreamController.close();
90101
}
91102

103+
/// The length getter for selected assets currently.
104+
/// 当前选中的资源的长度获取
105+
int get selectedCount => selectedAssets?.length ?? 0;
106+
107+
/// Construct a notifier to notify
108+
/// whether if a new asset is selected or unselected.
109+
/// 构造一个通知器,在新资源选中或取消选中时通知。
110+
late final ValueNotifier<int> selectedNotifier =
111+
ValueNotifier<int>(selectedCount);
112+
113+
void unSelectAsset(A entity) {
114+
provider?.unSelectAssetEntity(entity);
115+
if (!isSelectedPreviewing) {
116+
selectedAssets?.remove(entity);
117+
}
118+
if (selectedCount != selectedNotifier.value) {
119+
selectedNotifier.value = selectedCount;
120+
}
121+
}
122+
123+
void selectAsset(A entity) {
124+
provider?.selectAssetEntity(entity);
125+
if (!isSelectedPreviewing) {
126+
selectedAssets?.add(entity);
127+
}
128+
if (selectedCount != selectedNotifier.value) {
129+
selectedNotifier.value = selectedCount;
130+
}
131+
}
132+
92133
/// Split page builder according to type of asset.
93134
/// 根据资源类型使用不同的构建页
94135
Widget assetPageBuilder(BuildContext context, int index);
@@ -200,7 +241,8 @@ class DefaultAssetPickerViewerBuilderDelegate
200241

201242
/// [PageController] for assets preview [PageView].
202243
/// 查看图片资源的页面控制器
203-
late final PageController pageController;
244+
late final PageController pageController =
245+
PageController(initialPage: currentIndex);
204246

205247
/// Whether detail widgets displayed.
206248
/// 详情部件是否显示
@@ -220,7 +262,6 @@ class DefaultAssetPickerViewerBuilderDelegate
220262
parent: _doubleTapAnimationController,
221263
curve: Curves.easeInOut,
222264
);
223-
pageController = PageController(initialPage: currentIndex);
224265
}
225266

226267
@override
@@ -339,6 +380,74 @@ class DefaultAssetPickerViewerBuilderDelegate
339380
);
340381
}
341382

383+
@override
384+
Widget bottomDetailBuilder(BuildContext context) {
385+
final Color _backgroundColor = themeData.canvasColor.withOpacity(0.85);
386+
return ValueListenableBuilder2<bool, int>(
387+
firstNotifier: isDisplayingDetail,
388+
secondNotifier: selectedNotifier,
389+
builder: (_, bool value, int count, Widget? child) => AnimatedPositioned(
390+
duration: kThemeAnimationDuration,
391+
curve: Curves.easeInOut,
392+
bottom: value ? 0.0 : -(Screens.bottomSafeHeight + bottomDetailHeight),
393+
left: 0.0,
394+
right: 0.0,
395+
height: Screens.bottomSafeHeight + bottomDetailHeight,
396+
child: child!,
397+
),
398+
child: Container(
399+
padding: EdgeInsets.only(bottom: Screens.bottomSafeHeight),
400+
child: ChangeNotifierProvider<
401+
AssetPickerViewerProvider<AssetEntity>>.value(
402+
value: provider!,
403+
child: Column(
404+
mainAxisSize: MainAxisSize.min,
405+
mainAxisAlignment: MainAxisAlignment.end,
406+
children: <Widget>[
407+
ValueListenableBuilder<int>(
408+
valueListenable: selectedNotifier,
409+
builder: (_, int count, __) => Container(
410+
width: count > 0 ? double.maxFinite : 0,
411+
height: 90,
412+
color: _backgroundColor,
413+
child: ListView.builder(
414+
scrollDirection: Axis.horizontal,
415+
padding: const EdgeInsets.symmetric(horizontal: 5.0),
416+
itemCount: count,
417+
itemBuilder: bottomDetailItemBuilder,
418+
),
419+
),
420+
),
421+
Container(
422+
height: 50,
423+
padding: const EdgeInsets.symmetric(horizontal: 20.0),
424+
decoration: BoxDecoration(
425+
border: Border(
426+
top: BorderSide(
427+
width: 1.0,
428+
color: themeData.dividerColor,
429+
),
430+
),
431+
color: _backgroundColor,
432+
),
433+
child: Row(
434+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
435+
children: <Widget>[
436+
const Spacer(),
437+
if (isAppleOS && provider != null)
438+
confirmButton(context)
439+
else
440+
selectButton(context),
441+
],
442+
),
443+
),
444+
],
445+
),
446+
),
447+
),
448+
);
449+
}
450+
342451
@override
343452
Widget bottomDetailItemBuilder(BuildContext context, int index) {
344453
return Padding(
@@ -350,7 +459,7 @@ class DefaultAssetPickerViewerBuilderDelegate
350459
stream: pageStreamController.stream,
351460
builder: (_, AsyncSnapshot<int> snapshot) {
352461
final AssetEntity asset = selectedAssets!.elementAt(index);
353-
final bool isViewing = index == snapshot.data!;
462+
final bool isViewing = previewAssets[snapshot.data!] == asset;
354463
final Widget _item = () {
355464
switch (asset.type) {
356465
case AssetType.image:
@@ -410,62 +519,6 @@ class DefaultAssetPickerViewerBuilderDelegate
410519
);
411520
}
412521

413-
@override
414-
Widget bottomDetailBuilder(BuildContext context) {
415-
return ValueListenableBuilder<bool>(
416-
valueListenable: isDisplayingDetail,
417-
builder: (_, bool value, Widget? child) => AnimatedPositioned(
418-
duration: kThemeAnimationDuration,
419-
curve: Curves.easeInOut,
420-
bottom: value ? 0.0 : -(Screens.bottomSafeHeight + bottomDetailHeight),
421-
left: 0.0,
422-
right: 0.0,
423-
height: Screens.bottomSafeHeight + bottomDetailHeight,
424-
child: child!,
425-
),
426-
child: Container(
427-
padding: EdgeInsets.only(bottom: Screens.bottomSafeHeight),
428-
color: themeData.canvasColor.withOpacity(0.85),
429-
child: ChangeNotifierProvider<
430-
AssetPickerViewerProvider<AssetEntity>>.value(
431-
value: provider!,
432-
child: Column(
433-
children: <Widget>[
434-
SizedBox(
435-
height: 90.0,
436-
child: ListView.builder(
437-
scrollDirection: Axis.horizontal,
438-
padding: const EdgeInsets.symmetric(horizontal: 5.0),
439-
itemCount: selectedAssets!.length,
440-
itemBuilder: bottomDetailItemBuilder,
441-
),
442-
),
443-
Container(
444-
height: 1.0,
445-
color: themeData.dividerColor,
446-
),
447-
Expanded(
448-
child: Padding(
449-
padding: const EdgeInsets.symmetric(horizontal: 20.0),
450-
child: Row(
451-
mainAxisAlignment: MainAxisAlignment.spaceBetween,
452-
children: <Widget>[
453-
const Spacer(),
454-
if (isAppleOS && provider != null)
455-
confirmButton(context)
456-
else
457-
selectButton(context),
458-
],
459-
),
460-
),
461-
),
462-
],
463-
),
464-
),
465-
),
466-
);
467-
}
468-
469522
/// AppBar widget.
470523
/// 顶栏部件
471524
Widget appBar(BuildContext context) {
@@ -537,14 +590,7 @@ class DefaultAssetPickerViewerBuilderDelegate
537590
}(),
538591
height: 32.0,
539592
padding: const EdgeInsets.symmetric(horizontal: 12.0),
540-
color: () {
541-
if (specialPickerType == SpecialPickerType.wechatMoment) {
542-
return themeData.colorScheme.secondary;
543-
}
544-
return provider!.isSelectedNotEmpty
545-
? themeData.colorScheme.secondary
546-
: themeData.dividerColor;
547-
}(),
593+
color: themeData.colorScheme.secondary,
548594
shape: RoundedRectangleBorder(
549595
borderRadius: BorderRadius.circular(3.0),
550596
),
@@ -562,14 +608,7 @@ class DefaultAssetPickerViewerBuilderDelegate
562608
return Constants.textDelegate.confirm;
563609
}(),
564610
style: TextStyle(
565-
color: () {
566-
if (specialPickerType == SpecialPickerType.wechatMoment) {
567-
return themeData.textTheme.bodyText1?.color;
568-
}
569-
return provider!.isSelectedNotEmpty
570-
? themeData.textTheme.bodyText1?.color
571-
: themeData.textTheme.caption?.color;
572-
}(),
611+
color: themeData.textTheme.bodyText1?.color,
573612
fontSize: 17.0,
574613
fontWeight: FontWeight.normal,
575614
),
@@ -581,6 +620,11 @@ class DefaultAssetPickerViewerBuilderDelegate
581620
}
582621
if (provider!.isSelectedNotEmpty) {
583622
Navigator.of(context).pop(provider.currentlySelectedAssets);
623+
} else {
624+
selectAsset(currentAsset);
625+
Navigator.of(context).pop(
626+
selectedAssets ?? <AssetEntity>[currentAsset],
627+
);
584628
}
585629
},
586630
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
@@ -599,9 +643,9 @@ class DefaultAssetPickerViewerBuilderDelegate
599643
behavior: HitTestBehavior.opaque,
600644
onTap: () {
601645
if (isSelected) {
602-
provider?.unSelectAssetEntity(asset);
646+
unSelectAsset(asset);
603647
} else {
604-
provider?.selectAssetEntity(asset);
648+
selectAsset(asset);
605649
}
606650
},
607651
child: AnimatedContainer(
@@ -640,9 +684,9 @@ class DefaultAssetPickerViewerBuilderDelegate
640684
),
641685
onChanged: (bool? value) {
642686
if (isSelected) {
643-
provider?.unSelectAssetEntity(asset);
687+
unSelectAsset(asset);
644688
} else {
645-
provider?.selectAssetEntity(asset);
689+
selectAsset(asset);
646690
}
647691
},
648692
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
///
2+
/// [Author] Alex (https://github.com/AlexV525)
3+
/// [Date] 2021-03-27 18:53
4+
///
5+
import 'package:flutter/material.dart';
6+
7+
class ValueListenableBuilder2<A, B> extends StatelessWidget {
8+
const ValueListenableBuilder2({
9+
Key? key,
10+
required this.firstNotifier,
11+
required this.secondNotifier,
12+
required this.builder,
13+
this.child,
14+
}) : super(key: key);
15+
16+
final ValueNotifier<A> firstNotifier;
17+
final ValueNotifier<B> secondNotifier;
18+
final Widget Function(BuildContext, A, B, Widget?) builder;
19+
final Widget? child;
20+
21+
@override
22+
Widget build(BuildContext context) {
23+
return ValueListenableBuilder<A>(
24+
valueListenable: firstNotifier,
25+
builder: (_, A first, __) => ValueListenableBuilder<B>(
26+
valueListenable: secondNotifier,
27+
builder: (BuildContext context, B second, Widget? w) {
28+
return builder(context, first, second, w);
29+
},
30+
child: child,
31+
),
32+
);
33+
}
34+
}

0 commit comments

Comments
 (0)