From 1bb6ab5988037543872a8dcb68cec75340f9df1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jalen=20Gabbidon=20=E2=80=93=20Videotape?= Date: Tue, 9 Dec 2025 21:30:15 -0500 Subject: [PATCH 1/3] Fixed crashing null check bug in camera_web because of accessing a non-nullable field on browsers without facingMode available --- packages/camera/camera_web/lib/src/camera_service.dart | 10 +++++----- packages/camera/camera_web/lib/src/pkg_web_tweaks.dart | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 828f5391481..9bf588a456d 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -200,11 +200,11 @@ class CameraService { // A list of facing mode capabilities as // the camera may support multiple facing modes. - final List facingModeCapabilities = videoTrackCapabilities - .facingMode - .toDart - .map((JSString e) => e.toDart) - .toList(); + final List facingModeCapabilities = + videoTrackCapabilities.facingModeNullable?.toDart + .map((JSString e) => e.toDart) + .toList() ?? + []; if (facingModeCapabilities.isNotEmpty) { final String facingModeCapability = facingModeCapabilities.first; diff --git a/packages/camera/camera_web/lib/src/pkg_web_tweaks.dart b/packages/camera/camera_web/lib/src/pkg_web_tweaks.dart index 4657e9fdff9..f5d54d7dfdf 100644 --- a/packages/camera/camera_web/lib/src/pkg_web_tweaks.dart +++ b/packages/camera/camera_web/lib/src/pkg_web_tweaks.dart @@ -31,6 +31,9 @@ extension NonStandardFieldsOnMediaTrackCapabilities on MediaTrackCapabilities { @JS('torch') external JSArray? get torchNullable; + + @JS('facingMode') + external JSArray? get facingModeNullable; } /// Adds missing fields to [MediaTrackSettings] From bac87478a89582ca11552c0c810a172d40c563a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jalen=20Gabbidon=20=E2=80=93=20Videotape?= Date: Tue, 9 Dec 2025 21:30:28 -0500 Subject: [PATCH 2/3] Added pubspec update, changelog update, and added test to dir --- packages/camera/camera_web/CHANGELOG.md | 5 ++++ .../integration_test/camera_service_test.dart | 23 +++++++++++++++++++ .../integration_test/helpers/mocks.dart | 12 ++++++++++ packages/camera/camera_web/pubspec.yaml | 2 +- 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 372808067e5..7cb15bfddd7 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.3.5+3 + +* Fixes camera initialization failure on Safari by fixing a null check operator error using + a nullable getter and null safe practices. + ## 0.3.5+2 * Fixes camera initialization failure on Firefox Android by using `{video: true}` instead diff --git a/packages/camera/camera_web/example/integration_test/camera_service_test.dart b/packages/camera/camera_web/example/integration_test/camera_service_test.dart index fd18ec60225..e5ece4e5794 100644 --- a/packages/camera/camera_web/example/integration_test/camera_service_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_service_test.dart @@ -708,6 +708,29 @@ void main() { expect(facingMode, isNull); }); + + testWidgets('returns null ' + 'when the facing mode setting is empty and ' + 'the facingMode capability is null', (WidgetTester tester) async { + mockVideoTrack.getSettings = () { + return web.MediaTrackSettings(facingMode: ''); + }.toJS; + mockVideoTrack.getCapabilities = () { + // Use the fake class that has no facingMode property + return createJSInteropWrapper(FakeMediaTrackCapabilities()) + as web.MediaTrackCapabilities; + }.toJS; + + when( + jsUtil.hasProperty(videoTrack, 'getCapabilities'.toJS), + ).thenReturn(true); + + final String? facingMode = cameraService.getFacingModeForVideoTrack( + videoTrack, + ); + + expect(facingMode, isNull); + }); }); }); diff --git a/packages/camera/camera_web/example/integration_test/helpers/mocks.dart b/packages/camera/camera_web/example/integration_test/helpers/mocks.dart index 8e191dc9122..cdf96000583 100644 --- a/packages/camera/camera_web/example/integration_test/helpers/mocks.dart +++ b/packages/camera/camera_web/example/integration_test/helpers/mocks.dart @@ -11,6 +11,7 @@ import 'dart:ui'; // ignore_for_file: implementation_imports import 'package:camera_web/src/camera.dart'; import 'package:camera_web/src/camera_service.dart'; +import 'package:camera_web/src/pkg_web_tweaks.dart'; import 'package:camera_web/src/shims/dart_js_util.dart'; import 'package:camera_web/src/types/types.dart'; import 'package:mockito/annotations.dart'; @@ -284,3 +285,14 @@ class MockEventStreamProvider extends Mock as ElementStream; } } + +/// A fake [web.MediaTrackCapabilities] where facingMode is null/undefined. +/// +/// Used to test null-safe handling when the browser doesn't provide +/// the facingMode capability. +@JSExport() +class FakeMediaTrackCapabilities { + + // Dummy property required by @JSExport + bool get dummy => true; +} diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 2826d61702a..59e8a1b1fb4 100644 --- a/packages/camera/camera_web/pubspec.yaml +++ b/packages/camera/camera_web/pubspec.yaml @@ -2,7 +2,7 @@ name: camera_web description: A Flutter plugin for getting information about and controlling the camera on Web. repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22 -version: 0.3.5+2 +version: 0.3.5+3 environment: sdk: ^3.8.0 From 53406ea1d4f6c4bdd68af8e89d7867ebdaab54d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jalen=20Gabbidon=20=E2=80=93=20Videotape?= Date: Tue, 9 Dec 2025 21:39:08 -0500 Subject: [PATCH 3/3] Improved test for camera_web regression by adding a FakeMediaTrackSettings on top of FakeMediaTrackCapabilities --- .../example/integration_test/camera_service_test.dart | 4 ++-- .../example/integration_test/helpers/mocks.dart | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera_web/example/integration_test/camera_service_test.dart b/packages/camera/camera_web/example/integration_test/camera_service_test.dart index e5ece4e5794..2805ae06935 100644 --- a/packages/camera/camera_web/example/integration_test/camera_service_test.dart +++ b/packages/camera/camera_web/example/integration_test/camera_service_test.dart @@ -713,10 +713,10 @@ void main() { 'when the facing mode setting is empty and ' 'the facingMode capability is null', (WidgetTester tester) async { mockVideoTrack.getSettings = () { - return web.MediaTrackSettings(facingMode: ''); + return createJSInteropWrapper(FakeMediaTrackSettings()) + as web.MediaTrackSettings; }.toJS; mockVideoTrack.getCapabilities = () { - // Use the fake class that has no facingMode property return createJSInteropWrapper(FakeMediaTrackCapabilities()) as web.MediaTrackCapabilities; }.toJS; diff --git a/packages/camera/camera_web/example/integration_test/helpers/mocks.dart b/packages/camera/camera_web/example/integration_test/helpers/mocks.dart index cdf96000583..aafdcc0895a 100644 --- a/packages/camera/camera_web/example/integration_test/helpers/mocks.dart +++ b/packages/camera/camera_web/example/integration_test/helpers/mocks.dart @@ -11,7 +11,6 @@ import 'dart:ui'; // ignore_for_file: implementation_imports import 'package:camera_web/src/camera.dart'; import 'package:camera_web/src/camera_service.dart'; -import 'package:camera_web/src/pkg_web_tweaks.dart'; import 'package:camera_web/src/shims/dart_js_util.dart'; import 'package:camera_web/src/types/types.dart'; import 'package:mockito/annotations.dart'; @@ -292,7 +291,16 @@ class MockEventStreamProvider extends Mock /// the facingMode capability. @JSExport() class FakeMediaTrackCapabilities { + // Dummy property required by @JSExport + bool get dummy => true; +} +/// A fake [web.MediaTrackSettings] where facingMode is null/undefined. +/// +/// Used to test null-safe handling when the browser doesn't provide +/// the facingMode capability. +@JSExport() +class FakeMediaTrackSettings { // Dummy property required by @JSExport bool get dummy => true; }