@@ -255,6 +255,21 @@ abstract class AssetPickerBuilderDelegate<A, P> {
255
255
/// 资源是否已选的指示器
256
256
Widget selectIndicator (BuildContext context, A asset);
257
257
258
+ /// Indicator when the asset cannot be selected.
259
+ /// 当资源无法被选中时的遮罩
260
+ Widget itemBannedIndicator (BuildContext context, A asset) {
261
+ return Consumer <AssetPickerProvider <A , P >>(
262
+ builder: (_, AssetPickerProvider <A , P > p, __) {
263
+ if (! p.selectedAssets.contains (asset) && p.selectedMaximumAssets) {
264
+ return Container (
265
+ color: theme.colorScheme.background.withOpacity (.85 ),
266
+ );
267
+ }
268
+ return const SizedBox .shrink ();
269
+ },
270
+ );
271
+ }
272
+
258
273
/// Loading indicator.
259
274
/// 加载指示器
260
275
Widget loadingIndicator (BuildContext context) {
@@ -652,7 +667,7 @@ class DefaultAssetPickerBuilderDelegate
652
667
653
668
/// [Duration] when triggering path switching.
654
669
/// 切换路径时的动画时长
655
- Duration get switchingPathDuration => kThemeAnimationDuration * 1.5 ;
670
+ Duration get switchingPathDuration => kThemeAnimationDuration;
656
671
657
672
/// [Curve] when triggering path switching.
658
673
/// 切换路径时的动画曲线
@@ -971,6 +986,7 @@ class DefaultAssetPickerBuilderDelegate
971
986
builder,
972
987
if (! isWeChatMoment || asset.type != AssetType .video)
973
988
selectIndicator (context, asset),
989
+ itemBannedIndicator (context, asset),
974
990
],
975
991
);
976
992
}
@@ -1199,98 +1215,104 @@ class DefaultAssetPickerBuilderDelegate
1199
1215
return Positioned .fill (
1200
1216
top: isAppleOS ? context.topPadding + kToolbarHeight : 0 ,
1201
1217
bottom: null ,
1202
- child: Selector <DefaultAssetPickerProvider , bool >(
1203
- selector: (_, DefaultAssetPickerProvider p) => p.isSwitchingPath,
1204
- builder: (_, bool isSwitchingPath, Widget ? w) => AnimatedAlign (
1205
- duration: switchingPathDuration,
1206
- curve: switchingPathCurve,
1207
- alignment: Alignment .bottomCenter,
1208
- heightFactor: isSwitchingPath ? 1 : 0 ,
1209
- child: AnimatedOpacity (
1218
+ child: ClipRRect (
1219
+ borderRadius: const BorderRadius .vertical (
1220
+ bottom: Radius .circular (10.0 ),
1221
+ ),
1222
+ child: Selector <DefaultAssetPickerProvider , bool >(
1223
+ selector: (_, DefaultAssetPickerProvider p) => p.isSwitchingPath,
1224
+ builder: (_, bool isSwitchingPath, Widget ? w) => AnimatedAlign (
1210
1225
duration: switchingPathDuration,
1211
1226
curve: switchingPathCurve,
1212
- opacity: ! isAppleOS || isSwitchingPath ? 1.0 : 0.0 ,
1213
- child: Container (
1214
- height: context.mediaQuery.size.height * (isAppleOS ? .6 : .8 ),
1215
- decoration: BoxDecoration (
1216
- borderRadius: isAppleOS
1217
- ? const BorderRadius .vertical (bottom: Radius .circular (10.0 ))
1218
- : null ,
1227
+ alignment: Alignment .bottomCenter,
1228
+ heightFactor: isSwitchingPath ? 1 : 0 ,
1229
+ child: AnimatedOpacity (
1230
+ duration: switchingPathDuration,
1231
+ curve: switchingPathCurve,
1232
+ opacity: ! isAppleOS || isSwitchingPath ? 1.0 : 0.0 ,
1233
+ child: Container (
1234
+ constraints: BoxConstraints (
1235
+ maxHeight:
1236
+ context.mediaQuery.size.height * (isAppleOS ? .6 : .8 ),
1237
+ ),
1219
1238
color: theme.colorScheme.background,
1239
+ child: w,
1220
1240
),
1221
- child: w,
1222
1241
),
1223
1242
),
1224
- ),
1225
- child: Column (
1226
- children: < Widget > [
1227
- ValueListenableBuilder <PermissionState >(
1228
- valueListenable: permission,
1229
- builder: (_, PermissionState ps, Widget ? child) {
1230
- if (isPermissionLimited) {
1231
- return child! ;
1232
- }
1233
- return const SizedBox .shrink ();
1234
- },
1235
- child: Padding (
1236
- padding: const EdgeInsets .symmetric (
1237
- horizontal: 20 ,
1238
- vertical: 12 ,
1239
- ),
1240
- child: Text .rich (
1241
- TextSpan (
1242
- children: < TextSpan > [
1243
- TextSpan (
1244
- text: Constants .textDelegate.viewingLimitedAssetsTip,
1245
- ),
1246
- TextSpan (
1247
- text: ' '
1248
- '${Constants .textDelegate .changeAccessibleLimitedAssets }' ,
1249
- style: TextStyle (color: interactiveTextColor (context)),
1250
- recognizer: TapGestureRecognizer ()
1251
- ..onTap = PhotoManager .presentLimited,
1252
- ),
1253
- ],
1243
+ child: Column (
1244
+ mainAxisSize: MainAxisSize .min,
1245
+ children: < Widget > [
1246
+ ValueListenableBuilder <PermissionState >(
1247
+ valueListenable: permission,
1248
+ builder: (_, PermissionState ps, Widget ? child) {
1249
+ if (isPermissionLimited) {
1250
+ return child! ;
1251
+ }
1252
+ return const SizedBox .shrink ();
1253
+ },
1254
+ child: Padding (
1255
+ padding: const EdgeInsets .symmetric (
1256
+ horizontal: 20 ,
1257
+ vertical: 12 ,
1254
1258
),
1255
- style: context.themeData.textTheme.caption? .copyWith (
1256
- fontSize: 15 ,
1259
+ child: Text .rich (
1260
+ TextSpan (
1261
+ children: < TextSpan > [
1262
+ TextSpan (
1263
+ text: Constants .textDelegate.viewingLimitedAssetsTip,
1264
+ ),
1265
+ TextSpan (
1266
+ text: ' '
1267
+ '${Constants .textDelegate .changeAccessibleLimitedAssets }' ,
1268
+ style:
1269
+ TextStyle (color: interactiveTextColor (context)),
1270
+ recognizer: TapGestureRecognizer ()
1271
+ ..onTap = PhotoManager .presentLimited,
1272
+ ),
1273
+ ],
1274
+ ),
1275
+ style: context.themeData.textTheme.caption? .copyWith (
1276
+ fontSize: 15 ,
1277
+ ),
1257
1278
),
1258
1279
),
1259
1280
),
1260
- ),
1261
- Expanded (
1262
- child: Selector <DefaultAssetPickerProvider , int >(
1263
- selector: (_, DefaultAssetPickerProvider p) =>
1264
- p.validPathThumbCount,
1265
- builder: (_, int count, __) => Selector <
1266
- DefaultAssetPickerProvider ,
1267
- Map <AssetPathEntity , Uint8List ?>>(
1281
+ Flexible (
1282
+ child: Selector <DefaultAssetPickerProvider , int >(
1268
1283
selector: (_, DefaultAssetPickerProvider p) =>
1269
- p.pathEntityList,
1270
- builder: (_, Map <AssetPathEntity , Uint8List ?> list, __) {
1271
- return ListView .separated (
1272
- padding: const EdgeInsetsDirectional .only (top: 1.0 ),
1273
- itemCount: list.length,
1274
- itemBuilder: (BuildContext c, int index) =>
1275
- pathEntityWidget (
1276
- context: c,
1277
- list: list,
1278
- index: index,
1279
- isAudio: (provider as DefaultAssetPickerProvider )
1280
- .requestType ==
1281
- RequestType .audio,
1282
- ),
1283
- separatorBuilder: (_, __) => Container (
1284
- margin: const EdgeInsetsDirectional .only (start: 60.0 ),
1285
- height: 1.0 ,
1286
- color: theme.canvasColor,
1287
- ),
1288
- );
1289
- },
1284
+ p.validPathThumbCount,
1285
+ builder: (_, int count, __) => Selector <
1286
+ DefaultAssetPickerProvider ,
1287
+ Map <AssetPathEntity , Uint8List ?>>(
1288
+ selector: (_, DefaultAssetPickerProvider p) =>
1289
+ p.pathEntityList,
1290
+ builder: (_, Map <AssetPathEntity , Uint8List ?> list, __) {
1291
+ return ListView .separated (
1292
+ padding: const EdgeInsetsDirectional .only (top: 1.0 ),
1293
+ shrinkWrap: true ,
1294
+ itemCount: list.length,
1295
+ itemBuilder: (BuildContext c, int i) =>
1296
+ pathEntityWidget (
1297
+ context: c,
1298
+ list: list,
1299
+ index: i,
1300
+ isAudio: (provider as DefaultAssetPickerProvider )
1301
+ .requestType ==
1302
+ RequestType .audio,
1303
+ ),
1304
+ separatorBuilder: (_, __) => Container (
1305
+ margin: const EdgeInsetsDirectional .only (start: 60.0 ),
1306
+ height: 1.0 ,
1307
+ color: theme.canvasColor,
1308
+ ),
1309
+ );
1310
+ },
1311
+ ),
1290
1312
),
1291
1313
),
1292
- ) ,
1293
- ] ,
1314
+ ] ,
1315
+ ) ,
1294
1316
),
1295
1317
),
1296
1318
);
@@ -1519,8 +1541,26 @@ class DefaultAssetPickerBuilderDelegate
1519
1541
);
1520
1542
}
1521
1543
1544
+ @override
1545
+ Widget itemBannedIndicator (BuildContext context, AssetEntity asset) {
1546
+ return Consumer <DefaultAssetPickerProvider >(
1547
+ builder: (_, DefaultAssetPickerProvider p, __) {
1548
+ if ((! p.selectedAssets.contains (asset) && p.selectedMaximumAssets) ||
1549
+ (isWeChatMoment &&
1550
+ asset.type == AssetType .video &&
1551
+ p.selectedAssets.isNotEmpty)) {
1552
+ return Container (
1553
+ color: theme.colorScheme.background.withOpacity (.85 ),
1554
+ );
1555
+ }
1556
+ return const SizedBox .shrink ();
1557
+ },
1558
+ );
1559
+ }
1560
+
1522
1561
@override
1523
1562
Widget selectIndicator (BuildContext context, AssetEntity asset) {
1563
+ final Duration duration = switchingPathDuration * 0.75 ;
1524
1564
return Selector <DefaultAssetPickerProvider , String >(
1525
1565
selector: (_, DefaultAssetPickerProvider p) => p.selectedDescriptions,
1526
1566
builder: (BuildContext context, _, __) {
@@ -1532,7 +1572,7 @@ class DefaultAssetPickerBuilderDelegate
1532
1572
final double indicatorSize =
1533
1573
context.mediaQuery.size.width / gridCount / 3 ;
1534
1574
final Widget innerSelector = AnimatedContainer (
1535
- duration: switchingPathDuration ,
1575
+ duration: duration ,
1536
1576
width: indicatorSize / (isAppleOS ? 1.25 : 1.5 ),
1537
1577
height: indicatorSize / (isAppleOS ? 1.25 : 1.5 ),
1538
1578
decoration: BoxDecoration (
@@ -1542,22 +1582,10 @@ class DefaultAssetPickerBuilderDelegate
1542
1582
shape: BoxShape .circle,
1543
1583
),
1544
1584
child: AnimatedSwitcher (
1545
- duration: switchingPathDuration ,
1546
- reverseDuration: switchingPathDuration ,
1585
+ duration: duration ,
1586
+ reverseDuration: duration ,
1547
1587
child: selected
1548
- ? isSingleAssetMode
1549
- ? const Icon (Icons .check, size: 18.0 )
1550
- : Text (
1551
- '${selectedAssets .indexOf (asset ) + 1 }' ,
1552
- style: TextStyle (
1553
- color: selected
1554
- ? theme.textTheme.bodyText1? .color
1555
- : null ,
1556
- fontSize: isAppleOS ? 16.0 : 14.0 ,
1557
- fontWeight:
1558
- isAppleOS ? FontWeight .w600 : FontWeight .bold,
1559
- ),
1560
- )
1588
+ ? const Icon (Icons .check, size: 18.0 )
1561
1589
: const SizedBox .shrink (),
1562
1590
),
1563
1591
);
@@ -1602,15 +1630,23 @@ class DefaultAssetPickerBuilderDelegate
1602
1630
1603
1631
@override
1604
1632
Widget selectedBackdrop (BuildContext context, int index, AssetEntity asset) {
1633
+ bool selectedAllAndNotSelected () =>
1634
+ ! provider.selectedAssets.contains (asset) &&
1635
+ provider.selectedMaximumAssets;
1636
+ bool selectedPhotosAndIsVideo () =>
1637
+ isWeChatMoment &&
1638
+ asset.type == AssetType .video &&
1639
+ provider.selectedAssets.isNotEmpty;
1640
+
1605
1641
return Positioned .fill (
1606
1642
child: GestureDetector (
1607
1643
onTap: () async {
1644
+ // When we reached the maximum select count and the asset
1645
+ // is not selected, do nothing.
1608
1646
// When the special type is WeChat Moment, pictures and videos cannot
1609
1647
// be selected at the same time. Video select should be banned if any
1610
1648
// pictures are selected.
1611
- if (isWeChatMoment &&
1612
- asset.type == AssetType .video &&
1613
- provider.selectedAssets.isNotEmpty) {
1649
+ if (selectedAllAndNotSelected () || selectedPhotosAndIsVideo ()) {
1614
1650
return ;
1615
1651
}
1616
1652
final List <AssetEntity > _current;
@@ -1652,12 +1688,27 @@ class DefaultAssetPickerBuilderDelegate
1652
1688
child: Selector <DefaultAssetPickerProvider , List <AssetEntity >>(
1653
1689
selector: (_, DefaultAssetPickerProvider p) => p.selectedAssets,
1654
1690
builder: (_, List <AssetEntity > selectedAssets, __) {
1655
- final bool selected = selectedAssets.contains (asset);
1691
+ final int index = selectedAssets.indexOf (asset);
1692
+ final bool selected = index != - 1 ;
1656
1693
return AnimatedContainer (
1657
1694
duration: switchingPathDuration,
1658
1695
color: selected
1659
- ? theme.colorScheme.primary.withOpacity (0.45 )
1660
- : Colors .black.withOpacity (0.1 ),
1696
+ ? theme.colorScheme.primary.withOpacity (.45 )
1697
+ : Colors .black.withOpacity (.1 ),
1698
+ child: selected && ! isSingleAssetMode
1699
+ ? Container (
1700
+ alignment: AlignmentDirectional .topStart,
1701
+ padding: const EdgeInsets .all (14 ),
1702
+ child: Text (
1703
+ '${index + 1 }' ,
1704
+ style: TextStyle (
1705
+ color: theme.textTheme.bodyText1? .color
1706
+ ? .withOpacity (.75 ),
1707
+ fontWeight: FontWeight .w600,
1708
+ ),
1709
+ ),
1710
+ )
1711
+ : const SizedBox .shrink (),
1661
1712
);
1662
1713
},
1663
1714
),
0 commit comments