From 0e5a3916aff7cd20cb8cc993b58f82338e9c72e1 Mon Sep 17 00:00:00 2001 From: Alex Li Date: Sun, 14 Aug 2022 01:07:12 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=96=203.5.0=20(#117)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 17 +++-------- lib/src/internals/methods.dart | 2 ++ lib/src/states/camera_picker_state.dart | 29 ++++++++++--------- .../states/camera_picker_viewer_state.dart | 24 +++++++++++---- lib/src/widgets/camera_progress_button.dart | 4 +-- pubspec.yaml | 5 ++-- 6 files changed, 44 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8eb776..30f1bac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,19 +4,7 @@ that can be found in the LICENSE file. --> # Changelog -## 3.5.0-dev.3 - -### Improvements - -- Prevent switching cameras when taking picture or recording video. (#120) - -## 3.5.0-dev.2 - -### Improvements - -- Re-export `CameraPicker`'s constructor. (#116) - -## 3.5.0-dev.1 +## 3.5.0 ### New features @@ -25,6 +13,9 @@ that can be found in the LICENSE file. --> ### Improvements - Expose multiple internal widgets. (#113) +- Re-export `CameraPicker`'s constructor. (#116) +- Avoid duplicate entity saving. (#117) +- Prevent switching cameras when taking picture or recording video. (#120) ## 3.4.0 diff --git a/lib/src/internals/methods.dart b/lib/src/internals/methods.dart index 5f554f8..fecfedb 100644 --- a/lib/src/internals/methods.dart +++ b/lib/src/internals/methods.dart @@ -27,3 +27,5 @@ void handleErrorWithHandler( } throw e; } + +T? ambiguate(T value) => value; diff --git a/lib/src/states/camera_picker_state.dart b/lib/src/states/camera_picker_state.dart index e81612f..e7e26f8 100644 --- a/lib/src/states/camera_picker_state.dart +++ b/lib/src/states/camera_picker_state.dart @@ -7,7 +7,6 @@ import 'dart:async'; import 'dart:io'; import 'dart:math' as math; -import 'package:bindings_compatible/bindings_compatible.dart'; import 'package:camera/camera.dart'; import 'package:camera_platform_interface/camera_platform_interface.dart'; import 'package:flutter/gestures.dart'; @@ -49,7 +48,9 @@ class CameraPickerState extends State /// 在开始录像前,最后一次在拍照按钮按下的位置 Offset? lastShootingButtonPressedPosition; - final ValueNotifier isExposureModeDisplays = ValueNotifier(false); + /// Whether the focus point is displaying. + /// 是否正在展示当前的聚焦点 + final ValueNotifier isFocusPointDisplays = ValueNotifier(false); /// The controller for the current camera. /// 当前相机实例的控制器 @@ -58,7 +59,7 @@ class CameraPickerState extends State /// Available cameras. /// 可用的相机实例 - late List cameras; + late final List cameras; /// Current exposure offset. /// 当前曝光值 @@ -162,7 +163,7 @@ class CameraPickerState extends State @override void initState() { super.initState(); - useWidgetsBinding().addObserver(this); + ambiguate(WidgetsBinding.instance)?.addObserver(this); // TODO(Alex): Currently hide status bar will cause the viewport shaking on Android. /// Hide system status bar automatically when the platform is not Android. @@ -179,11 +180,11 @@ class CameraPickerState extends State if (!Platform.isAndroid) { SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); } - useWidgetsBinding().removeObserver(this); + ambiguate(WidgetsBinding.instance)?.removeObserver(this); innerController?.dispose(); currentExposureOffset.dispose(); lastExposurePoint.dispose(); - isExposureModeDisplays.dispose(); + isFocusPointDisplays.dispose(); exposurePointDisplayTimer?.cancel(); exposureModeDisplayTimer?.cancel(); recordDetectTimer?.cancel(); @@ -255,7 +256,7 @@ class CameraPickerState extends State }); // **IMPORTANT**: Push methods into a post frame callback, which ensures the // controller has already unbind from widgets. - useWidgetsBinding().addPostFrameCallback((_) async { + ambiguate(WidgetsBinding.instance)?.addPostFrameCallback((_) async { // When the [cameraDescription] is null, which means this is the first // time initializing cameras, so available cameras should be fetched. if (cameraDescription == null) { @@ -264,7 +265,7 @@ class CameraPickerState extends State // After cameras fetched, judge again with the list is empty or not to // ensure there is at least an available camera for use. - if (cameraDescription == null && (cameras.isEmpty)) { + if (cameraDescription == null && cameras.isEmpty) { handleErrorWithHandler( CameraException( 'No CameraDescription found.', @@ -440,7 +441,7 @@ class CameraPickerState extends State void restartDisplayModeDisplayTimer() { exposureModeDisplayTimer?.cancel(); exposureModeDisplayTimer = Timer(const Duration(seconds: 2), () { - isExposureModeDisplays.value = false; + isFocusPointDisplays.value = false; }); } @@ -474,7 +475,7 @@ class CameraPickerState extends State Offset position, BoxConstraints constraints, ) async { - isExposureModeDisplays.value = false; + isFocusPointDisplays.value = false; // Ignore point update when the new point is less than 8% and higher than // 92% of the screen's height. if (position.dy < constraints.maxHeight / 12 || @@ -535,8 +536,8 @@ class CameraPickerState extends State } catch (e, s) { handleErrorWithHandler(e, pickerConfig.onError, s: s); } - if (!isExposureModeDisplays.value) { - isExposureModeDisplays.value = true; + if (!isFocusPointDisplays.value) { + isFocusPointDisplays.value = true; } restartDisplayModeDisplayTimer(); restartExposurePointDisplayTimer(); @@ -1008,7 +1009,7 @@ class CameraPickerState extends State final bool isLocked = mode == ExposureMode.locked; final Color? color = isLocked ? _lockedColor : theme.iconTheme.color; final Widget lineWidget = ValueListenableBuilder( - valueListenable: isExposureModeDisplays, + valueListenable: isFocusPointDisplays, builder: (_, bool value, Widget? child) => AnimatedOpacity( duration: _kDuration, opacity: value ? 1 : 0, @@ -1077,7 +1078,7 @@ class CameraPickerState extends State return Column( children: [ ValueListenableBuilder( - valueListenable: isExposureModeDisplays, + valueListenable: isFocusPointDisplays, builder: (_, bool value, Widget? child) => AnimatedOpacity( duration: _kDuration, opacity: value ? 1 : 0, diff --git a/lib/src/states/camera_picker_viewer_state.dart b/lib/src/states/camera_picker_viewer_state.dart index e320ab5..543e39d 100644 --- a/lib/src/states/camera_picker_viewer_state.dart +++ b/lib/src/states/camera_picker_viewer_state.dart @@ -47,6 +47,9 @@ class CameraPickerViewerState extends State { /// 初始化视频控制器时是否发生错误 bool hasErrorWhenInitializing = false; + /// Whether the saving process is ongoing. + bool isSavingEntity = false; + CameraErrorHandler? get onError => widget.pickerConfig.onError; @override @@ -121,13 +124,23 @@ class CameraPickerViewerState extends State { /// While the entity might returned null, there's no side effects if popping `null` /// because the parent picker will ignore it. Future createAssetEntityAndPop() async { + if (isSavingEntity) { + return; + } + isSavingEntity = true; final CameraPickerViewType viewType = widget.viewType; if (widget.pickerConfig.onEntitySaving != null) { - await widget.pickerConfig.onEntitySaving!( - context, - widget.viewType, - File(widget.previewXFile.path), - ); + try { + await widget.pickerConfig.onEntitySaving!( + context, + widget.viewType, + File(widget.previewXFile.path), + ); + } catch (e, s) { + handleErrorWithHandler(e, widget.pickerConfig.onError, s: s); + } finally { + isSavingEntity = false; + } return; } AssetEntity? entity; @@ -165,6 +178,7 @@ class CameraPickerViewerState extends State { realDebugPrint('Saving entity failed: $e'); handleErrorWithHandler(e, widget.pickerConfig.onError, s: s); } finally { + isSavingEntity = false; if (mounted) { Navigator.of(context).pop(entity); } diff --git a/lib/src/widgets/camera_progress_button.dart b/lib/src/widgets/camera_progress_button.dart index e4035f7..8adcba6 100644 --- a/lib/src/widgets/camera_progress_button.dart +++ b/lib/src/widgets/camera_progress_button.dart @@ -4,10 +4,10 @@ import 'dart:math' as math; -import 'package:bindings_compatible/bindings_compatible.dart'; import 'package:flutter/material.dart'; import '../constants/styles.dart'; +import '../internals/methods.dart'; class CameraProgressButton extends StatefulWidget { const CameraProgressButton({ @@ -41,7 +41,7 @@ class _CircleProgressState extends State @override void initState() { super.initState(); - useWidgetsBinding().addPostFrameCallback((Duration _) { + ambiguate(WidgetsBinding.instance)?.addPostFrameCallback((_) { progressController.forward(); }); } diff --git a/pubspec.yaml b/pubspec.yaml index 3c838b1..08c25a0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: wechat_camera_picker description: A camera picker which is an extension to wechat_assets_picker, but can be run separately. -version: 3.5.0-dev.3 +version: 3.5.0 homepage: https://github.com/fluttercandies/flutter_wechat_camera_picker environment: @@ -11,9 +11,8 @@ dependencies: flutter: sdk: flutter - bindings_compatible: ^1.0.1 camera: ^0.9.6 camera_platform_interface: ^2.1.5 path: ^1.8.0 - photo_manager: ^2.1.0+2 + photo_manager: ^2.2.0 video_player: ^2.3.2