From 7027a3e202f31b46c9fc120e4cf5f469114d853d Mon Sep 17 00:00:00 2001 From: Mac Date: Mon, 18 May 2026 10:19:03 +0100 Subject: [PATCH 1/3] Fix camera_web facingMode capability handling --- packages/camera/camera_web/CHANGELOG.md | 4 +- .../integration_test/camera_service_test.dart | 54 +++++++++++++++++++ .../camera_web/lib/src/camera_service.dart | 27 ++++++---- packages/camera/camera_web/pubspec.yaml | 2 +- 4 files changed, 74 insertions(+), 13 deletions(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 46d7958a8eb1..02ff6f1c7600 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 0.3.5+4 +* Fixes a TypeError in `availableCameras` when a browser returns an invalid + `facingMode` capability. * Updates minimum supported SDK version to Flutter 3.35/Dart 3.9. ## 0.3.5+3 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 2805ae069350..acc77979d28b 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 @@ -672,6 +672,60 @@ void main() { expect(facingMode, equals('environment')); }); + testWidgets('returns null ' + 'when the facing mode setting is empty and ' + 'the first facingMode capability is not a JavaScript string', ( + WidgetTester tester, + ) async { + mockVideoTrack.getSettings = () { + return web.MediaTrackSettings(facingMode: ''); + }.toJS; + mockVideoTrack.getCapabilities = () { + return createJSInteropWrapper(FakeMediaTrackCapabilities()) + as web.MediaTrackCapabilities; + }.toJS; + + when( + jsUtil.hasProperty(videoTrack, 'getCapabilities'.toJS), + ).thenReturn(true); + when( + jsUtil.getProperty(any, 'facingMode'.toJS), + ).thenReturn([true.toJS].toJS); + + final String? facingMode = cameraService.getFacingModeForVideoTrack( + videoTrack, + ); + + expect(facingMode, isNull); + }); + + testWidgets('returns null ' + 'when the facing mode setting is empty and ' + 'the facingMode capability is not a JavaScript array', ( + WidgetTester tester, + ) async { + mockVideoTrack.getSettings = () { + return web.MediaTrackSettings(facingMode: ''); + }.toJS; + mockVideoTrack.getCapabilities = () { + return createJSInteropWrapper(FakeMediaTrackCapabilities()) + as web.MediaTrackCapabilities; + }.toJS; + + when( + jsUtil.hasProperty(videoTrack, 'getCapabilities'.toJS), + ).thenReturn(true); + when( + jsUtil.getProperty(any, 'facingMode'.toJS), + ).thenReturn(true.toJS); + + final String? facingMode = cameraService.getFacingModeForVideoTrack( + videoTrack, + ); + + expect(facingMode, isNull); + }); + testWidgets('returns null ' 'when the facing mode setting ' 'and capabilities are empty', (WidgetTester tester) async { diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 9bf588a456da..47ac8ec3b8f6 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -200,19 +200,24 @@ class CameraService { // A list of facing mode capabilities as // the camera may support multiple facing modes. - final List facingModeCapabilities = - videoTrackCapabilities.facingModeNullable?.toDart - .map((JSString e) => e.toDart) - .toList() ?? - []; - - if (facingModeCapabilities.isNotEmpty) { - final String facingModeCapability = facingModeCapabilities.first; - return facingModeCapability; - } else { - // Return null if there are no facing mode capabilities. + final JSAny? facingModeCapabilities = jsUtil.getProperty( + videoTrackCapabilities, + 'facingMode'.toJS, + ); + if (facingModeCapabilities == null || + !facingModeCapabilities.isA()) { return null; } + + final List facingModes = + (facingModeCapabilities as JSArray).toDart; + + if (facingModes.isNotEmpty && facingModes.first.isA()) { + return (facingModes.first! as JSString).toDart; + } + + // Return null if there are no facing mode capabilities. + return null; } return facingMode; diff --git a/packages/camera/camera_web/pubspec.yaml b/packages/camera/camera_web/pubspec.yaml index 4b3603756387..54da0b925efc 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+3 +version: 0.3.5+4 environment: sdk: ^3.9.0 From 68891892150c8c470f5c0930a53f4d1e5047f0bc Mon Sep 17 00:00:00 2001 From: mackings <48776784+mackings@users.noreply.github.com> Date: Mon, 1 Jun 2026 22:01:29 +0100 Subject: [PATCH 2/3] Improve comments for facing mode capabilities Enhance comments for facing mode capabilities handling in camera service. --- .../camera_web/lib/src/camera_service.dart | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart index 47ac8ec3b8f6..079587249adf 100644 --- a/packages/camera/camera_web/lib/src/camera_service.dart +++ b/packages/camera/camera_web/lib/src/camera_service.dart @@ -199,13 +199,19 @@ class CameraService { .getCapabilities(); // A list of facing mode capabilities as - // the camera may support multiple facing modes. - final JSAny? facingModeCapabilities = jsUtil.getProperty( - videoTrackCapabilities, - 'facingMode'.toJS, - ); - if (facingModeCapabilities == null || - !facingModeCapabilities.isA()) { + //The camera may support multiple facing modes. +// Some browsers (e.g., Firefox) do not conform to the MediaTrackCapabilities +// spec and may return `facingMode` as a non-array value (e.g., an empty string, +// a plain object, or a boolean) Rather than the expected DOMString sequence. +// We use jsUtil.getProperty to safely read the raw JS value, then explicitly +// validate it is a JSArray before accessing its elements to prevent a TypeError. + +final JSAny? facingModeCapabilities = jsUtil.getProperty( + videoTrackCapabilities, + 'facingMode'.toJS, +); +if (facingModeCapabilities == null || + !facingModeCapabilities.isA()) { return null; } From dbcd27c74ab23773c1cb1625e5eb21dc5659a667 Mon Sep 17 00:00:00 2001 From: mackings <48776784+mackings@users.noreply.github.com> Date: Mon, 1 Jun 2026 22:09:11 +0100 Subject: [PATCH 3/3] Update CHANGELOG for version 0.3.5+4 Fix TypeError in availableCameras() and update SDK version. --- packages/camera/camera_web/CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/camera/camera_web/CHANGELOG.md b/packages/camera/camera_web/CHANGELOG.md index 824c2f8be031..472186b4b1f4 100644 --- a/packages/camera/camera_web/CHANGELOG.md +++ b/packages/camera/camera_web/CHANGELOG.md @@ -1,11 +1,13 @@ ## 0.3.5+4 +* Fixes a `TypeError` in `availableCameras()` caused by browsers (e.g. Firefox) returning + an invalid `facingMode` capability value instead of the expected `DOMString` sequence. * Updates minimum supported SDK version to Flutter 3.38/Dart 3.10. ## 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. + a nullable getter and null-safe practices. ## 0.3.5+2