From fe84761cc346e3d95cadd7e776a9f645bb6a45da Mon Sep 17 00:00:00 2001 From: Alex Li Date: Wed, 9 Oct 2024 23:43:47 +0800 Subject: [PATCH 1/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Abstract=20`assetSeman?= =?UTF-8?q?ticsBuilder`=20for=20`AssetPickerViewerBuilderDelegate`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../asset_picker_viewer_builder_delegate.dart | 66 ++++++++++--------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/lib/src/delegates/asset_picker_viewer_builder_delegate.dart b/lib/src/delegates/asset_picker_viewer_builder_delegate.dart index 8f386724..855ef1fd 100644 --- a/lib/src/delegates/asset_picker_viewer_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_viewer_builder_delegate.dart @@ -421,35 +421,10 @@ class DefaultAssetPickerViewerBuilderDelegate (selectedAssets?.any((AssetEntity e) => e.type == AssetType.video) ?? false); - @override - Widget assetPageBuilder(BuildContext context, int index) { - final AssetEntity asset = previewAssets.elementAt( + Widget assetSemanticsBuilder(BuildContext context, int index) { + final asset = previewAssets.elementAt( shouldReversePreview ? previewAssets.length - index - 1 : index, ); - final Widget builder = switch (asset.type) { - AssetType.audio => AudioPageBuilder( - asset: asset, - shouldAutoplayPreview: shouldAutoplayPreview, - ), - AssetType.image => ImagePageBuilder( - asset: asset, - delegate: this, - previewThumbnailSize: previewThumbnailSize, - shouldAutoplayPreview: shouldAutoplayPreview, - ), - AssetType.video => VideoPageBuilder( - asset: asset, - delegate: this, - hasOnlyOneVideoAndMoment: isWeChatMoment && hasVideo, - shouldAutoplayPreview: shouldAutoplayPreview, - ), - AssetType.other => Center( - child: ScaleText( - textDelegate.unSupportedAssetType, - semanticsLabel: semanticsTextDelegate.unSupportedAssetType, - ), - ), - }; return MergeSemantics( child: Consumer?>( builder: ( @@ -476,14 +451,45 @@ class DefaultAssetPickerViewerBuilderDelegate hint: hint, image: asset.type == AssetType.image || asset.type == AssetType.video, - child: w, + child: child, ); }, - child: builder, + child: assetPageBuilder(context, index), ), ); } + @override + Widget assetPageBuilder(BuildContext context, int index) { + final asset = previewAssets.elementAt( + shouldReversePreview ? previewAssets.length - index - 1 : index, + ); + return switch (asset.type) { + AssetType.audio => AudioPageBuilder( + asset: asset, + shouldAutoplayPreview: shouldAutoplayPreview, + ), + AssetType.image => ImagePageBuilder( + asset: asset, + delegate: this, + previewThumbnailSize: previewThumbnailSize, + shouldAutoplayPreview: shouldAutoplayPreview, + ), + AssetType.video => VideoPageBuilder( + asset: asset, + delegate: this, + hasOnlyOneVideoAndMoment: isWeChatMoment && hasVideo, + shouldAutoplayPreview: shouldAutoplayPreview, + ), + AssetType.other => Center( + child: ScaleText( + textDelegate.unSupportedAssetType, + semanticsLabel: semanticsTextDelegate.unSupportedAssetType, + ), + ), + }; + } + /// Preview item widgets for audios. /// 音频的底部预览部件 Widget _audioPreviewItem(AssetEntity asset) { @@ -992,7 +998,7 @@ class DefaultAssetPickerViewerBuilderDelegate : const CustomBouncingScrollPhysics(), controller: pageController, itemCount: previewAssets.length, - itemBuilder: assetPageBuilder, + itemBuilder: assetSemanticsBuilder, onPageChanged: (int index) { currentIndex = index; pageStreamController.add(index); From f65cf3abfbe6809b630649272dac94f42cc47a2c Mon Sep 17 00:00:00 2001 From: Alex Li Date: Wed, 9 Oct 2024 23:45:49 +0800 Subject: [PATCH 2/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Move=20`selectorProvid?= =?UTF-8?q?er`=20from=20base=20delegate=20to=20the=20default=20delegate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../asset_picker_viewer_builder_delegate.dart | 50 ++++++++++++------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/lib/src/delegates/asset_picker_viewer_builder_delegate.dart b/lib/src/delegates/asset_picker_viewer_builder_delegate.dart index 855ef1fd..a1b70703 100644 --- a/lib/src/delegates/asset_picker_viewer_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_viewer_builder_delegate.dart @@ -33,7 +33,6 @@ abstract class AssetPickerViewerBuilderDelegate { required this.previewAssets, required this.themeData, required this.currentIndex, - this.selectorProvider, this.provider, this.selectedAssets, this.maxAssets, @@ -59,10 +58,6 @@ abstract class AssetPickerViewerBuilderDelegate { /// 已选的资源 final List? selectedAssets; - /// Provider for [AssetPicker]. - /// 资源选择器的状态保持 - final AssetPickerProvider? selectorProvider; - /// Whether the preview sequence is reversed. /// 预览时顺序是否为反向 /// @@ -264,7 +259,6 @@ abstract class AssetPickerViewerBuilderDelegate { void unSelectAsset(Asset entity) { provider?.unSelectAsset(entity); - selectorProvider?.unSelectAsset(entity); if (!isSelectedPreviewing) { selectedAssets?.remove(entity); } @@ -276,7 +270,6 @@ abstract class AssetPickerViewerBuilderDelegate { return; } provider?.selectAsset(entity); - selectorProvider?.selectAsset(entity); if (!isSelectedPreviewing) { selectedAssets?.add(entity); } @@ -316,12 +309,7 @@ abstract class AssetPickerViewerBuilderDelegate { 'No longer used by the package. ' 'This will be removed in 10.0.0', ) - Future syncSelectedAssetsWhenPop() async { - if (provider?.currentlySelectedAssets != null) { - selectorProvider?.selectedAssets = provider!.currentlySelectedAssets; - } - return true; - } + Future syncSelectedAssetsWhenPop() async => true; /// Split page builder according to type of asset. /// 根据资源类型使用不同的构建页 @@ -383,9 +371,9 @@ class DefaultAssetPickerViewerBuilderDelegate required super.currentIndex, required super.previewAssets, required super.themeData, - super.selectorProvider, super.provider, super.selectedAssets, + this.selectorProvider, this.previewThumbnailSize, this.specialPickerType, super.maxAssets, @@ -394,9 +382,9 @@ class DefaultAssetPickerViewerBuilderDelegate this.shouldAutoplayPreview = false, }); - /// Whether the preview should auto play. - /// 预览是否自动播放 - final bool shouldAutoplayPreview; + /// Provider for [AssetPicker]. + /// 资源选择器的状态保持 + final P? selectorProvider; /// Thumb size for the preview of images in the viewer. /// 预览时图片的缩略图大小 @@ -409,6 +397,10 @@ class DefaultAssetPickerViewerBuilderDelegate /// 如果类型不为空,则标题将不会显示。 final SpecialPickerType? specialPickerType; + /// Whether the preview should auto play. + /// 预览是否自动播放 + final bool shouldAutoplayPreview; + /// Whether the [SpecialPickerType.wechatMoment] is enabled. /// 当前是否为微信朋友圈选择模式 bool get isWeChatMoment => @@ -421,6 +413,30 @@ class DefaultAssetPickerViewerBuilderDelegate (selectedAssets?.any((AssetEntity e) => e.type == AssetType.video) ?? false); + @override + void unSelectAsset(AssetEntity entity) { + super.unSelectAsset(entity); + selectorProvider?.unSelectAsset(entity); + } + + @override + void selectAsset(AssetEntity entity) { + super.selectAsset(entity); + selectedNotifier.value = selectedCount; + } + + @Deprecated( + 'No longer used by the package. ' + 'This will be removed in 10.0.0', + ) + @override + Future syncSelectedAssetsWhenPop() async { + if (provider?.currentlySelectedAssets != null) { + selectorProvider?.selectedAssets = provider!.currentlySelectedAssets; + } + return true; + } + Widget assetSemanticsBuilder(BuildContext context, int index) { final asset = previewAssets.elementAt( shouldReversePreview ? previewAssets.length - index - 1 : index, From 9c5e14fa45d65f3e91918cf906a1f1d4f63fa838 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Wed, 9 Oct 2024 23:46:34 +0800 Subject: [PATCH 3/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Mark=20delegates=20and?= =?UTF-8?q?=20providers=20as=20generic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pickers/directory_file_asset_picker.dart | 26 ++++-- .../customs/pickers/insta_asset_picker.dart | 25 +++-- .../asset_picker_builder_delegate.dart | 92 +++++++++---------- .../asset_picker_viewer_builder_delegate.dart | 63 +++++++------ .../asset_picker_viewer_provider.dart | 28 +++--- lib/src/widget/asset_picker_viewer.dart | 70 ++++++++------ .../widget/builder/image_page_builder.dart | 4 +- .../widget/builder/video_page_builder.dart | 4 +- 8 files changed, 174 insertions(+), 138 deletions(-) diff --git a/example/lib/customs/pickers/directory_file_asset_picker.dart b/example/lib/customs/pickers/directory_file_asset_picker.dart index 535cb316..e79e44c1 100644 --- a/example/lib/customs/pickers/directory_file_asset_picker.dart +++ b/example/lib/customs/pickers/directory_file_asset_picker.dart @@ -163,7 +163,11 @@ class _DirectoryFileAssetPickerState extends State { return GestureDetector( onTap: isDisplayingDetail ? () async { - final Widget viewer = AssetPickerViewer( + final viewer = AssetPickerViewer< + File, + Directory, + FileAssetPickerViewerProvider, + FileAssetPickerViewerBuilderDelegate>( builder: FileAssetPickerViewerBuilderDelegate( currentIndex: index, previewAssets: fileList, @@ -392,7 +396,11 @@ class FileAssetPickerBuilder Animation animation, Animation secondaryAnimation, ) { - return AssetPickerViewer( + return AssetPickerViewer< + File, + Directory, + FileAssetPickerViewerProvider, + FileAssetPickerViewerBuilderDelegate>( builder: FileAssetPickerViewerBuilderDelegate( currentIndex: index ?? provider.selectedAssets.indexOf(currentAsset), @@ -418,7 +426,8 @@ class FileAssetPickerBuilder List? selectedAssets, FileAssetPickerProvider? selectorProvider, }) async { - final Widget viewer = AssetPickerViewer( + final viewer = AssetPickerViewer( builder: FileAssetPickerViewerBuilderDelegate( currentIndex: index, previewAssets: previewAssets, @@ -1210,17 +1219,18 @@ class FileAssetPickerViewerProvider extends AssetPickerViewerProvider { } class FileAssetPickerViewerBuilderDelegate - extends AssetPickerViewerBuilderDelegate { + extends AssetPickerViewerBuilderDelegate { FileAssetPickerViewerBuilderDelegate({ required super.previewAssets, required super.themeData, required super.currentIndex, super.selectedAssets, - super.selectorProvider, + this.selectorProvider, super.provider, - }) : super( - maxAssets: selectorProvider?.maxAssets, - ); + }) : super(maxAssets: selectorProvider?.maxAssets); + + final FileAssetPickerProvider? selectorProvider; late final PageController _pageController = PageController( initialPage: currentIndex, diff --git a/example/lib/customs/pickers/insta_asset_picker.dart b/example/lib/customs/pickers/insta_asset_picker.dart index 879fd87d..2568237e 100644 --- a/example/lib/customs/pickers/insta_asset_picker.dart +++ b/example/lib/customs/pickers/insta_asset_picker.dart @@ -286,7 +286,8 @@ class _InstaAssetPickerState extends State { } } -class InstaAssetPickerBuilder extends DefaultAssetPickerBuilderDelegate { +class InstaAssetPickerBuilder + extends DefaultAssetPickerBuilderDelegate { InstaAssetPickerBuilder({ required super.provider, required super.initialPermission, @@ -349,7 +350,7 @@ class InstaAssetPickerBuilder extends DefaultAssetPickerBuilderDelegate { /// Initialize [_previewAsset] with [p.selectedAssets] if not empty /// otherwise if the first item of the album Future _initializePreviewAsset( - DefaultAssetPickerProvider p, + T p, bool shouldDisplayAssets, ) async { if (_previewAsset.value != null) { @@ -487,8 +488,8 @@ class InstaAssetPickerBuilder extends DefaultAssetPickerBuilderDelegate { SizedBox( width: MediaQuery.sizeOf(context).width, height: previewHeight(context), - child: Selector>( - selector: (_, DefaultAssetPickerProvider p) => p.selectedAssets, + child: Selector>( + selector: (_, T p) => p.selectedAssets, builder: (_, List selected, __) { if (previewAsset == null && selected.isEmpty) { return loadingIndicator(context); @@ -502,7 +503,11 @@ class InstaAssetPickerBuilder extends DefaultAssetPickerBuilderDelegate { final List assets = selected.isEmpty ? [previewAsset!] : selected; - return AssetPickerViewer( + return AssetPickerViewer< + AssetEntity, + AssetPathEntity, + AssetPickerViewerProvider, + InstaAssetPickerViewerBuilder>( builder: InstaAssetPickerViewerBuilder( currentIndex: effectiveIndex == -1 ? 0 : effectiveIndex, previewAssets: assets, @@ -541,7 +546,7 @@ class InstaAssetPickerBuilder extends DefaultAssetPickerBuilderDelegate { _kPathSelectorRowHeight + MediaQuery.paddingOf(context).top; - return ChangeNotifierProvider.value( + return ChangeNotifierProvider.value( value: provider, builder: (BuildContext context, _) => ValueListenableBuilder( valueListenable: _viewerPosition, @@ -644,8 +649,8 @@ class InstaAssetPickerBuilder extends DefaultAssetPickerBuilderDelegate { Widget _buildListAlbums(BuildContext context) { appBarPreferredSize ??= appBar(context).preferredSize; - return Consumer( - builder: (BuildContext context, DefaultAssetPickerProvider provider, __) { + return Consumer( + builder: (BuildContext context, T provider, __) { if (isAppleOS(context)) { return pathEntityListWidget(context); } @@ -672,8 +677,8 @@ class InstaAssetPickerBuilder extends DefaultAssetPickerBuilderDelegate { Widget _buildGrid(BuildContext context) { appBarPreferredSize ??= appBar(context).preferredSize; - return Consumer( - builder: (BuildContext context, DefaultAssetPickerProvider p, __) { + return Consumer( + builder: (BuildContext context, T p, __) { final bool shouldDisplayAssets = p.hasAssetsToDisplay || shouldBuildSpecialItem; _initializePreviewAsset(p, shouldDisplayAssets); diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index 96c402c1..8c3b93d0 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -736,7 +736,7 @@ abstract class AssetPickerBuilderDelegate { } } -class DefaultAssetPickerBuilderDelegate +class DefaultAssetPickerBuilderDelegate extends AssetPickerBuilderDelegate { DefaultAssetPickerBuilderDelegate({ required this.provider, @@ -769,7 +769,7 @@ class DefaultAssetPickerBuilderDelegate /// [ChangeNotifier] for asset picker. /// 资源选择器状态保持 - final DefaultAssetPickerProvider provider; + final T provider; /// Thumbnail size in the grid. /// 预览时网络的缩略图大小 @@ -877,8 +877,7 @@ class DefaultAssetPickerBuilderDelegate if (selectPredicateResult == false) { return; } - final DefaultAssetPickerProvider provider = - context.read(); + final provider = context.read(); if (selected) { provider.unSelectAsset(asset); return; @@ -1010,7 +1009,7 @@ class DefaultAssetPickerBuilderDelegate int? index, AssetEntity currentAsset, ) async { - final p = context.read(); + final p = context.read(); // - When we reached the maximum select count and the asset is not selected, // do nothing. // - When the special type is WeChat Moment, pictures and videos cannot @@ -1077,7 +1076,7 @@ class DefaultAssetPickerBuilderDelegate if (current.isEmpty) { throw StateError('Previewing empty assets is not allowed. $_debugFlow'); } - final List? result = await AssetPickerViewer.pushToViewer( + final result = await AssetPickerViewer.pushToViewer( context, currentIndex: effectiveIndex, previewAssets: current, @@ -1114,8 +1113,8 @@ class DefaultAssetPickerBuilderDelegate Widget androidLayout(BuildContext context) { return AssetPickerAppBarWrapper( appBar: appBar(context), - body: Consumer( - builder: (BuildContext context, DefaultAssetPickerProvider p, _) { + body: Consumer( + builder: (BuildContext context, T p, _) { final bool shouldDisplayAssets = p.hasAssetsToDisplay || shouldBuildSpecialItem; return AnimatedSwitcher( @@ -1165,8 +1164,8 @@ class DefaultAssetPickerBuilderDelegate return Stack( children: [ Positioned.fill( - child: Consumer( - builder: (_, DefaultAssetPickerProvider p, __) { + child: Consumer( + builder: (_, p, __) { final Widget child; final bool shouldDisplayAssets = p.hasAssetsToDisplay || shouldBuildSpecialItem; @@ -1207,8 +1206,8 @@ class DefaultAssetPickerBuilderDelegate @override Widget loadingIndicator(BuildContext context) { - return Selector( - selector: (_, DefaultAssetPickerProvider p) => p.isAssetsEmpty, + return Selector( + selector: (_, T p) => p.isAssetsEmpty, builder: (BuildContext context, bool isAssetsEmpty, Widget? w) { if (loadingIndicatorBuilder != null) { return loadingIndicatorBuilder!(context, isAssetsEmpty); @@ -1226,8 +1225,8 @@ class DefaultAssetPickerBuilderDelegate Widget assetsGridBuilder(BuildContext context) { appBarPreferredSize ??= appBar(context).preferredSize; final bool gridRevert = effectiveShouldRevertGrid(context); - return Selector?>( - selector: (_, DefaultAssetPickerProvider p) => p.currentPath, + return Selector?>( + selector: (_, T p) => p.currentPath, builder: (context, wrapper, _) { // First, we need the count of the assets. int totalCount = wrapper?.assetCount ?? 0; @@ -1347,9 +1346,8 @@ class DefaultAssetPickerBuilderDelegate textDirection: effectiveGridDirection(context), child: ColoredBox( color: theme.canvasColor, - child: Selector>( - selector: (_, DefaultAssetPickerProvider p) => - p.currentAssets, + child: Selector>( + selector: (_, T p) => p.currentAssets, builder: (BuildContext context, List assets, _) { final SliverGap bottomGap = SliverGap.v( context.bottomPadding + bottomSectionHeight, @@ -1406,8 +1404,7 @@ class DefaultAssetPickerBuilderDelegate List currentAssets, { Widget? specialItem, }) { - final DefaultAssetPickerProvider p = - context.read(); + final p = context.read(); final int length = currentAssets.length; final PathWrapper? currentWrapper = p.currentPath; final AssetPathEntity? currentPathEntity = currentWrapper?.path; @@ -1477,8 +1474,8 @@ class DefaultAssetPickerBuilderDelegate return ValueListenableBuilder( valueListenable: isSwitchingPath, builder: (_, bool isSwitchingPath, Widget? child) { - return Consumer( - builder: (_, DefaultAssetPickerProvider p, __) { + return Consumer( + builder: (_, T p, __) { final bool isBanned = (!p.selectedAssets.contains(asset) && p.selectedMaximumAssets) || (isWeChatMoment && @@ -1570,9 +1567,9 @@ class DefaultAssetPickerBuilderDelegate int placeholderCount = 0, Widget? specialItem, }) { - final PathWrapper? currentWrapper = context - .select?>( - (DefaultAssetPickerProvider p) => p.currentPath, + final PathWrapper? currentWrapper = + context.select?>( + (T p) => p.currentPath, ); final AssetPathEntity? currentPathEntity = currentWrapper?.path; final int length = assets.length + placeholderCount; @@ -1661,8 +1658,8 @@ class DefaultAssetPickerBuilderDelegate /// 当有资源已选时,点击按钮将把已选资源通过路由返回。 @override Widget confirmButton(BuildContext context) { - return Consumer( - builder: (_, DefaultAssetPickerProvider p, __) { + return Consumer( + builder: (_, T p, __) { final bool isSelectedNotEmpty = p.isSelectedNotEmpty; final bool shouldAllowConfirm = isSelectedNotEmpty || p.previousSelectedAssets.isNotEmpty; @@ -1873,9 +1870,8 @@ class DefaultAssetPickerBuilderDelegate ), ), Flexible( - child: Selector>>( - selector: (_, DefaultAssetPickerProvider p) => p.paths, + child: Selector>>( + selector: (_, T p) => p.paths, builder: (_, List> paths, __) { final List> filtered = paths .where( @@ -1950,9 +1946,8 @@ class DefaultAssetPickerBuilderDelegate borderRadius: BorderRadius.circular(999), color: theme.focusColor, ), - child: Selector?>( - selector: (_, DefaultAssetPickerProvider p) => p.currentPath, + child: Selector?>( + selector: (_, T p) => p.currentPath, builder: (_, PathWrapper? p, Widget? w) { final AssetPathEntity? path = p?.path; return Row( @@ -2045,8 +2040,8 @@ class DefaultAssetPickerBuilderDelegate if (semanticsCount != null) { labelBuffer.write(': $semanticsCount'); } - return Selector?>( - selector: (_, DefaultAssetPickerProvider p) => p.currentPath, + return Selector?>( + selector: (_, T p) => p.currentPath, builder: (_, PathWrapper? currentWrapper, __) { final bool isSelected = currentWrapper?.path == pathEntity; return Semantics( @@ -2060,7 +2055,7 @@ class DefaultAssetPickerBuilderDelegate splashFactory: InkSplash.splashFactory, onTap: () { Feedback.forTap(context); - context.read().switchPath(wrapper); + context.read().switchPath(wrapper); isSwitchingPath.value = false; gridScrollController.jumpTo(0); }, @@ -2108,8 +2103,8 @@ class DefaultAssetPickerBuilderDelegate @override Widget previewButton(BuildContext context) { - return Consumer( - builder: (_, DefaultAssetPickerProvider p, Widget? child) { + return Consumer( + builder: (_, T p, Widget? child) { return ValueListenableBuilder( valueListenable: isSwitchingPath, builder: (_, bool isSwitchingPath, __) => Semantics( @@ -2121,16 +2116,15 @@ class DefaultAssetPickerBuilderDelegate ), ); }, - child: Consumer( - builder: (context, DefaultAssetPickerProvider p, __) => GestureDetector( + child: Consumer( + builder: (context, T p, __) => GestureDetector( onTap: p.isSelectedNotEmpty ? () { viewAsset(context, null, p.selectedAssets.first); } : null, - child: Selector( - selector: (_, DefaultAssetPickerProvider p) => - p.selectedDescriptions, + child: Selector( + selector: (_, T p) => p.selectedDescriptions, builder: (BuildContext c, __, ___) => Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: ScaleText( @@ -2155,8 +2149,8 @@ class DefaultAssetPickerBuilderDelegate @override Widget itemBannedIndicator(BuildContext context, AssetEntity asset) { - return Consumer( - builder: (_, DefaultAssetPickerProvider p, __) { + return Consumer( + builder: (_, T p, __) { final bool isDisabled = (!p.selectedAssets.contains(asset) && p.selectedMaximumAssets) || (isWeChatMoment && @@ -2177,8 +2171,8 @@ class DefaultAssetPickerBuilderDelegate final double indicatorSize = MediaQuery.sizeOf(context).width / gridCount / 3; final Duration duration = switchingPathDuration * 0.75; - return Selector( - selector: (_, DefaultAssetPickerProvider p) => p.selectedDescriptions, + return Selector( + selector: (_, T p) => p.selectedDescriptions, builder: (BuildContext context, String descriptions, __) { final bool selected = descriptions.contains(asset.toString()); final Widget innerSelector = AnimatedContainer( @@ -2243,8 +2237,8 @@ class DefaultAssetPickerBuilderDelegate viewAsset(context, index, asset); } : null, - child: Consumer( - builder: (_, DefaultAssetPickerProvider p, __) { + child: Consumer( + builder: (_, T p, __) { final int index = p.selectedAssets.indexOf(asset); final bool selected = index != -1; return AnimatedContainer( @@ -2438,7 +2432,7 @@ class DefaultAssetPickerBuilderDelegate data: theme, child: AnnotatedRegion( value: overlayStyle, - child: CNP.value( + child: CNP.value( value: provider, builder: (BuildContext context, _) => Scaffold( backgroundColor: theme.scaffoldBackgroundColor, diff --git a/lib/src/delegates/asset_picker_viewer_builder_delegate.dart b/lib/src/delegates/asset_picker_viewer_builder_delegate.dart index a1b70703..5b319599 100644 --- a/lib/src/delegates/asset_picker_viewer_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_viewer_builder_delegate.dart @@ -28,7 +28,8 @@ import '../widget/builder/fade_image_builder.dart'; import '../widget/builder/image_page_builder.dart'; import '../widget/builder/video_page_builder.dart'; -abstract class AssetPickerViewerBuilderDelegate { +abstract class AssetPickerViewerBuilderDelegate> { AssetPickerViewerBuilderDelegate({ required this.previewAssets, required this.themeData, @@ -44,7 +45,7 @@ abstract class AssetPickerViewerBuilderDelegate { /// [ChangeNotifier] for photo selector viewer. /// 资源预览器的状态保持 - final AssetPickerViewerProvider? provider; + final Provider? provider; /// Assets provided to preview. /// 提供预览的资源 @@ -89,7 +90,8 @@ abstract class AssetPickerViewerBuilderDelegate { /// The [State] for a viewer. /// 预览器的状态实例 - late AssetPickerViewerState viewerState; + late AssetPickerViewerState> viewerState; /// [AnimationController] for double tap animation. /// 双击缩放的动画控制器 @@ -160,7 +162,9 @@ abstract class AssetPickerViewerBuilderDelegate { /// 当预览器调用 [State.initState] 时注册 [State]。 @mustCallSuper void initStateAndTicker( - covariant AssetPickerViewerState state, + covariant AssetPickerViewerState> + state, TickerProvider v, // TODO(Alex): Remove this in the next major version. ) { initAnimations(state); @@ -175,9 +179,15 @@ abstract class AssetPickerViewerBuilderDelegate { /// a new delegate and only calling [State.didUpdateWidget] at the moment. @mustCallSuper void didUpdateViewer( - covariant AssetPickerViewerState state, - covariant AssetPickerViewer oldWidget, - covariant AssetPickerViewer newWidget, + covariant AssetPickerViewerState> + state, + covariant AssetPickerViewer> + oldWidget, + covariant AssetPickerViewer> + newWidget, ) { // Widgets are useless in the default delegate. initAnimations(state); @@ -201,7 +211,11 @@ abstract class AssetPickerViewerBuilderDelegate { /// Initialize animations related to the zooming preview. /// 为缩放预览初始化动画 - void initAnimations(covariant AssetPickerViewerState state) { + void initAnimations( + covariant AssetPickerViewerState> + state, + ) { viewerState = state; doubleTapAnimationController = AnimationController( duration: const Duration(milliseconds: 200), @@ -365,8 +379,10 @@ abstract class AssetPickerViewerBuilderDelegate { Widget build(BuildContext context); } -class DefaultAssetPickerViewerBuilderDelegate - extends AssetPickerViewerBuilderDelegate { +class DefaultAssetPickerViewerBuilderDelegate< + T extends AssetPickerViewerProvider, + P extends DefaultAssetPickerProvider> + extends AssetPickerViewerBuilderDelegate { DefaultAssetPickerViewerBuilderDelegate({ required super.currentIndex, required super.previewAssets, @@ -442,12 +458,8 @@ class DefaultAssetPickerViewerBuilderDelegate shouldReversePreview ? previewAssets.length - index - 1 : index, ); return MergeSemantics( - child: Consumer?>( - builder: ( - BuildContext c, - AssetPickerViewerProvider? p, - Widget? w, - ) { + child: Consumer( + builder: (context, T? p, child) { final bool isSelected = (p?.currentlySelectedAssets ?? selectedAssets)?.contains(asset) ?? false; @@ -595,7 +607,7 @@ class DefaultAssetPickerViewerBuilderDelegate height: context.bottomPadding + bottomDetailHeight, child: child!, ), - child: CNP?>.value( + child: CNP.value( value: provider, child: Column( mainAxisSize: MainAxisSize.min, @@ -703,10 +715,8 @@ class DefaultAssetPickerViewerBuilderDelegate onTap: () { onTap(asset); }, - child: Selector?, - List?>( - selector: (_, AssetPickerViewerProvider? p) => - p?.currentlySelectedAssets, + child: Selector?>( + selector: (_, T? p) => p?.currentlySelectedAssets, child: item, builder: ( _, @@ -809,10 +819,10 @@ class DefaultAssetPickerViewerBuilderDelegate /// 资源选择器将识别并一同返回。 @override Widget confirmButton(BuildContext context) { - return CNP?>.value( + return CNP.value( value: provider, - child: Consumer?>( - builder: (_, AssetPickerViewerProvider? provider, __) { + child: Consumer( + builder: (context, T? provider, __) { assert( isWeChatMoment || provider != null, 'Viewer provider must not be null ' @@ -945,7 +955,7 @@ class DefaultAssetPickerViewerBuilderDelegate @override Widget selectButton(BuildContext context) { - return CNP>.value( + return CNP.value( value: provider!, builder: (_, Widget? w) => StreamBuilder( initialData: currentIndex, @@ -969,8 +979,7 @@ class DefaultAssetPickerViewerBuilderDelegate ); } final asset = previewAssets.elementAt(assetIndex); - return Selector, - List>( + return Selector>( selector: (_, p) => p.currentlySelectedAssets, builder: (context, assets, _) { final bool isSelected = assets.contains(asset); diff --git a/lib/src/provider/asset_picker_viewer_provider.dart b/lib/src/provider/asset_picker_viewer_provider.dart index c8b3c177..62d1d0ad 100644 --- a/lib/src/provider/asset_picker_viewer_provider.dart +++ b/lib/src/provider/asset_picker_viewer_provider.dart @@ -8,14 +8,14 @@ import '../constants/constants.dart'; /// [ChangeNotifier] for assets picker viewer. /// 资源选择查看器的 provider model. -class AssetPickerViewerProvider extends ChangeNotifier { - /// Copy selected assets for editing when constructing. - /// 构造时深拷贝已选择的资源集合,用于后续编辑。 +class AssetPickerViewerProvider extends ChangeNotifier { AssetPickerViewerProvider( - List? assets, { + List? assets, { this.maxAssets = defaultMaxAssetsCount, }) : assert(maxAssets > 0, 'maxAssets must be greater than 0.') { - _currentlySelectedAssets = (assets ?? []).toList(); + // Copy selected assets for editing when constructing. + // 构造时深拷贝已选择的资源集合,用于后续编辑。 + _currentlySelectedAssets = (assets ?? []).toList(); } /// Maximum count for asset selection. @@ -24,11 +24,11 @@ class AssetPickerViewerProvider extends ChangeNotifier { /// Selected assets in the viewer. /// 查看器中已选择的资源 - late List _currentlySelectedAssets; + late List _currentlySelectedAssets; - List get currentlySelectedAssets => _currentlySelectedAssets; + List get currentlySelectedAssets => _currentlySelectedAssets; - set currentlySelectedAssets(List value) { + set currentlySelectedAssets(List value) { if (value == _currentlySelectedAssets) { return; } @@ -41,33 +41,33 @@ class AssetPickerViewerProvider extends ChangeNotifier { /// Select asset. /// 选中资源 - void selectAsset(A item) { + void selectAsset(Asset item) { if (currentlySelectedAssets.length == maxAssets || currentlySelectedAssets.contains(item)) { return; } - final List newList = _currentlySelectedAssets.toList()..add(item); + final List newList = _currentlySelectedAssets.toList()..add(item); currentlySelectedAssets = newList; } /// Un-select asset. /// 取消选中资源 - void unSelectAsset(A item) { + void unSelectAsset(Asset item) { if (currentlySelectedAssets.isEmpty || !currentlySelectedAssets.contains(item)) { return; } - final List newList = _currentlySelectedAssets.toList()..remove(item); + final List newList = _currentlySelectedAssets.toList()..remove(item); currentlySelectedAssets = newList; } @Deprecated('Use selectAsset instead. This will be removed in 10.0.0') - void selectAssetEntity(A entity) { + void selectAssetEntity(Asset entity) { selectAsset(entity); } @Deprecated('Use unSelectAsset instead. This will be removed in 10.0.0') - void unselectAssetEntity(A entity) { + void unselectAssetEntity(Asset entity) { unSelectAsset(entity); } } diff --git a/lib/src/widget/asset_picker_viewer.dart b/lib/src/widget/asset_picker_viewer.dart index fe30d20e..9498a09d 100644 --- a/lib/src/widget/asset_picker_viewer.dart +++ b/lib/src/widget/asset_picker_viewer.dart @@ -13,44 +13,51 @@ import '../constants/typedefs.dart'; import '../delegates/asset_picker_viewer_builder_delegate.dart'; import '../provider/asset_picker_provider.dart'; import '../provider/asset_picker_viewer_provider.dart'; -import 'asset_picker.dart'; -class AssetPickerViewer extends StatefulWidget { +class AssetPickerViewer< + Asset, + Path, + Provider extends AssetPickerViewerProvider, + Delegate extends AssetPickerViewerBuilderDelegate> extends StatefulWidget { const AssetPickerViewer({ super.key, required this.builder, }); - final AssetPickerViewerBuilderDelegate builder; + final Delegate builder; @override - AssetPickerViewerState createState() => - AssetPickerViewerState(); + AssetPickerViewerState createState() => + AssetPickerViewerState(); /// Static method to push with the navigator. /// 跳转至选择预览的静态方法 - static Future?> pushToViewer( + static Future?> + pushToViewer

( BuildContext context, { int currentIndex = 0, required List previewAssets, required ThemeData themeData, - DefaultAssetPickerProvider? selectorProvider, + P? selectorProvider, ThumbnailSize? previewThumbnailSize, List? selectedAssets, SpecialPickerType? specialPickerType, int? maxAssets, bool shouldReversePreview = false, AssetSelectPredicate? selectPredicate, - PermissionRequestOption permissionRequestOption = - const PermissionRequestOption(), bool shouldAutoplayPreview = false, }) async { if (previewAssets.isEmpty) { throw StateError('Previewing empty assets is not allowed.'); } - await AssetPicker.permissionCheck(requestOption: permissionRequestOption); - final Widget viewer = AssetPickerViewer( - builder: DefaultAssetPickerViewerBuilderDelegate( + final viewer = AssetPickerViewer< + AssetEntity, + AssetPathEntity, + AssetPickerViewerProvider, + DefaultAssetPickerViewerBuilderDelegate>( + builder: DefaultAssetPickerViewerBuilderDelegate< + AssetPickerViewerProvider, P>( currentIndex: currentIndex, previewAssets: previewAssets, provider: selectedAssets != null @@ -72,44 +79,49 @@ class AssetPickerViewer extends StatefulWidget { shouldAutoplayPreview: shouldAutoplayPreview, ), ); - final PageRouteBuilder> pageRoute = - PageRouteBuilder>( + final pageRoute = PageRouteBuilder>( pageBuilder: (_, __, ___) => viewer, transitionsBuilder: (_, Animation animation, __, Widget child) { return FadeTransition(opacity: animation, child: child); }, ); - final List? result = + final result = await Navigator.maybeOf(context)?.push>(pageRoute); return result; } /// Call the viewer with provided delegate and provider. /// 通过指定的 [delegate] 调用查看器 - static Future?> pushToViewerWithDelegate( + static Future?> pushToViewerWithDelegate< + A, + P, + Provider extends AssetPickerViewerProvider, + Delegate extends AssetPickerViewerBuilderDelegate>( BuildContext context, { - required AssetPickerViewerBuilderDelegate delegate, - PermissionRequestOption permissionRequestOption = - const PermissionRequestOption(), + required Delegate delegate, }) async { - await AssetPicker.permissionCheck(requestOption: permissionRequestOption); - final Widget viewer = AssetPickerViewer(builder: delegate); - final PageRouteBuilder> pageRoute = PageRouteBuilder>( + final viewer = AssetPickerViewer( + builder: delegate, + ); + final pageRoute = PageRouteBuilder>( pageBuilder: (_, __, ___) => viewer, transitionsBuilder: (_, Animation animation, __, Widget child) { return FadeTransition(opacity: animation, child: child); }, ); - final List? result = - await Navigator.maybeOf(context)?.push>(pageRoute); + final result = await Navigator.maybeOf(context)?.push>(pageRoute); return result; } } -class AssetPickerViewerState - extends State> +class AssetPickerViewerState< + A, + P, + Provider extends AssetPickerViewerProvider, + Delegate extends AssetPickerViewerBuilderDelegate> + extends State> with TickerProviderStateMixin { - AssetPickerViewerBuilderDelegate get builder => widget.builder; + Delegate get builder => widget.builder; @override void initState() { @@ -118,7 +130,9 @@ class AssetPickerViewerState } @override - void didUpdateWidget(covariant AssetPickerViewer oldWidget) { + void didUpdateWidget( + covariant AssetPickerViewer oldWidget, + ) { super.didUpdateWidget(oldWidget); builder.didUpdateViewer(this, oldWidget, widget); } diff --git a/lib/src/widget/builder/image_page_builder.dart b/lib/src/widget/builder/image_page_builder.dart index 8636cbd1..27015d8d 100644 --- a/lib/src/widget/builder/image_page_builder.dart +++ b/lib/src/widget/builder/image_page_builder.dart @@ -16,6 +16,7 @@ import 'package:wechat_picker_library/wechat_picker_library.dart'; import '../../constants/constants.dart'; import '../../delegates/asset_picker_text_delegate.dart'; import '../../delegates/asset_picker_viewer_builder_delegate.dart'; +import '../../provider/asset_picker_viewer_provider.dart'; class ImagePageBuilder extends StatefulWidget { const ImagePageBuilder({ @@ -30,7 +31,8 @@ class ImagePageBuilder extends StatefulWidget { /// 展示的资源 final AssetEntity asset; - final AssetPickerViewerBuilderDelegate delegate; + final AssetPickerViewerBuilderDelegate> delegate; final ThumbnailSize? previewThumbnailSize; diff --git a/lib/src/widget/builder/video_page_builder.dart b/lib/src/widget/builder/video_page_builder.dart index 396105de..0c571d98 100644 --- a/lib/src/widget/builder/video_page_builder.dart +++ b/lib/src/widget/builder/video_page_builder.dart @@ -12,6 +12,7 @@ import 'package:wechat_picker_library/wechat_picker_library.dart'; import '../../constants/constants.dart'; import '../../delegates/asset_picker_viewer_builder_delegate.dart'; import '../../internals/singleton.dart'; +import '../../provider/asset_picker_viewer_provider.dart'; class VideoPageBuilder extends StatefulWidget { const VideoPageBuilder({ @@ -26,7 +27,8 @@ class VideoPageBuilder extends StatefulWidget { /// 展示的资源 final AssetEntity asset; - final AssetPickerViewerBuilderDelegate delegate; + final AssetPickerViewerBuilderDelegate> delegate; /// Only previewing one video and with the [SpecialPickerType.wechatMoment]. /// 是否处于 [SpecialPickerType.wechatMoment] 且只有一个视频 From 4a1366586f63daa83994402cccfb6225f83c41d8 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Thu, 10 Oct 2024 00:05:54 +0800 Subject: [PATCH 4/9] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Correct=20delegate=20t?= =?UTF-8?q?ype=20reference=20with=20`AssetPicker`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/lib/constants/picker_method.dart | 7 +++-- .../pickers/multi_tabs_assets_picker.dart | 5 +++- .../asset_picker_builder_delegate.dart | 21 +++++++-------- lib/src/delegates/asset_picker_delegate.dart | 18 ++++++++----- lib/src/widget/asset_picker.dart | 26 ++++++++++++------- test/providers_test.dart | 16 ++++++------ test/test_utils.dart | 3 ++- 7 files changed, 55 insertions(+), 41 deletions(-) diff --git a/example/lib/constants/picker_method.dart b/example/lib/constants/picker_method.dart index 00857410..6636452d 100644 --- a/example/lib/constants/picker_method.dart +++ b/example/lib/constants/picker_method.dart @@ -207,10 +207,9 @@ class PickMethod { return; } final picker = context.findAncestorWidgetOfExactType< - AssetPicker>()!; - final builder = - picker.builder as DefaultAssetPickerBuilderDelegate; - final p = builder.provider; + AssetPicker>()!; + final p = picker.builder.provider; await p.switchPath( PathWrapper( path: diff --git a/example/lib/customs/pickers/multi_tabs_assets_picker.dart b/example/lib/customs/pickers/multi_tabs_assets_picker.dart index f60689e6..d15a8e3f 100644 --- a/example/lib/customs/pickers/multi_tabs_assets_picker.dart +++ b/example/lib/customs/pickers/multi_tabs_assets_picker.dart @@ -279,7 +279,10 @@ class MultiTabAssetPickerBuilder extends DefaultAssetPickerBuilderDelegate { late final TabController _tabController; @override - void initState(AssetPickerState state) { + void initState( + AssetPickerState + state, + ) { super.initState(state); _tabController = TabController(length: 3, vsync: state); } diff --git a/lib/src/delegates/asset_picker_builder_delegate.dart b/lib/src/delegates/asset_picker_builder_delegate.dart index 8c3b93d0..43621f63 100644 --- a/lib/src/delegates/asset_picker_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_builder_delegate.dart @@ -121,14 +121,13 @@ abstract class AssetPickerBuilderDelegate { final LimitedPermissionOverlayPredicate? limitedPermissionOverlayPredicate; /// {@macro wechat_assets_picker.PathNameBuilder} - final PathNameBuilder? pathNameBuilder; + final PathNameBuilder? pathNameBuilder; /// {@macro wechat_assets_picker.AssetsChangeCallback} - final AssetsChangeCallback? assetsChangeCallback; + final AssetsChangeCallback? assetsChangeCallback; /// {@macro wechat_assets_picker.AssetsChangeRefreshPredicate} - final AssetsChangeRefreshPredicate? - assetsChangeRefreshPredicate; + final AssetsChangeRefreshPredicate? assetsChangeRefreshPredicate; /// [ThemeData] for the picker. /// 选择器使用的主题 @@ -229,7 +228,11 @@ abstract class AssetPickerBuilderDelegate { /// Keep a `initState` method to sync with [State]. /// 保留一个 `initState` 方法与 [State] 同步。 @mustCallSuper - void initState(AssetPickerState state) {} + void initState( + covariant AssetPickerState> + state, + ) {} /// Keep a `dispose` method to sync with [State]. /// 保留一个 `dispose` 方法与 [State] 同步。 @@ -395,11 +398,7 @@ abstract class AssetPickerBuilderDelegate { /// Loading indicator. /// 加载指示器 /// - /// Subclasses need to implement this due to the generic type limitation, and - /// not all delegates use [AssetPickerProvider]. - /// - /// See also: - /// - [DefaultAssetPickerBuilderDelegate.loadingIndicator] as an example. + /// See [DefaultAssetPickerBuilderDelegate.loadingIndicator] as an example. Widget loadingIndicator(BuildContext context); /// GIF image type indicator. @@ -1653,7 +1652,7 @@ class DefaultAssetPickerBuilderDelegate ); } - /// It'll pop with [AssetPickerProvider.selectedAssets] + /// It'll pop with [T.selectedAssets] /// when there are any assets were chosen. /// 当有资源已选时,点击按钮将把已选资源通过路由返回。 @override diff --git a/lib/src/delegates/asset_picker_delegate.dart b/lib/src/delegates/asset_picker_delegate.dart index 3f788b3d..bee83a1b 100644 --- a/lib/src/delegates/asset_picker_delegate.dart +++ b/lib/src/delegates/asset_picker_delegate.dart @@ -94,7 +94,8 @@ class AssetPickerDelegate { filterOptions: pickerConfig.filterOptions, initializeDelayDuration: route.transitionDuration, ); - final Widget picker = AssetPicker( + final picker = AssetPicker( key: key, permissionRequestOption: permissionRequestOption, builder: DefaultAssetPickerBuilderDelegate( @@ -148,10 +149,13 @@ class AssetPickerDelegate { /// * [AssetPickerBuilderDelegate] for how to customize/override widgets /// during the picking process. /// {@endtemplate} - Future?> pickAssetsWithDelegate>( + Future?> pickAssetsWithDelegate< + Asset, + Path, + PickerProvider extends AssetPickerProvider, + Delegate extends AssetPickerBuilderDelegate>( BuildContext context, { - required AssetPickerBuilderDelegate delegate, + required Delegate delegate, PermissionRequestOption permissionRequestOption = const PermissionRequestOption(), Key? key, @@ -159,15 +163,15 @@ class AssetPickerDelegate { AssetPickerPageRouteBuilder>? pageRouteBuilder, }) async { await permissionCheck(requestOption: permissionRequestOption); - final Widget picker = AssetPicker( + final picker = AssetPicker( key: key, permissionRequestOption: permissionRequestOption, builder: delegate, ); - final List? result = await Navigator.maybeOf( + final result = await Navigator.maybeOf( context, rootNavigator: useRootNavigator, - )?.push>( + )?.push( pageRouteBuilder?.call(picker) ?? AssetPickerPageRoute>(builder: (_) => picker), ); diff --git a/lib/src/widget/asset_picker.dart b/lib/src/widget/asset_picker.dart index ec22a02d..4db70dbd 100644 --- a/lib/src/widget/asset_picker.dart +++ b/lib/src/widget/asset_picker.dart @@ -18,7 +18,9 @@ import 'asset_picker_page_route.dart'; AssetPickerDelegate _pickerDelegate = const AssetPickerDelegate(); -class AssetPicker extends StatefulWidget { +class AssetPicker> + extends StatefulWidget { const AssetPicker({ super.key, required this.permissionRequestOption, @@ -26,7 +28,7 @@ class AssetPicker extends StatefulWidget { }); final PermissionRequestOption permissionRequestOption; - final AssetPickerBuilderDelegate builder; + final Delegate builder; /// Provide another [AssetPickerDelegate] which override with /// custom methods during handling the picking, @@ -66,17 +68,21 @@ class AssetPicker extends StatefulWidget { } /// {@macro wechat_assets_picker.delegates.AssetPickerDelegate.pickAssetsWithDelegate} - static Future?> pickAssetsWithDelegate>( + static Future?> pickAssetsWithDelegate< + Asset, + Path, + PickerProvider extends AssetPickerProvider, + Delegate extends AssetPickerBuilderDelegate>( BuildContext context, { - required AssetPickerBuilderDelegate delegate, + required Delegate delegate, PermissionRequestOption permissionRequestOption = const PermissionRequestOption(), Key? key, AssetPickerPageRouteBuilder>? pageRouteBuilder, bool useRootNavigator = true, }) { - return _pickerDelegate.pickAssetsWithDelegate( + return _pickerDelegate + .pickAssetsWithDelegate( context, key: key, delegate: delegate, @@ -102,11 +108,13 @@ class AssetPicker extends StatefulWidget { } @override - AssetPickerState createState() => - AssetPickerState(); + AssetPickerState createState() => + AssetPickerState(); } -class AssetPickerState extends State> +class AssetPickerState> + extends State> with TickerProviderStateMixin, WidgetsBindingObserver { Completer? permissionStateLock; diff --git a/test/providers_test.dart b/test/providers_test.dart index 33b0eef1..b1228386 100644 --- a/test/providers_test.dart +++ b/test/providers_test.dart @@ -23,15 +23,15 @@ void main() async { ); await tester.tap(defaultButtonFinder); await tester.pumpAndSettle(); - final Finder pickerFinder = find.byType( - AssetPicker, - ); - final AssetPicker picker = tester.widget( - pickerFinder, + final picker = tester.widget< + AssetPicker>( + find.byType( + AssetPicker, + ), ); - final DefaultAssetPickerProvider provider = - (picker.builder as DefaultAssetPickerBuilderDelegate).provider; - expect(provider, isA()); + final provider = picker.builder.provider; await tester.tap(find.widgetWithIcon(IconButton, Icons.close)); await tester.pumpAndSettle(); expect( diff --git a/test/test_utils.dart b/test/test_utils.dart index c5d0a670..cda469b6 100644 --- a/test/test_utils.dart +++ b/test/test_utils.dart @@ -120,7 +120,8 @@ class TestAssetPickerDelegate extends AssetPickerDelegate { ) ..hasAssetsToDisplay = true ..totalAssetsCount = 1; - final Widget picker = AssetPicker( + final picker = AssetPicker( key: key, permissionRequestOption: permissionRequestOption, builder: DefaultAssetPickerBuilderDelegate( From c5c3a1865bb3304dca39a81e71f1fab1822835a0 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Thu, 10 Oct 2024 00:06:12 +0800 Subject: [PATCH 5/9] =?UTF-8?q?=F0=9F=9A=80=20Expose=20item=20builders=20e?= =?UTF-8?q?ntry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/builder.dart | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 lib/builder.dart diff --git a/lib/builder.dart b/lib/builder.dart new file mode 100644 index 00000000..6b862abe --- /dev/null +++ b/lib/builder.dart @@ -0,0 +1,8 @@ +// Copyright 2019 The FlutterCandies author. All rights reserved. +// Use of this source code is governed by an Apache license that can be found +// in the LICENSE file. + +export 'src/widget/builder/asset_entity_grid_item_builder.dart'; +export 'src/widget/builder/audio_page_builder.dart'; +export 'src/widget/builder/image_page_builder.dart'; +export 'src/widget/builder/video_page_builder.dart'; From 4001e904d7daed6ef8471752cac439e35e1798bf Mon Sep 17 00:00:00 2001 From: Alex Li Date: Thu, 10 Oct 2024 13:21:02 +0800 Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=90=9B=20Fix=20`AssetPickerViewerStat?= =?UTF-8?q?e`=20type=20inference=20in=20the=20viewer=20builder=20delegate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../asset_picker_viewer_builder_delegate.dart | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/lib/src/delegates/asset_picker_viewer_builder_delegate.dart b/lib/src/delegates/asset_picker_viewer_builder_delegate.dart index 5b319599..64205d6c 100644 --- a/lib/src/delegates/asset_picker_viewer_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_viewer_builder_delegate.dart @@ -90,8 +90,7 @@ abstract class AssetPickerViewerBuilderDelegate> viewerState; + late AssetPickerViewerState viewerState; /// [AnimationController] for double tap animation. /// 双击缩放的动画控制器 @@ -162,9 +161,7 @@ abstract class AssetPickerViewerBuilderDelegate> - state, + covariant AssetPickerViewerState state, TickerProvider v, // TODO(Alex): Remove this in the next major version. ) { initAnimations(state); @@ -179,15 +176,9 @@ abstract class AssetPickerViewerBuilderDelegate> - state, - covariant AssetPickerViewer> - oldWidget, - covariant AssetPickerViewer> - newWidget, + covariant AssetPickerViewerState state, + covariant AssetPickerViewer oldWidget, + covariant AssetPickerViewer newWidget, ) { // Widgets are useless in the default delegate. initAnimations(state); @@ -212,9 +203,7 @@ abstract class AssetPickerViewerBuilderDelegate> - state, + covariant AssetPickerViewerState state, ) { viewerState = state; doubleTapAnimationController = AnimationController( From 4e0740dfbff79c5b8086096a4cc1b81258390779 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Thu, 10 Oct 2024 13:22:13 +0800 Subject: [PATCH 7/9] =?UTF-8?q?=F0=9F=94=A5=20Remove=20redundant=20`Ticker?= =?UTF-8?q?Provider`=20of=20viewer=20builder=20delegate=20`initState`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/delegates/asset_picker_viewer_builder_delegate.dart | 3 +-- lib/src/widget/asset_picker_viewer.dart | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/src/delegates/asset_picker_viewer_builder_delegate.dart b/lib/src/delegates/asset_picker_viewer_builder_delegate.dart index 64205d6c..76709d63 100644 --- a/lib/src/delegates/asset_picker_viewer_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_viewer_builder_delegate.dart @@ -160,9 +160,8 @@ abstract class AssetPickerViewerBuilderDelegate Date: Thu, 10 Oct 2024 15:50:06 +0800 Subject: [PATCH 8/9] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Serve=20T=20and=20P=20?= =?UTF-8?q?in=20the=20viewer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../asset_picker_viewer_builder_delegate.dart | 381 +++++++++--------- 1 file changed, 188 insertions(+), 193 deletions(-) diff --git a/lib/src/delegates/asset_picker_viewer_builder_delegate.dart b/lib/src/delegates/asset_picker_viewer_builder_delegate.dart index 76709d63..482c94b2 100644 --- a/lib/src/delegates/asset_picker_viewer_builder_delegate.dart +++ b/lib/src/delegates/asset_picker_viewer_builder_delegate.dart @@ -595,47 +595,43 @@ class DefaultAssetPickerViewerBuilderDelegate< height: context.bottomPadding + bottomDetailHeight, child: child!, ), - child: CNP.value( - value: provider, - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - if (provider != null) - ValueListenableBuilder( - valueListenable: selectedNotifier, - builder: (_, int count, __) => Container( - width: count > 0 ? double.maxFinite : 0, - height: bottomPreviewHeight, - color: backgroundColor, - child: ListView.builder( - controller: previewingListController, - scrollDirection: Axis.horizontal, - padding: const EdgeInsets.symmetric(horizontal: 5.0), - physics: const ClampingScrollPhysics(), - itemCount: count, - itemBuilder: bottomDetailItemBuilder, - ), - ), - ), - Container( - height: bottomBarHeight + context.bottomPadding, - padding: const EdgeInsets.symmetric(horizontal: 20.0) - .copyWith(bottom: context.bottomPadding), - decoration: BoxDecoration( - border: Border(top: BorderSide(color: themeData.canvasColor)), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (provider != null) + ValueListenableBuilder( + valueListenable: selectedNotifier, + builder: (_, int count, __) => Container( + width: count > 0 ? double.maxFinite : 0, + height: bottomPreviewHeight, color: backgroundColor, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - if (provider != null || isWeChatMoment) - confirmButton(context), - ], + child: ListView.builder( + controller: previewingListController, + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.symmetric(horizontal: 5.0), + physics: const ClampingScrollPhysics(), + itemCount: count, + itemBuilder: bottomDetailItemBuilder, + ), ), ), - ], - ), + Container( + height: bottomBarHeight + context.bottomPadding, + padding: const EdgeInsets.symmetric(horizontal: 20.0) + .copyWith(bottom: context.bottomPadding), + decoration: BoxDecoration( + border: Border(top: BorderSide(color: themeData.canvasColor)), + color: backgroundColor, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (provider != null || isWeChatMoment) confirmButton(context), + ], + ), + ), + ], ), ); } @@ -807,86 +803,82 @@ class DefaultAssetPickerViewerBuilderDelegate< /// 资源选择器将识别并一同返回。 @override Widget confirmButton(BuildContext context) { - return CNP.value( - value: provider, - child: Consumer( - builder: (context, T? provider, __) { - assert( - isWeChatMoment || provider != null, - 'Viewer provider must not be null ' - 'when the special type is not WeChat moment.', - ); - Future onPressed() async { - if (isWeChatMoment && hasVideo) { - Navigator.maybeOf(context)?.pop([currentAsset]); - return; - } - if (provider!.isSelectedNotEmpty) { - Navigator.maybeOf(context)?.pop(provider.currentlySelectedAssets); - return; - } - if (await onChangingSelected(context, currentAsset, false)) { - Navigator.maybeOf(context)?.pop( - selectedAssets ?? [currentAsset], - ); - } + return Consumer( + builder: (context, T? provider, __) { + assert( + isWeChatMoment || provider != null, + 'Viewer provider must not be null ' + 'when the special type is not WeChat moment.', + ); + Future onPressed() async { + if (isWeChatMoment && hasVideo) { + Navigator.maybeOf(context)?.pop([currentAsset]); + return; + } + if (provider!.isSelectedNotEmpty) { + Navigator.maybeOf(context)?.pop(provider.currentlySelectedAssets); + return; + } + if (await onChangingSelected(context, currentAsset, false)) { + Navigator.maybeOf(context)?.pop( + selectedAssets ?? [currentAsset], + ); } + } - String buildText() { - if (isWeChatMoment && hasVideo) { - return textDelegate.confirm; - } - if (provider!.isSelectedNotEmpty) { - return '${textDelegate.confirm}' - ' (${provider.currentlySelectedAssets.length}' - '/' - '${selectorProvider!.maxAssets})'; - } + String buildText() { + if (isWeChatMoment && hasVideo) { return textDelegate.confirm; } - - final isButtonEnabled = provider == null || - previewAssets.isEmpty || - (selectedAssets?.isNotEmpty ?? false); - return MaterialButton( - minWidth: - (isWeChatMoment && hasVideo) || provider!.isSelectedNotEmpty - ? 48 - : 20, - height: 32, - padding: const EdgeInsets.symmetric(horizontal: 12), - color: themeData.colorScheme.secondary, - disabledColor: themeData.splashColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(3), + if (provider!.isSelectedNotEmpty) { + return '${textDelegate.confirm}' + ' (${provider.currentlySelectedAssets.length}' + '/' + '${selectorProvider!.maxAssets})'; + } + return textDelegate.confirm; + } + + final isButtonEnabled = provider == null || + previewAssets.isEmpty || + (selectedAssets?.isNotEmpty ?? false); + return MaterialButton( + minWidth: (isWeChatMoment && hasVideo) || provider!.isSelectedNotEmpty + ? 48 + : 20, + height: 32, + padding: const EdgeInsets.symmetric(horizontal: 12), + color: themeData.colorScheme.secondary, + disabledColor: themeData.splashColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(3), + ), + onPressed: isButtonEnabled ? onPressed : null, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + child: ScaleText( + buildText(), + style: TextStyle( + color: themeData.textTheme.bodyLarge?.color, + fontSize: 17, + fontWeight: FontWeight.normal, ), - onPressed: isButtonEnabled ? onPressed : null, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - child: ScaleText( - buildText(), - style: TextStyle( - color: themeData.textTheme.bodyLarge?.color, - fontSize: 17, - fontWeight: FontWeight.normal, - ), - overflow: TextOverflow.fade, - softWrap: false, - semanticsLabel: () { - if (isWeChatMoment && hasVideo) { - return semanticsTextDelegate.confirm; - } - if (provider!.isSelectedNotEmpty) { - return '${semanticsTextDelegate.confirm}' - ' (${provider.currentlySelectedAssets.length}' - '/' - '${selectorProvider!.maxAssets})'; - } + overflow: TextOverflow.fade, + softWrap: false, + semanticsLabel: () { + if (isWeChatMoment && hasVideo) { return semanticsTextDelegate.confirm; - }(), - ), - ); - }, - ), + } + if (provider!.isSelectedNotEmpty) { + return '${semanticsTextDelegate.confirm}' + ' (${provider.currentlySelectedAssets.length}' + '/' + '${selectorProvider!.maxAssets})'; + } + return semanticsTextDelegate.confirm; + }(), + ), + ); + }, ); } @@ -943,62 +935,59 @@ class DefaultAssetPickerViewerBuilderDelegate< @override Widget selectButton(BuildContext context) { - return CNP.value( - value: provider!, - builder: (_, Widget? w) => StreamBuilder( - initialData: currentIndex, - stream: pageStreamController.stream, - builder: (_, s) { - final index = s.data!; - final assetIndex = - shouldReversePreview ? previewAssets.length - index - 1 : index; - if (assetIndex < 0) { - throw IndexError.withLength( - assetIndex, - previewAssets.length, - indexable: previewAssets, - name: 'selectButton.assetIndex', - message: 'previewReversed: $shouldReversePreview\n' - 'stream.index: $index\n' - 'selectedAssets.length: ${selectedAssets?.length}\n' - 'previewAssets.length: ${previewAssets.length}\n' - 'currentIndex: $currentIndex\n' - 'maxAssets: $maxAssets', - ); - } - final asset = previewAssets.elementAt(assetIndex); - return Selector>( - selector: (_, p) => p.currentlySelectedAssets, - builder: (context, assets, _) { - final bool isSelected = assets.contains(asset); - return Semantics( - selected: isSelected, - label: semanticsTextDelegate.select, - onTap: () { - onChangingSelected(context, asset, isSelected); - }, - onTapHint: semanticsTextDelegate.select, - excludeSemantics: true, - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - if (isAppleOS(context)) - _appleOSSelectButton(context, isSelected, asset) - else - _androidSelectButton(context, isSelected, asset), - if (!isAppleOS(context)) - ScaleText( - textDelegate.select, - style: const TextStyle(fontSize: 17, height: 1.2), - semanticsLabel: semanticsTextDelegate.select, - ), - ], - ), - ); - }, + return StreamBuilder( + initialData: currentIndex, + stream: pageStreamController.stream, + builder: (_, s) { + final index = s.data!; + final assetIndex = + shouldReversePreview ? previewAssets.length - index - 1 : index; + if (assetIndex < 0) { + throw IndexError.withLength( + assetIndex, + previewAssets.length, + indexable: previewAssets, + name: 'selectButton.assetIndex', + message: 'previewReversed: $shouldReversePreview\n' + 'stream.index: $index\n' + 'selectedAssets.length: ${selectedAssets?.length}\n' + 'previewAssets.length: ${previewAssets.length}\n' + 'currentIndex: $currentIndex\n' + 'maxAssets: $maxAssets', ); - }, - ), + } + final asset = previewAssets.elementAt(assetIndex); + return Selector>( + selector: (_, p) => p.currentlySelectedAssets, + builder: (context, assets, _) { + final bool isSelected = assets.contains(asset); + return Semantics( + selected: isSelected, + label: semanticsTextDelegate.select, + onTap: () { + onChangingSelected(context, asset, isSelected); + }, + onTapHint: semanticsTextDelegate.select, + excludeSemantics: true, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (isAppleOS(context)) + _appleOSSelectButton(context, isSelected, asset) + else + _androidSelectButton(context, isSelected, asset), + if (!isAppleOS(context)) + ScaleText( + textDelegate.select, + style: const TextStyle(fontSize: 17, height: 1.2), + semanticsLabel: semanticsTextDelegate.select, + ), + ], + ), + ); + }, + ); + }, ); } @@ -1022,32 +1011,38 @@ class DefaultAssetPickerViewerBuilderDelegate< @override Widget build(BuildContext context) { - return Theme( - data: themeData, - child: AnnotatedRegion( - value: themeData.appBarTheme.systemOverlayStyle ?? - (themeData.effectiveBrightness.isDark - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark), - child: Scaffold( - resizeToAvoidBottomInset: false, - body: Stack( - children: [ - Positioned.fill(child: _pageViewBuilder(context)), - if (isWeChatMoment && hasVideo) ...[ - momentVideoBackButton(context), - PositionedDirectional( - end: 16, - bottom: context.bottomPadding + 16, - child: confirmButton(context), - ), - ] else ...[ - appBar(context), - if (selectedAssets != null || - (isWeChatMoment && hasVideo && isAppleOS(context))) - bottomDetailBuilder(context), - ], - ], + return CNP.value( + value: provider, + child: CNP.value( + value: selectorProvider, + builder: (context, _) => Theme( + data: themeData, + child: AnnotatedRegion( + value: themeData.appBarTheme.systemOverlayStyle ?? + (themeData.effectiveBrightness.isDark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark), + child: Scaffold( + resizeToAvoidBottomInset: false, + body: Stack( + children: [ + Positioned.fill(child: _pageViewBuilder(context)), + if (isWeChatMoment && hasVideo) ...[ + momentVideoBackButton(context), + PositionedDirectional( + end: 16, + bottom: context.bottomPadding + 16, + child: confirmButton(context), + ), + ] else ...[ + appBar(context), + if (selectedAssets != null || + (isWeChatMoment && hasVideo && isAppleOS(context))) + bottomDetailBuilder(context), + ], + ], + ), + ), ), ), ), From 6cc49c8746fcd1d4a0d364a9d12afd054685afb6 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Thu, 10 Oct 2024 18:00:39 +0800 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=9A=80=20Pass=20asset=20through=20wid?= =?UTF-8?q?gets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/src/widget/builder/image_page_builder.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/src/widget/builder/image_page_builder.dart b/lib/src/widget/builder/image_page_builder.dart index 27015d8d..ba92b0e6 100644 --- a/lib/src/widget/builder/image_page_builder.dart +++ b/lib/src/widget/builder/image_page_builder.dart @@ -120,6 +120,7 @@ class _ImagePageBuilderState extends State { ); if (_isLivePhoto && _livePhotoVideoController != null) { return _LivePhotoWidget( + asset: asset, controller: _livePhotoVideoController!, fit: BoxFit.contain, state: state, @@ -161,12 +162,14 @@ class _ImagePageBuilderState extends State { class _LivePhotoWidget extends StatefulWidget { const _LivePhotoWidget({ + required this.asset, required this.controller, required this.state, required this.fit, required this.textDelegate, }); + final AssetEntity asset; final VideoPlayerController controller; final ExtendedImageState state; final BoxFit fit;