Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ that can be found in the LICENSE file. -->

## Unreleased

*None.*
**Fixes**

- Fix picker and viewer selection syncing when selecting the last allowed asset.
- Fix Live Photo visibility callbacks updating video state after the widget is disposed.

## 10.1.2

Expand Down
8 changes: 5 additions & 3 deletions lib/src/delegates/asset_picker_viewer_builder_delegate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,9 @@ abstract class AssetPickerViewerBuilderDelegate<Asset, Path,

/// 是否已经选择了最大数量的资源
bool get selectedMaximumAssets =>
selectedAssets != null && selectedAssets!.length == maxAssets;
selectedAssets != null &&
maxAssets != null &&
selectedAssets!.length >= maxAssets!;

/// Construct a notifier to notify
/// whether if a new asset is selected or unselected.
Expand All @@ -268,11 +270,11 @@ abstract class AssetPickerViewerBuilderDelegate<Asset, Path,
}

void selectAsset(Asset asset) {
if (maxAssets != null && selectedCount > maxAssets!) {
if (selectedMaximumAssets && !(selectedAssets?.contains(asset) ?? false)) {
return;
}
provider?.selectAsset(asset);
if (!isSelectedPreviewing) {
if (!isSelectedPreviewing && !(selectedAssets?.contains(asset) ?? false)) {
selectedAssets?.add(asset);
}
selectedNotifier.value = selectedCount;
Expand Down
8 changes: 4 additions & 4 deletions lib/src/provider/asset_picker_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -234,17 +234,17 @@ abstract class AssetPickerProvider<Asset, Path> extends ChangeNotifier {
bool get isSelectedNotEmpty => selectedAssets.isNotEmpty;

/// 是否已经选择了最大数量的资源
bool get selectedMaximumAssets => selectedAssets.length == maxAssets;
bool get selectedMaximumAssets => selectedAssets.length >= maxAssets;

/// Select asset.
/// 选中资源
void selectAsset(Asset item) {
if (selectedAssets.length == maxAssets) {
if (selectedAssets.contains(item)) {
notifyListeners();
return;
}

if (selectedAssets.contains(item)) {
notifyListeners();
if (selectedAssets.length >= maxAssets) {
return;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/src/provider/asset_picker_viewer_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class AssetPickerViewerProvider<Asset> extends ChangeNotifier {
/// Select asset.
/// 选中资源
void selectAsset(Asset item) {
if (currentlySelectedAssets.length == maxAssets ||
if (currentlySelectedAssets.length >= maxAssets ||
currentlySelectedAssets.contains(item)) {
return;
}
Expand Down
29 changes: 25 additions & 4 deletions lib/src/widget/builder/image_page_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ class _LivePhotoWidgetState extends State<_LivePhotoWidget> {
}

void _handleScrollNotification(ScrollNotification notification) {
if (!mounted) {
return;
}
if (notification is ScrollStartNotification) {
_scrolling = true;
} else if (notification is ScrollEndNotification) {
Expand All @@ -225,6 +228,9 @@ class _LivePhotoWidgetState extends State<_LivePhotoWidget> {
}

void _onVisibilityChanged(VisibilityInfo info) {
if (!mounted) {
return;
}
final fraction = info.visibleFraction;
if (fraction == 1 && !_showVideo.value && !_scrolling) {
_showVideoAndPlay();
Expand All @@ -234,15 +240,21 @@ class _LivePhotoWidgetState extends State<_LivePhotoWidget> {
}

Future<void> _notify() async {
if (_controller.value.position >= _controller.value.duration) {
await _controller.pause();
await _controller.seekTo(Duration.zero);
if (!mounted || _controller.value.position < _controller.value.duration) {
return;
}
await _controller.pause();
if (!mounted) {
return;
}
await _controller.seekTo(Duration.zero);
if (mounted) {
_showVideo.value = false;
}
}

Future<void> _showVideoAndPlay() async {
if (_controller.value.isPlaying) {
if (!mounted || _controller.value.isPlaying) {
return;
}
HapticFeedback.lightImpact();
Expand All @@ -251,9 +263,18 @@ class _LivePhotoWidgetState extends State<_LivePhotoWidget> {
}

Future<void> _hideVideoAndStop() async {
if (!mounted) {
return;
}
_showVideo.value = false;
await Future.delayed(kThemeChangeDuration);
if (!mounted) {
return;
}
await _controller.pause();
if (!mounted) {
return;
}
await _controller.seekTo(Duration.zero);
}

Expand Down
83 changes: 82 additions & 1 deletion test/delegates_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
// Use of this source code is governed by an Apache license that can be found
// in the LICENSE file.

import 'package:flutter/rendering.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';

import 'test_utils.dart';

void main() {
group(AssetPickerTextDelegate, () {
test('returns the default when available', () {
Expand Down Expand Up @@ -60,4 +62,83 @@ void main() {
(PathWrapper<AssetPathEntity> e) => e.path.name == 'Screenshots',
);
});

group(AssetPickerViewerBuilderDelegate, () {
test('notifies the picker provider when selecting the last allowed asset',
() {
final DefaultAssetPickerProvider selectorProvider =
DefaultAssetPickerProvider.forTest(maxAssets: 1);
addTearDown(selectorProvider.dispose);
int notificationCount = 0;
selectorProvider.addListener(() {
notificationCount++;
});
final AssetPickerViewerProvider<AssetEntity> viewerProvider =
AssetPickerViewerProvider<AssetEntity>(
selectorProvider.selectedAssets,
maxAssets: selectorProvider.maxAssets,
);
addTearDown(viewerProvider.dispose);
final DefaultAssetPickerViewerBuilderDelegate<
AssetPickerViewerProvider<AssetEntity>,
DefaultAssetPickerProvider> delegate =
DefaultAssetPickerViewerBuilderDelegate<
AssetPickerViewerProvider<AssetEntity>,
DefaultAssetPickerProvider>(
currentIndex: 0,
previewAssets: <AssetEntity>[testAssetEntity],
themeData: ThemeData(),
provider: viewerProvider,
selectedAssets: selectorProvider.selectedAssets,
selectorProvider: selectorProvider,
maxAssets: selectorProvider.maxAssets,
);

delegate.selectAsset(testAssetEntity);

expect(selectorProvider.selectedAssets, <AssetEntity>[testAssetEntity]);
expect(
viewerProvider.currentlySelectedAssets,
<AssetEntity>[testAssetEntity],
);
expect(notificationCount, 1);
});

test('does not add unselected assets beyond maxAssets', () {
final AssetEntity extraAsset = AssetEntity(
id: 'test-extra',
typeInt: 0,
width: 0,
height: 0,
);
final List<AssetEntity> selectedAssets = <AssetEntity>[testAssetEntity];
final AssetPickerViewerProvider<AssetEntity> viewerProvider =
AssetPickerViewerProvider<AssetEntity>(
selectedAssets,
maxAssets: 1,
);
addTearDown(viewerProvider.dispose);
final DefaultAssetPickerViewerBuilderDelegate<
AssetPickerViewerProvider<AssetEntity>,
DefaultAssetPickerProvider> delegate =
DefaultAssetPickerViewerBuilderDelegate<
AssetPickerViewerProvider<AssetEntity>,
DefaultAssetPickerProvider>(
currentIndex: 0,
previewAssets: <AssetEntity>[testAssetEntity, extraAsset],
themeData: ThemeData(),
provider: viewerProvider,
selectedAssets: selectedAssets,
maxAssets: 1,
);

delegate.selectAsset(extraAsset);

expect(selectedAssets, <AssetEntity>[testAssetEntity]);
expect(
viewerProvider.currentlySelectedAssets,
<AssetEntity>[testAssetEntity],
);
});
});
}