[READ ONLY MIRROR] Open Source TikTok alternative built on AT Protocol github.com/sprksocial/client
flutter atproto video dart
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: resume path for camera provider

+54 -10
+54 -10
lib/src/features/posting/providers/camera_provider.dart
··· 15 15 late final SparkLogger _logger; 16 16 AppLifecycleListener? _lifecycleListener; 17 17 18 + // Track if camera was disposed due to app lifecycle (not user navigation) 19 + bool _wasDisposedByLifecycle = false; 20 + 18 21 @override 19 22 FutureOr<CameraState> build() async { 20 23 _logger = GetIt.instance<LogService>().getLogger('Camera'); 21 24 22 - ref.onDispose(_disposeCamera); 25 + // Only dispose lifecycle listener when provider is permanently disposed 26 + // Don't use _disposeCamera here - we need to keep lifecycle listener 27 + // active so we can detect when app returns to foreground 28 + ref.onDispose(_disposeLifecycleListener); 23 29 24 - // Listen to app lifecycle to dispose camera when backgrounded 25 - _lifecycleListener = AppLifecycleListener( 30 + // Listen to app lifecycle to pause/resume camera 31 + // Note: onHide is for app fully backgrounded (not transient inactive states) 32 + // onShow handles when app returns to foreground 33 + _lifecycleListener ??= AppLifecycleListener( 26 34 onHide: _onAppBackgrounded, 27 - onInactive: _onAppBackgrounded, 35 + onShow: _onAppForegrounded, 28 36 ); 37 + 38 + // If camera was disposed by lifecycle, reinitialize now 39 + if (_wasDisposedByLifecycle && state.value != null) { 40 + _logger.i('Reinitializing camera after app resume'); 41 + _wasDisposedByLifecycle = false; 42 + try { 43 + return await _initializeCamera(); 44 + } catch (e, stackTrace) { 45 + _logger.e( 46 + 'Failed to reinitialize camera after resume', 47 + error: e, 48 + stackTrace: stackTrace, 49 + ); 50 + // Return error state but don't crash 51 + return CameraState( 52 + error: 'Failed to restart camera: $e', 53 + cameras: state.value?.cameras ?? [], 54 + ); 55 + } 56 + } 29 57 30 58 _logger.i('Initializing camera provider'); 31 59 ··· 321 349 Future<void> _disposeCamera() async { 322 350 _logger.d('Disposing camera'); 323 351 324 - // Dispose lifecycle listener to prevent further callbacks 325 - _lifecycleListener?.dispose(); 326 - _lifecycleListener = null; 327 - 328 352 try { 329 353 final currentState = state.value; 330 354 final controller = currentState?.controller; ··· 366 390 } 367 391 } 368 392 393 + /// Disposes the lifecycle listener. Should only be called when provider 394 + /// is being permanently disposed (not for lifecycle pauses). 395 + void _disposeLifecycleListener() { 396 + _lifecycleListener?.dispose(); 397 + _lifecycleListener = null; 398 + } 399 + 369 400 Future<void> _waitForPreviewDetach() async { 370 401 if (!ref.mounted) return; 371 402 ··· 387 418 } 388 419 } 389 420 390 - /// Handles app lifecycle changes when app is backgrounded or becomes inactive. 421 + /// Handles app lifecycle changes when app is fully backgrounded (onHide). 391 422 /// Disposes the camera to free resources and prevent battery drain. 392 423 void _onAppBackgrounded() { 393 - _logger.i('App backgrounded/inactive, disposing camera'); 424 + _logger.i('App backgrounded, disposing camera'); 425 + _wasDisposedByLifecycle = true; 394 426 _disposeCamera(); 427 + } 428 + 429 + /// Handles app lifecycle changes when app returns to foreground (onShow). 430 + /// Triggers a rebuild which will reinitialize the camera. 431 + void _onAppForegrounded() { 432 + _logger.i('App returned to foreground'); 433 + if (_wasDisposedByLifecycle) { 434 + _logger.d('Camera was disposed by lifecycle, will reinitialize'); 435 + // Trigger a rebuild by invalidating the state 436 + // The build method will check _wasDisposedByLifecycle and reinit 437 + ref.invalidateSelf(); 438 + } 395 439 } 396 440 397 441 Future<void> disposeCamera() async {