@@ -35,6 +35,7 @@ class CameraPicker extends StatefulWidget {
35
35
this .enableSetExposure = true ,
36
36
this .enableExposureControlOnPoint = true ,
37
37
this .enablePinchToZoom = true ,
38
+ this .enablePullToZoomInRecord = true ,
38
39
this .shouldDeletePreviewFile = false ,
39
40
this .maximumRecordingDuration = const Duration (seconds: 15 ),
40
41
this .theme,
@@ -47,6 +48,7 @@ class CameraPicker extends StatefulWidget {
47
48
assert (onlyEnableRecording != null ),
48
49
assert (enableAudio != null ),
49
50
assert (enablePinchToZoom != null ),
51
+ assert (enablePullToZoomInRecord != null ),
50
52
assert (enableSetExposure != null ),
51
53
assert (enableExposureControlOnPoint != null ),
52
54
assert (shouldDeletePreviewFile != null ),
@@ -96,6 +98,10 @@ class CameraPicker extends StatefulWidget {
96
98
/// 用户是否可以在界面上双指缩放相机对焦
97
99
final bool enablePinchToZoom;
98
100
101
+ /// Whether users can zoom by pulling up when recording video.
102
+ /// 用户是否可以在录制视频时上拉缩放
103
+ final bool enablePullToZoomInRecord;
104
+
99
105
/// Whether the preview file will be delete when pop.
100
106
/// 返回页面时是否删除预览文件
101
107
final bool shouldDeletePreviewFile;
@@ -235,6 +241,10 @@ class CameraPickerState extends State<CameraPicker>
235
241
/// 最后一次手动聚焦的点坐标
236
242
final ValueNotifier <Offset > _lastExposurePoint = ValueNotifier <Offset >(null );
237
243
244
+ /// The last pressed position on the shooting button before starts recording.
245
+ /// 在开始录像前,最后一次在拍照按钮按下的位置
246
+ Offset _lastShootingButtonPressedPosition;
247
+
238
248
/// Current exposure mode.
239
249
/// 当前曝光模式
240
250
final ValueNotifier <ExposureMode > _exposureMode =
@@ -337,6 +347,8 @@ class CameraPickerState extends State<CameraPicker>
337
347
338
348
bool get enablePinchToZoom => widget.enablePinchToZoom;
339
349
350
+ bool get enablePullToZoomInRecord => widget.enablePullToZoomInRecord;
351
+
340
352
bool get shouldDeletePreviewFile => widget.shouldDeletePreviewFile;
341
353
342
354
/// Getter for `widget.maximumRecordingDuration` .
@@ -534,25 +546,33 @@ class CameraPickerState extends State<CameraPicker>
534
546
}
535
547
}
536
548
549
+ Future <void > zoom (double scale) async {
550
+ final double _zoom = (_baseZoom * scale)
551
+ .clamp (_minAvailableZoom, _maxAvailableZoom)
552
+ .toDouble ();
553
+ if (_zoom == _currentZoom) {
554
+ return ;
555
+ }
556
+ _currentZoom = _zoom;
557
+
558
+ await controller.setZoomLevel (_currentZoom);
559
+ }
560
+
537
561
/// Handle when the scale gesture start.
538
562
/// 处理缩放开始的手势
539
563
void _handleScaleStart (ScaleStartDetails details) {
540
564
_baseZoom = _currentZoom;
541
565
}
542
566
543
- /// Handle when the scale details is updating.
544
- /// 处理缩放更新
567
+ /// Handle when the double tap scale details is updating.
568
+ /// 处理双指缩放更新
545
569
Future <void > _handleScaleUpdate (ScaleUpdateDetails details) async {
546
570
// When there are not exactly two fingers on screen don't scale
547
571
if (_pointers != 2 ) {
548
572
return ;
549
573
}
550
574
551
- _currentZoom = (_baseZoom * details.scale)
552
- .clamp (_minAvailableZoom, _maxAvailableZoom)
553
- .toDouble ();
554
-
555
- await controller.setZoomLevel (_currentZoom);
575
+ zoom (details.scale);
556
576
}
557
577
558
578
void _restartPointDisplayTimer () {
@@ -644,6 +664,20 @@ class CameraPickerState extends State<CameraPicker>
644
664
_restartPointDisplayTimer ();
645
665
}
646
666
667
+ void onShootingButtonMove (PointerMoveEvent event) {
668
+ _lastShootingButtonPressedPosition ?? = event.position;
669
+ if (controller.value.isRecordingVideo) {
670
+ // First calculate relative offset.
671
+ final Offset _offset =
672
+ event.position - _lastShootingButtonPressedPosition;
673
+ // Then turn negative,
674
+ // multiply double with 10 * 1.5 - 1 = 14,
675
+ // plus 1 to ensure always scale.
676
+ final double _scale = _offset.dy / Screens .height * - 14 + 1 ;
677
+ zoom (_scale);
678
+ }
679
+ }
680
+
647
681
/// The method to take a picture.
648
682
/// 拍照方法
649
683
///
@@ -696,6 +730,7 @@ class CameraPickerState extends State<CameraPicker>
696
730
void recordDetectionCancel (PointerUpEvent event) {
697
731
_recordDetectTimer? .cancel ();
698
732
if (controller.value.isRecordingVideo) {
733
+ _lastShootingButtonPressedPosition = null ;
699
734
stopRecordingVideo ();
700
735
safeSetState (() {});
701
736
}
@@ -760,17 +795,22 @@ class CameraPickerState extends State<CameraPicker>
760
795
/// This displayed at the top of the screen.
761
796
/// 该区域显示在屏幕上方。
762
797
Widget get settingsAction {
763
- return Padding (
764
- padding: const EdgeInsets .symmetric (horizontal: 12.0 ),
765
- child: Row (
766
- children: < Widget > [
767
- if ((cameras? .length ?? 0 ) > 1 ) switchCamerasButton,
768
- const Spacer (),
769
- _initializeWrapper (
770
- builder: (CameraValue v, __) => switchFlashesButton (v),
798
+ return _initializeWrapper (
799
+ builder: (CameraValue v, __) {
800
+ if (v.isRecordingVideo) {
801
+ return const SizedBox .shrink ();
802
+ }
803
+ return Padding (
804
+ padding: const EdgeInsets .symmetric (horizontal: 12.0 ),
805
+ child: Row (
806
+ children: < Widget > [
807
+ if ((cameras? .length ?? 0 ) > 1 ) switchCamerasButton,
808
+ const Spacer (),
809
+ switchFlashesButton (v),
810
+ ],
771
811
),
772
- ],
773
- ) ,
812
+ );
813
+ } ,
774
814
);
775
815
}
776
816
@@ -881,6 +921,7 @@ class CameraPickerState extends State<CameraPicker>
881
921
return Listener (
882
922
behavior: HitTestBehavior .opaque,
883
923
onPointerUp: enableRecording ? recordDetectionCancel : null ,
924
+ onPointerMove: enablePullToZoomInRecord ? onShootingButtonMove : null ,
884
925
child: InkWell (
885
926
borderRadius: maxBorderRadius,
886
927
onTap: ! onlyEnableRecording ? takePicture : null ,
0 commit comments