@@ -10,6 +10,7 @@ import 'package:flutter/services.dart';
10
10
import 'package:extended_image/extended_image.dart' ;
11
11
12
12
import '../constants/constants.dart' ;
13
+ import '../widget/builder/value_listenable_builder_2.dart' ;
13
14
import '../widget/custom_checkbox.dart' ;
14
15
15
16
abstract class AssetPickerViewerBuilderDelegate <A , P > {
@@ -64,13 +65,23 @@ abstract class AssetPickerViewerBuilderDelegate<A, P> {
64
65
/// 当前查看的索引
65
66
int currentIndex;
66
67
68
+ /// Whether the viewer is under preview mode for selected assets.
69
+ /// 当前是否处于查看已选中资源的模式
70
+ late final bool isSelectedPreviewing = selectedAssets == previewAssets;
71
+
67
72
/// Getter for the current asset.
68
73
/// 当前资源的Getter
69
74
A get currentAsset => previewAssets.elementAt (currentIndex);
70
75
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;
74
85
75
86
/// Whether the current platform is Apple OS.
76
87
/// 当前平台是否为苹果系列系统
@@ -89,6 +100,36 @@ abstract class AssetPickerViewerBuilderDelegate<A, P> {
89
100
pageStreamController.close ();
90
101
}
91
102
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
+
92
133
/// Split page builder according to type of asset.
93
134
/// 根据资源类型使用不同的构建页
94
135
Widget assetPageBuilder (BuildContext context, int index);
@@ -200,7 +241,8 @@ class DefaultAssetPickerViewerBuilderDelegate
200
241
201
242
/// [PageController] for assets preview [PageView] .
202
243
/// 查看图片资源的页面控制器
203
- late final PageController pageController;
244
+ late final PageController pageController =
245
+ PageController (initialPage: currentIndex);
204
246
205
247
/// Whether detail widgets displayed.
206
248
/// 详情部件是否显示
@@ -220,7 +262,6 @@ class DefaultAssetPickerViewerBuilderDelegate
220
262
parent: _doubleTapAnimationController,
221
263
curve: Curves .easeInOut,
222
264
);
223
- pageController = PageController (initialPage: currentIndex);
224
265
}
225
266
226
267
@override
@@ -339,6 +380,74 @@ class DefaultAssetPickerViewerBuilderDelegate
339
380
);
340
381
}
341
382
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
+
342
451
@override
343
452
Widget bottomDetailItemBuilder (BuildContext context, int index) {
344
453
return Padding (
@@ -350,7 +459,7 @@ class DefaultAssetPickerViewerBuilderDelegate
350
459
stream: pageStreamController.stream,
351
460
builder: (_, AsyncSnapshot <int > snapshot) {
352
461
final AssetEntity asset = selectedAssets! .elementAt (index);
353
- final bool isViewing = index == snapshot.data! ;
462
+ final bool isViewing = previewAssets[ snapshot.data! ] == asset ;
354
463
final Widget _item = () {
355
464
switch (asset.type) {
356
465
case AssetType .image:
@@ -410,62 +519,6 @@ class DefaultAssetPickerViewerBuilderDelegate
410
519
);
411
520
}
412
521
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
-
469
522
/// AppBar widget.
470
523
/// 顶栏部件
471
524
Widget appBar (BuildContext context) {
@@ -537,14 +590,7 @@ class DefaultAssetPickerViewerBuilderDelegate
537
590
}(),
538
591
height: 32.0 ,
539
592
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,
548
594
shape: RoundedRectangleBorder (
549
595
borderRadius: BorderRadius .circular (3.0 ),
550
596
),
@@ -562,14 +608,7 @@ class DefaultAssetPickerViewerBuilderDelegate
562
608
return Constants .textDelegate.confirm;
563
609
}(),
564
610
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,
573
612
fontSize: 17.0 ,
574
613
fontWeight: FontWeight .normal,
575
614
),
@@ -581,6 +620,11 @@ class DefaultAssetPickerViewerBuilderDelegate
581
620
}
582
621
if (provider! .isSelectedNotEmpty) {
583
622
Navigator .of (context).pop (provider.currentlySelectedAssets);
623
+ } else {
624
+ selectAsset (currentAsset);
625
+ Navigator .of (context).pop (
626
+ selectedAssets ?? < AssetEntity > [currentAsset],
627
+ );
584
628
}
585
629
},
586
630
materialTapTargetSize: MaterialTapTargetSize .shrinkWrap,
@@ -599,9 +643,9 @@ class DefaultAssetPickerViewerBuilderDelegate
599
643
behavior: HitTestBehavior .opaque,
600
644
onTap: () {
601
645
if (isSelected) {
602
- provider ? . unSelectAssetEntity (asset);
646
+ unSelectAsset (asset);
603
647
} else {
604
- provider ? . selectAssetEntity (asset);
648
+ selectAsset (asset);
605
649
}
606
650
},
607
651
child: AnimatedContainer (
@@ -640,9 +684,9 @@ class DefaultAssetPickerViewerBuilderDelegate
640
684
),
641
685
onChanged: (bool ? value) {
642
686
if (isSelected) {
643
- provider ? . unSelectAssetEntity (asset);
687
+ unSelectAsset (asset);
644
688
} else {
645
- provider ? . selectAssetEntity (asset);
689
+ selectAsset (asset);
646
690
}
647
691
},
648
692
materialTapTargetSize: MaterialTapTargetSize .shrinkWrap,
0 commit comments