4
4
///
5
5
import 'dart:async' ;
6
6
import 'dart:io' ;
7
+ import 'dart:math' as math;
7
8
8
9
import 'package:flutter/material.dart' ;
9
10
import 'package:flutter/services.dart' ;
@@ -47,13 +48,17 @@ abstract class AssetPickerViewerBuilderDelegate<A, P> {
47
48
/// [StreamController] for viewing page index update.
48
49
/// 用于更新当前正在浏览的资源页码的流控制器
49
50
///
50
- /// The main purpose is narrow down build parts when page index is changing,
51
+ /// The main purpose is to narrow down build parts when index is changing,
51
52
/// prevent widely [setState] and causing other widgets rebuild.
52
53
/// 使用 [StreamController] 的主要目的是缩小页码变化时构建组件的范围,
53
54
/// 防止滥用 [setState] 导致其他部件重新构建。
54
55
final StreamController <int > pageStreamController =
55
56
StreamController <int >.broadcast ();
56
57
58
+ /// The [ScrollController] for the previewing assets list.
59
+ /// 正在预览的资源的 [ScrollController]
60
+ final ScrollController previewingListController = ScrollController ();
61
+
57
62
/// The [State] for a viewer.
58
63
/// 预览器的状态实例
59
64
late final AssetPickerViewerState <A , P > viewerState;
@@ -103,12 +108,18 @@ abstract class AssetPickerViewerBuilderDelegate<A, P> {
103
108
/// 保留一个 dispose 方法与 [State] 同步。
104
109
void dispose () {
105
110
pageStreamController.close ();
111
+ previewingListController.dispose ();
112
+ selectedNotifier.dispose ();
106
113
}
107
114
108
115
/// The length getter for selected assets currently.
109
116
/// 当前选中的资源的长度获取
110
117
int get selectedCount => selectedAssets? .length ?? 0 ;
111
118
119
+ /// 是否已经选择了最大数量的资源
120
+ bool get selectedMaximumAssets =>
121
+ selectedAssets != null && selectedAssets! .length == maxAssets;
122
+
112
123
/// Construct a notifier to notify
113
124
/// whether if a new asset is selected or unselected.
114
125
/// 构造一个通知器,在新资源选中或取消选中时通知。
@@ -289,6 +300,7 @@ class DefaultAssetPickerViewerBuilderDelegate
289
300
@override
290
301
void dispose () {
291
302
_doubleTapAnimationController.dispose ();
303
+ pageController.dispose ();
292
304
super .dispose ();
293
305
}
294
306
@@ -456,18 +468,20 @@ class DefaultAssetPickerViewerBuilderDelegate
456
468
valueListenable: selectedNotifier,
457
469
builder: (_, int count, __) => Container (
458
470
width: count > 0 ? double .maxFinite : 0 ,
459
- height: 90 ,
471
+ height: bottomPreviewHeight ,
460
472
color: _backgroundColor,
461
473
child: ListView .builder (
474
+ controller: previewingListController,
462
475
scrollDirection: Axis .horizontal,
463
476
padding: const EdgeInsets .symmetric (horizontal: 5.0 ),
477
+ physics: const ClampingScrollPhysics (),
464
478
itemCount: count,
465
479
itemBuilder: bottomDetailItemBuilder,
466
480
),
467
481
),
468
482
),
469
483
Container (
470
- height: 50 ,
484
+ height: bottomBarHeight ,
471
485
padding: const EdgeInsets .symmetric (horizontal: 20.0 ),
472
486
decoration: BoxDecoration (
473
487
border: Border (
@@ -498,8 +512,12 @@ class DefaultAssetPickerViewerBuilderDelegate
498
512
499
513
@override
500
514
Widget bottomDetailItemBuilder (BuildContext context, int index) {
515
+ const double padding = 8.0 ;
501
516
return Padding (
502
- padding: const EdgeInsets .symmetric (horizontal: 8.0 , vertical: 16.0 ),
517
+ padding: const EdgeInsets .symmetric (
518
+ horizontal: padding,
519
+ vertical: padding * 2 ,
520
+ ),
503
521
child: AspectRatio (
504
522
aspectRatio: 1.0 ,
505
523
child: StreamBuilder <int >(
@@ -522,9 +540,24 @@ class DefaultAssetPickerViewerBuilderDelegate
522
540
}();
523
541
return GestureDetector (
524
542
onTap: () {
543
+ if (pageController.page == index.toDouble ()) {
544
+ return ;
545
+ }
546
+ final int page;
525
547
if (previewAssets != selectedAssets) {
526
- pageController.jumpToPage (previewAssets.indexOf (asset));
548
+ page = previewAssets.indexOf (asset);
549
+ } else {
550
+ page = index;
527
551
}
552
+ pageController.jumpToPage (page);
553
+ final double offset =
554
+ (index - 0.5 ) * (bottomPreviewHeight - padding * 3 ) -
555
+ context.mediaQuery.size.width / 4 ;
556
+ previewingListController.animateTo (
557
+ math.max (0 , offset),
558
+ curve: Curves .ease,
559
+ duration: kThemeChangeDuration,
560
+ );
528
561
},
529
562
child: Selector <AssetPickerViewerProvider <AssetEntity >?,
530
563
List <AssetEntity >?>(
@@ -548,7 +581,7 @@ class DefaultAssetPickerViewerBuilderDelegate
548
581
border: isViewing
549
582
? Border .all (
550
583
color: themeData.colorScheme.secondary,
551
- width: 2.0 ,
584
+ width: 3 ,
552
585
)
553
586
: null ,
554
587
color: isSelected
@@ -691,6 +724,9 @@ class DefaultAssetPickerViewerBuilderDelegate
691
724
/// Select button for apple OS.
692
725
/// 苹果系列系统的选择按钮
693
726
Widget _appleOSSelectButton (bool isSelected, AssetEntity asset) {
727
+ if (! isSelected && selectedMaximumAssets) {
728
+ return const SizedBox .shrink ();
729
+ }
694
730
return Padding (
695
731
padding: const EdgeInsetsDirectional .only (end: 10.0 ),
696
732
child: GestureDetector (
@@ -712,17 +748,7 @@ class DefaultAssetPickerViewerBuilderDelegate
712
748
color: isSelected ? themeData.buttonColor : null ,
713
749
shape: BoxShape .circle,
714
750
),
715
- child: Center (
716
- child: isSelected
717
- ? Text (
718
- '${selectedAssets !.indexOf (asset ) + 1 }' ,
719
- style: const TextStyle (
720
- fontSize: 16.0 ,
721
- fontWeight: FontWeight .bold,
722
- ),
723
- )
724
- : const Icon (Icons .check, size: 20.0 ),
725
- ),
751
+ child: const Center (child: Icon (Icons .check, size: 20.0 )),
726
752
),
727
753
),
728
754
);
0 commit comments