@@ -18,6 +18,7 @@ import 'builder/slide_page_transition_builder.dart';
18
18
import 'camera_picker_viewer.dart' ;
19
19
import 'exposure_point_widget.dart' ;
20
20
21
+ const Color _lockedColor = Colors .amber;
21
22
const Duration _kRouteDuration = Duration (milliseconds: 300 );
22
23
23
24
/// Create a camera picker integrate with [CameraDescription] .
@@ -216,6 +217,14 @@ class CameraPickerState extends State<CameraPicker>
216
217
/// 最后一次手动聚焦的点坐标
217
218
final ValueNotifier <Offset > _lastExposurePoint = ValueNotifier <Offset >(null );
218
219
220
+ /// Current exposure mode.
221
+ /// 当前曝光模式
222
+ final ValueNotifier <ExposureMode > _exposureMode =
223
+ ValueNotifier <ExposureMode >(ExposureMode .auto);
224
+
225
+ final ValueNotifier <bool > _isExposureModeDisplays =
226
+ ValueNotifier <bool >(false );
227
+
219
228
/// The [ValueNotifier] to keep the [CameraController] .
220
229
/// 用于保持 [CameraController] 的 [ValueNotifier]
221
230
final ValueNotifier <CameraController > _controllerNotifier =
@@ -272,6 +281,8 @@ class CameraPickerState extends State<CameraPicker>
272
281
/// 用于控制上次手动聚焦点显示的计时器
273
282
Timer _exposurePointDisplayTimer;
274
283
284
+ Timer _exposureModeDisplayTimer;
285
+
275
286
/// The [Timer] for record start detection.
276
287
/// 用于检测是否开始录制的计时器
277
288
///
@@ -359,7 +370,13 @@ class CameraPickerState extends State<CameraPicker>
359
370
}
360
371
WidgetsBinding .instance.removeObserver (this );
361
372
controller? .dispose ();
373
+ _controllerNotifier? .dispose ();
374
+ _currentExposureOffset? .dispose ();
375
+ _lastExposurePoint? .dispose ();
376
+ _exposureMode? .dispose ();
377
+ _isExposureModeDisplays? .dispose ();
362
378
_exposurePointDisplayTimer? .cancel ();
379
+ _exposureModeDisplayTimer? .cancel ();
363
380
_recordDetectTimer? .cancel ();
364
381
_recordCountdownTimer? .cancel ();
365
382
super .dispose ();
@@ -371,17 +388,12 @@ class CameraPickerState extends State<CameraPicker>
371
388
if (controller == null || ! controller.value.isInitialized) {
372
389
return ;
373
390
}
374
- switch (state) {
375
- case AppLifecycleState .resumed:
376
- if (controller != null ) {
377
- initCameras (currentCamera);
378
- }
379
- break ;
380
- case AppLifecycleState .inactive:
381
- case AppLifecycleState .paused:
382
- case AppLifecycleState .detached:
383
- controller? .dispose ();
384
- break ;
391
+ if (state == AppLifecycleState .inactive) {
392
+ controller? .dispose ();
393
+ } else if (state == AppLifecycleState .resumed) {
394
+ if (controller != null ) {
395
+ initCameras (currentCamera);
396
+ }
385
397
}
386
398
}
387
399
@@ -408,7 +420,8 @@ class CameraPickerState extends State<CameraPicker>
408
420
// Then unbind the controller from widgets, which requires a build frame.
409
421
setState (() {
410
422
_controllerNotifier.value = null ;
411
- // Meanwhile, cancel the existed exposure point.
423
+ // Meanwhile, cancel the existed exposure point and mode display.
424
+ _exposureModeDisplayTimer? .cancel ();
412
425
_exposurePointDisplayTimer? .cancel ();
413
426
_lastExposurePoint.value = null ;
414
427
});
@@ -514,6 +527,39 @@ class CameraPickerState extends State<CameraPicker>
514
527
await controller.setZoomLevel (_currentZoom);
515
528
}
516
529
530
+ void _restartPointDisplayTimer () {
531
+ _exposurePointDisplayTimer? .cancel ();
532
+ _exposurePointDisplayTimer = Timer (const Duration (seconds: 5 ), () {
533
+ _lastExposurePoint.value = null ;
534
+ });
535
+ }
536
+
537
+ void _restartModeDisplayTimer () {
538
+ _exposureModeDisplayTimer? .cancel ();
539
+ _exposureModeDisplayTimer = Timer (const Duration (seconds: 2 ), () {
540
+ _isExposureModeDisplays.value = false ;
541
+ });
542
+ }
543
+
544
+ /// Use the specific [mode] to update the exposure mode.
545
+ /// 设置曝光模式
546
+ void switchExposureMode () {
547
+ assert (controller != null );
548
+ if (_exposureMode.value == ExposureMode .auto) {
549
+ _exposureMode.value = ExposureMode .locked;
550
+ } else {
551
+ _exposureMode.value = ExposureMode .auto;
552
+ }
553
+ _exposurePointDisplayTimer? .cancel ();
554
+ if (_exposureMode.value == ExposureMode .auto) {
555
+ _exposurePointDisplayTimer = Timer (const Duration (seconds: 5 ), () {
556
+ _lastExposurePoint.value = null ;
557
+ });
558
+ }
559
+ controller.setExposureMode (_exposureMode.value);
560
+ _restartModeDisplayTimer ();
561
+ }
562
+
517
563
/// Use the [details] point to set exposure and focus.
518
564
/// 通过点击点的 [details] 设置曝光和对焦。
519
565
void setExposurePoint (TapUpDetails details) {
@@ -534,14 +580,15 @@ class CameraPickerState extends State<CameraPicker>
534
580
details.globalPosition.dx,
535
581
details.globalPosition.dy,
536
582
);
537
- _exposurePointDisplayTimer? .cancel ();
538
- _exposurePointDisplayTimer = Timer (const Duration (seconds: 5 ), () {
539
- _lastExposurePoint.value = null ;
540
- });
583
+ _restartPointDisplayTimer ();
541
584
_currentExposureOffset.value = 0 ;
542
585
controller.setExposurePoint (
543
586
_lastExposurePoint.value.scale (1 / Screens .width, 1 / Screens .height),
544
587
);
588
+ if (_exposureMode.value == ExposureMode .locked) {
589
+ _exposureMode.value = ExposureMode .auto;
590
+ }
591
+ _isExposureModeDisplays.value = false ;
545
592
}
546
593
547
594
/// Update the exposure offset using the exposure controller.
@@ -553,6 +600,11 @@ class CameraPickerState extends State<CameraPicker>
553
600
}
554
601
_currentExposureOffset.value = value;
555
602
controller.setExposureOffset (value);
603
+ if (! _isExposureModeDisplays.value) {
604
+ _isExposureModeDisplays.value = true ;
605
+ }
606
+ _restartModeDisplayTimer ();
607
+ _restartPointDisplayTimer ();
556
608
}
557
609
558
610
/// The method to take a picture.
@@ -793,7 +845,7 @@ class CameraPickerState extends State<CameraPicker>
793
845
Screens .width / (isShootingButtonAnimate ? 10 : 35 ),
794
846
),
795
847
decoration: BoxDecoration (
796
- color: theme.canvasColor.withOpacity (0.95 ),
848
+ color: theme.canvasColor.withOpacity (0.85 ),
797
849
shape: BoxShape .circle,
798
850
),
799
851
child: const DecoratedBox (
@@ -821,9 +873,25 @@ class CameraPickerState extends State<CameraPicker>
821
873
);
822
874
}
823
875
824
- Widget _exposureSlider (double size, double height, double gap) {
876
+ Widget _exposureSlider (
877
+ ExposureMode mode,
878
+ double size,
879
+ double height,
880
+ double gap,
881
+ ) {
882
+ final bool isLocked = mode == ExposureMode .locked;
883
+ final Color color = isLocked ? _lockedColor : theme.iconTheme.color;
884
+
825
885
Widget _line () {
826
- return Center (child: Container (width: 1 , color: theme.iconTheme.color));
886
+ return ValueListenableBuilder <bool >(
887
+ valueListenable: _isExposureModeDisplays,
888
+ builder: (_, bool value, Widget child) => AnimatedOpacity (
889
+ duration: _kRouteDuration,
890
+ opacity: value ? 1 : 0 ,
891
+ child: child,
892
+ ),
893
+ child: Center (child: Container (width: 1 , color: color)),
894
+ );
827
895
}
828
896
829
897
return ValueListenableBuilder <double >(
@@ -854,7 +922,7 @@ class CameraPickerState extends State<CameraPicker>
854
922
child: Icon (
855
923
Icons .wb_sunny_outlined,
856
924
size: size,
857
- color: theme.iconTheme. color,
925
+ color: color,
858
926
),
859
927
),
860
928
),
@@ -888,17 +956,40 @@ class CameraPickerState extends State<CameraPicker>
888
956
Widget get _focusingAreaWidget {
889
957
Widget _buildControl (double size, double height) {
890
958
const double _verticalGap = 3 ;
891
- return Column (
892
- children: < Widget > [
893
- SizedBox .fromSize (
894
- size: Size .square (size),
895
- child: Icon (Icons .lock_open_rounded, size: size),
896
- ),
897
- const SizedBox (height: _verticalGap),
898
- Expanded (child: _exposureSlider (size, height, _verticalGap)),
899
- const SizedBox (height: _verticalGap),
900
- SizedBox .fromSize (size: Size .square (size)),
901
- ],
959
+ return ValueListenableBuilder <ExposureMode >(
960
+ valueListenable: _exposureMode,
961
+ builder: (_, ExposureMode mode, __) {
962
+ final bool isLocked = mode == ExposureMode .locked;
963
+ return Column (
964
+ children: < Widget > [
965
+ ValueListenableBuilder <bool >(
966
+ valueListenable: _isExposureModeDisplays,
967
+ builder: (_, bool value, Widget child) => AnimatedOpacity (
968
+ duration: _kRouteDuration,
969
+ opacity: value ? 1 : 0 ,
970
+ child: child,
971
+ ),
972
+ child: GestureDetector (
973
+ onTap: switchExposureMode,
974
+ child: SizedBox .fromSize (
975
+ size: Size .square (size),
976
+ child: Icon (
977
+ isLocked ? Icons .lock_rounded : Icons .lock_open_rounded,
978
+ size: size,
979
+ color: isLocked ? _lockedColor : null ,
980
+ ),
981
+ ),
982
+ ),
983
+ ),
984
+ const SizedBox (height: _verticalGap),
985
+ Expanded (
986
+ child: _exposureSlider (mode, size, height, _verticalGap),
987
+ ),
988
+ const SizedBox (height: _verticalGap),
989
+ SizedBox .fromSize (size: Size .square (size)),
990
+ ],
991
+ );
992
+ },
902
993
);
903
994
}
904
995
@@ -1065,7 +1156,6 @@ class CameraPickerState extends State<CameraPicker>
1065
1156
child: Stack (
1066
1157
children: < Widget > [
1067
1158
Positioned .fill (child: _cameraPreview (context)),
1068
- _focusingAreaWidget,
1069
1159
if (widget.foregroundBuilder != null )
1070
1160
Positioned .fill (
1071
1161
child: widget.foregroundBuilder (value),
@@ -1080,6 +1170,7 @@ class CameraPickerState extends State<CameraPicker>
1080
1170
},
1081
1171
),
1082
1172
if (enableSetExposure) _exposureDetectorWidget (context),
1173
+ _initializeWrapper (builder: (_, __) => _focusingAreaWidget),
1083
1174
SafeArea (
1084
1175
child: Padding (
1085
1176
padding: const EdgeInsets .only (bottom: 20.0 ),
0 commit comments