@@ -29,6 +29,8 @@ class AssetPicker extends StatelessWidget {
29
29
Color themeColor,
30
30
TextDelegate textDelegate,
31
31
this .specialPickerType,
32
+ this .customItemPosition = CustomItemPosition .none,
33
+ this .customItemBuilder,
32
34
}) : assert (
33
35
provider != null ,
34
36
'AssetPickerProvider must be provided and not null.' ,
@@ -75,6 +77,14 @@ class AssetPicker extends StatelessWidget {
75
77
/// * [SpecialPickerType.wechatMoment] 微信朋友圈模式。当用户选择了视频,将不能选择图片。
76
78
final SpecialPickerType specialPickerType;
77
79
80
+ /// Allow users set custom item in the picker.
81
+ /// 允许用户在选择器中添加一个自定义item,并指定位置。
82
+ final CustomItemPosition customItemPosition;
83
+
84
+ /// The widget builder for the custom item.
85
+ /// 自定义item的构造方法
86
+ final WidgetBuilder customItemBuilder;
87
+
78
88
/// Static method to push with the navigator.
79
89
/// 跳转至选择器的静态方法
80
90
static Future <List <AssetEntity >> pickAssets (
@@ -91,6 +101,8 @@ class AssetPicker extends StatelessWidget {
91
101
ThemeData pickerTheme,
92
102
SortPathDelegate sortPathDelegate,
93
103
TextDelegate textDelegate,
104
+ WidgetBuilder customItemBuilder,
105
+ CustomItemPosition customItemPosition = CustomItemPosition .none,
94
106
Curve routeCurve = Curves .easeIn,
95
107
Duration routeDuration = const Duration (milliseconds: 300 ),
96
108
}) async {
@@ -120,6 +132,12 @@ class AssetPicker extends StatelessWidget {
120
132
requestType ?? = RequestType .image;
121
133
}
122
134
}
135
+ if ((customItemBuilder == null &&
136
+ customItemPosition != CustomItemPosition .none) ||
137
+ (customItemBuilder != null &&
138
+ customItemPosition == CustomItemPosition .none)) {
139
+ throw ArgumentError ('Custom item didn\' t set properly.' );
140
+ }
123
141
124
142
try {
125
143
final bool isPermissionGranted = await PhotoManager .requestPermission ();
@@ -141,6 +159,8 @@ class AssetPicker extends StatelessWidget {
141
159
themeColor: themeColor,
142
160
pickerTheme: pickerTheme,
143
161
specialPickerType: specialPickerType,
162
+ customItemPosition: customItemPosition,
163
+ customItemBuilder: customItemBuilder,
144
164
);
145
165
final List <AssetEntity > result = await Navigator .of (
146
166
context,
@@ -602,6 +622,10 @@ class AssetPicker extends StatelessWidget {
602
622
fontSize: isAppleOS ? 14.0 : 12.0 ,
603
623
fontWeight: isAppleOS ? FontWeight .w500 : FontWeight .normal,
604
624
),
625
+ strutStyle: const StrutStyle (
626
+ forceStrutHeight: true ,
627
+ height: 1.0 ,
628
+ ),
605
629
),
606
630
),
607
631
),
@@ -658,6 +682,7 @@ class AssetPicker extends StatelessWidget {
658
682
),
659
683
),
660
684
child: Row (
685
+ crossAxisAlignment: CrossAxisAlignment .center,
661
686
children: < Widget > [
662
687
const Icon (
663
688
Icons .videocam,
@@ -674,6 +699,10 @@ class AssetPicker extends StatelessWidget {
674
699
color: Colors .white,
675
700
fontSize: 16.0 ,
676
701
),
702
+ strutStyle: const StrutStyle (
703
+ forceStrutHeight: true ,
704
+ height: 1.4 ,
705
+ ),
677
706
),
678
707
),
679
708
],
@@ -825,40 +854,122 @@ class AssetPicker extends StatelessWidget {
825
854
mainAxisSpacing: itemSpacing,
826
855
crossAxisSpacing: itemSpacing,
827
856
),
828
- itemCount: currentAssets.length ,
857
+ itemCount: _assetsGridItemCount (_, currentAssets) ,
829
858
itemBuilder: (BuildContext _, int index) {
830
- if (index == currentAssets.length - gridCount * 3 &&
831
- _.read <AssetPickerProvider >().hasMoreToLoad) {
832
- provider.loadMoreAssets ();
833
- }
834
- final AssetEntity asset = currentAssets.elementAt (index);
835
- Widget builder;
836
- switch (asset.type) {
837
- case AssetType .audio:
838
- builder = audioItemBuilder (context, index, asset);
839
- break ;
840
- case AssetType .image:
841
- case AssetType .video:
842
- builder = imageAndVideoItemBuilder (context, index, asset);
843
- break ;
844
- case AssetType .other:
845
- builder = const SizedBox .shrink ();
846
- break ;
847
- }
848
- return Stack (
849
- children: < Widget > [
850
- builder,
851
- if (specialPickerType != SpecialPickerType .wechatMoment ||
852
- asset.type != AssetType .video)
853
- _selectIndicator (asset),
854
- ],
855
- );
859
+ return _assetGridItemBuilder (_, index, currentAssets);
856
860
},
857
861
);
858
862
},
859
863
),
860
864
);
861
865
866
+ /// The function which return items count for the assets' grid.
867
+ /// 为资源列表提供内容数量计算的方法
868
+ int _assetsGridItemCount (
869
+ BuildContext context,
870
+ List <AssetEntity > currentAssets,
871
+ ) {
872
+ final AssetPathEntity currentPath = Provider .of <AssetPickerProvider >(
873
+ context,
874
+ listen: false ,
875
+ ).currentPathEntity;
876
+
877
+ /// Return actual length if current path is all.
878
+ /// 如果当前目录是全部内容,则返回实际的内容数量。
879
+ if (! currentPath.isAll) {
880
+ return currentAssets.length;
881
+ }
882
+ int length;
883
+ switch (customItemPosition) {
884
+ case CustomItemPosition .none:
885
+ length = currentAssets.length;
886
+ break ;
887
+ case CustomItemPosition .prepend:
888
+ case CustomItemPosition .append:
889
+ length = currentAssets.length + 1 ;
890
+ break ;
891
+ }
892
+ return length;
893
+ }
894
+
895
+ /// The item builder for the assets' grid.
896
+ /// 资源列表项的构建
897
+ ///
898
+ /// There're several conditions within this builder:
899
+ /// * Return [customItemBuilder] while the current path is all and [customItemPosition]
900
+ /// is not equal to [CustomItemPosition.none] .
901
+ /// * Return item builder according to the asset's type.
902
+ /// * [AssetType.audio] -> [audioItemBuilder]
903
+ /// * [AssetType.image] , [AssetType.video] -> [imageAndVideoItemBuilder]
904
+ /// * Load more assets when the index reached at third line counting backwards.
905
+ /// 资源构建有几个条件:
906
+ /// * 当前路径是全部资源且 [customItemPosition] 不等于 [CustomItemPosition.none] 时,将会通过
907
+ /// [customItemBuilder] 构建内容。
908
+ /// * 根据资源类型返回对应类型的构建:
909
+ /// * [AssetType.audio] -> [audioItemBuilder] 音频类型
910
+ /// * [AssetType.image] , [AssetType.video] -> [imageAndVideoItemBuilder] 图片和视频类型
911
+ /// * 在索引到达倒数第三列的时候加载更多资源。
912
+ Widget _assetGridItemBuilder (
913
+ BuildContext context,
914
+ int index,
915
+ List <AssetEntity > currentAssets,
916
+ ) {
917
+ final AssetPathEntity currentPath = Provider .of <AssetPickerProvider >(
918
+ context,
919
+ listen: false ,
920
+ ).currentPathEntity;
921
+
922
+ int currentIndex;
923
+ switch (customItemPosition) {
924
+ case CustomItemPosition .none:
925
+ case CustomItemPosition .append:
926
+ currentIndex = index;
927
+ break ;
928
+ case CustomItemPosition .prepend:
929
+ currentIndex = index - 1 ;
930
+ break ;
931
+ }
932
+ if (! currentPath.isAll) {
933
+ currentIndex = index;
934
+ }
935
+
936
+ if (index == currentAssets.length - gridCount * 3 &&
937
+ context.read <AssetPickerProvider >().hasMoreToLoad) {
938
+ provider.loadMoreAssets ();
939
+ }
940
+
941
+ if (currentPath.isAll) {
942
+ if ((index == currentAssets.length &&
943
+ customItemPosition == CustomItemPosition .append) ||
944
+ (index == 0 && customItemPosition == CustomItemPosition .prepend)) {
945
+ return customItemBuilder (context);
946
+ }
947
+ }
948
+
949
+ final AssetEntity asset = currentAssets.elementAt (currentIndex);
950
+ Widget builder;
951
+ switch (asset.type) {
952
+ case AssetType .audio:
953
+ builder = audioItemBuilder (context, currentIndex, asset);
954
+ break ;
955
+ case AssetType .image:
956
+ case AssetType .video:
957
+ builder = imageAndVideoItemBuilder (context, currentIndex, asset);
958
+ break ;
959
+ case AssetType .other:
960
+ builder = const SizedBox .shrink ();
961
+ break ;
962
+ }
963
+ return Stack (
964
+ children: < Widget > [
965
+ builder,
966
+ if (specialPickerType != SpecialPickerType .wechatMoment ||
967
+ asset.type != AssetType .video)
968
+ _selectIndicator (asset),
969
+ ],
970
+ );
971
+ }
972
+
862
973
/// The item builder for audio type of asset.
863
974
/// 音频资源的部件构建
864
975
Widget audioItemBuilder (
0 commit comments