Skip to content

Commit 2b29dfa

Browse files
authored
🎨 Handle camera controller gracefully (#93)
1 parent 4b96a53 commit 2b29dfa

File tree

2 files changed

+20
-37
lines changed

2 files changed

+20
-37
lines changed

lib/src/widgets/camera_picker.dart

Lines changed: 19 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,6 @@ class CameraPickerState extends State<CameraPicker>
153153
/// 当前相机实例的控制器
154154
CameraController get controller => _controller!;
155155
CameraController? _controller;
156-
bool _shouldLockInitialize = false;
157156

158157
/// Available cameras.
159158
/// 可用的相机实例
@@ -318,10 +317,7 @@ class CameraPickerState extends State<CameraPicker>
318317
}
319318
if (state == AppLifecycleState.inactive) {
320319
c.dispose();
321-
} else if (state == AppLifecycleState.resumed && !_shouldLockInitialize) {
322-
// Drop initialize when the controller has been already initialized.
323-
// This will typically resolve the lifecycle issue on iOS when permissions
324-
// are requested for the first time.
320+
} else if (state == AppLifecycleState.resumed) {
325321
initCameras(currentCamera);
326322
}
327323
}
@@ -352,17 +348,20 @@ class CameraPickerState extends State<CameraPicker>
352348

353349
/// Initialize cameras instances.
354350
/// 初始化相机实例
355-
void initCameras([CameraDescription? cameraDescription]) {
351+
Future<void> initCameras([CameraDescription? cameraDescription]) async {
356352
// Save the current controller to a local variable.
357353
final CameraController? c = _controller;
358-
// Then unbind the controller from widgets, which requires a build frame.
354+
// Dispose at last to avoid disposed usage with assertions.
355+
if (c != null) {
356+
_controller = null;
357+
await c.dispose();
358+
}
359+
// Then request a new frame to unbind the controller from elements.
359360
safeSetState(() {
360-
_shouldLockInitialize = true;
361361
_maxAvailableZoom = 1;
362362
_minAvailableZoom = 1;
363363
_currentZoom = 1;
364364
_baseZoom = 1;
365-
_controller = null;
366365
// Meanwhile, cancel the existed exposure point and mode display.
367366
_exposureModeDisplayTimer?.cancel();
368367
_exposurePointDisplayTimer?.cancel();
@@ -374,9 +373,6 @@ class CameraPickerState extends State<CameraPicker>
374373
// **IMPORTANT**: Push methods into a post frame callback, which ensures the
375374
// controller has already unbind from widgets.
376375
useWidgetsBinding().addPostFrameCallback((_) async {
377-
// Dispose at last to avoid disposed usage with assertions.
378-
await c?.dispose();
379-
380376
// When the [cameraDescription] is null, which means this is the first
381377
// time initializing cameras, so available cameras should be fetched.
382378
if (cameraDescription == null) {
@@ -386,7 +382,6 @@ class CameraPickerState extends State<CameraPicker>
386382
// After cameras fetched, judge again with the list is empty or not to
387383
// ensure there is at least an available camera for use.
388384
if (cameraDescription == null && (cameras.isEmpty)) {
389-
_shouldLockInitialize = false;
390385
handleErrorWithHandler(
391386
CameraException(
392387
'No CameraDescription found.',
@@ -408,49 +403,37 @@ class CameraPickerState extends State<CameraPicker>
408403
index = currentCameraIndex;
409404
}
410405
// Initialize the controller with the given resolution preset.
411-
_controller = CameraController(
406+
final CameraController newController = CameraController(
412407
cameraDescription ?? cameras[index],
413408
config.resolutionPreset,
414409
enableAudio: enableAudio,
415410
imageFormatGroup: config.imageFormatGroup,
416-
)..addListener(() {
417-
if (controller.value.hasError) {
418-
handleErrorWithHandler(
419-
CameraException(
420-
'CameraController exception',
421-
controller.value.errorDescription,
422-
),
423-
config.onError,
424-
);
425-
}
426-
});
411+
);
427412

428413
try {
429-
await controller.initialize();
430-
safeSetState(() {});
414+
await newController.initialize();
431415
// Call recording preparation first.
432416
if (shouldPrepareForVideoRecording) {
433-
await controller.prepareForVideoRecording();
417+
await newController.prepareForVideoRecording();
434418
}
435419
// Then call other asynchronous methods.
436420
await Future.wait(<Future<void>>[
437421
if (config.lockCaptureOrientation != null)
438-
controller.lockCaptureOrientation(config.lockCaptureOrientation),
422+
newController.lockCaptureOrientation(config.lockCaptureOrientation),
439423
(() async => _maxAvailableExposureOffset =
440-
await controller.getMaxExposureOffset())(),
424+
await newController.getMaxExposureOffset())(),
441425
(() async => _minAvailableExposureOffset =
442-
await controller.getMinExposureOffset())(),
426+
await newController.getMinExposureOffset())(),
443427
(() async =>
444-
_maxAvailableZoom = await controller.getMaxZoomLevel())(),
428+
_maxAvailableZoom = await newController.getMaxZoomLevel())(),
445429
(() async =>
446-
_minAvailableZoom = await controller.getMinZoomLevel())(),
430+
_minAvailableZoom = await newController.getMinZoomLevel())(),
447431
]);
432+
_controller = newController;
448433
} catch (e, s) {
449434
handleErrorWithHandler(e, config.onError, s: s);
450435
} finally {
451-
safeSetState(() {
452-
_shouldLockInitialize = false;
453-
});
436+
safeSetState(() {});
454437
}
455438
});
456439
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ dependencies:
1212
sdk: flutter
1313

1414
bindings_compatible: ^1.0.1
15-
camera: ^0.9.4+21
15+
camera: ^0.9.6
1616
path: ^1.8.0
1717
path_provider: ^2.0.8
1818
photo_manager: ^2.1.0+2

0 commit comments

Comments
 (0)