Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[camera] Add web support #4240

Merged
merged 25 commits into from
Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6925d10
feat: add web to the example app
bselwe Jul 23, 2021
f318101
docs: update camera README
bselwe Aug 13, 2021
52b8608
docs: update camera platform interface comments
bselwe Aug 13, 2021
85dab25
feat: update example to support web
bselwe Aug 13, 2021
2c58b59
build: update camera pubspec
bselwe Aug 13, 2021
f203e81
docs: add copyright to camera example index.html
bselwe Aug 13, 2021
410e853
[camera_web] Add support for pausing and resuming the camera preview …
bselwe Aug 13, 2021
17b7b2d
Unendorse camera_web for now, but use it in the example app.
ditman Aug 25, 2021
d03e585
Merge branch 'master' into feat/camera-web-example
ditman Aug 25, 2021
277407a
feat: move previous camera controller dispose to finally in case of a…
bselwe Aug 24, 2021
7c1be26
feat: do not rotate camera preview on the web
bselwe Aug 27, 2021
c374b00
Merge branch 'upstream/master' into feat/camera-web-example
bselwe Aug 27, 2021
69fa8d2
feat: use max camera resolution on the web
bselwe Aug 27, 2021
ad50574
docs: update resolution preset comments
bselwe Aug 27, 2021
5983616
Merge branch 'upstream/master' into feat/camera-web-example
bselwe Aug 30, 2021
0254106
revert: #4236
bselwe Aug 30, 2021
75330ed
Merge branch 'upstream/master' into feat/camera-web-example
bselwe Sep 14, 2021
52fca85
feat: use network VideoPlayerController for the camera recordings
bselwe Sep 14, 2021
6a051d9
docs: update readme
bselwe Sep 14, 2021
e08cab9
Merge branch 'flutter:master' into feat/camera-web-example
ditman Sep 17, 2021
4c169f4
Merge branch 'flutter:master' into feat/camera-web-example
ditman Sep 18, 2021
6a146af
Reenable video operations
ditman Sep 18, 2021
d8878c4
Use endorsed version of camera_web
ditman Sep 18, 2021
3f8a625
Endorse camera_web package.
ditman Sep 18, 2021
798443e
Skip android-only tests on web.
ditman Sep 18, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/camera/camera/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.9.4

* Add web support by endorsing `package:camera_web`.

## 0.9.3+1

* Remove iOS 9 availability check around ultra high capture sessions.
Expand Down
7 changes: 6 additions & 1 deletion packages/camera/camera/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera)

A Flutter plugin for iOS and Android allowing access to the device cameras.
A Flutter plugin for iOS, Android and Web allowing access to the device cameras.

*Note*: This plugin is still under development, and some APIs might not be available yet. We are working on a refactor which can be followed here: [issue](https://github.com/flutter/flutter/issues/31225)

Expand Down Expand Up @@ -47,6 +47,11 @@ minSdkVersion 21

It's important to note that the `MediaRecorder` class is not working properly on emulators, as stated in the documentation: https://developer.android.com/reference/android/media/MediaRecorder. Specifically, when recording a video with sound enabled and trying to play it back, the duration won't be correct and you will only see the first frame.

### Web integration

For web integration details, see the
[`camera_web` package](https://pub.dev/packages/camera_web).

### Handling Lifecycle states

As of version [0.5.0](https://github.com/flutter/plugins/blob/master/packages/camera/CHANGELOG.md#050) of the camera plugin, lifecycle changes are no longer handled by the plugin. This means developers are now responsible to control camera resources when the lifecycle state is updated. Failure to do so might lead to unexpected behavior (for example as described in issue [#39109](https://github.com/flutter/flutter/issues/39109)). Handling lifecycle changes can be done by overriding the `didChangeAppLifecycleState` method like so:
Expand Down
86 changes: 56 additions & 30 deletions packages/camera/camera/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'dart:async';
import 'dart:io';

import 'package:camera/camera.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';

Expand Down Expand Up @@ -231,7 +232,14 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
? Container()
: SizedBox(
child: (localVideoController == null)
? Image.file(File(imageFile!.path))
? (
// The captured image on the web contains a network-accessible URL
// pointing to a location within the browser. It may be displayed
// either with Image.network or Image.memory after loading the image
// bytes to memory.
kIsWeb
? Image.network(imageFile!.path)
: Image.file(File(imageFile!.path)))
: Container(
child: Center(
child: AspectRatio(
Expand Down Expand Up @@ -267,17 +275,24 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
color: Colors.blue,
onPressed: controller != null ? onFlashModeButtonPressed : null,
),
IconButton(
icon: Icon(Icons.exposure),
color: Colors.blue,
onPressed:
controller != null ? onExposureModeButtonPressed : null,
),
IconButton(
icon: Icon(Icons.filter_center_focus),
color: Colors.blue,
onPressed: controller != null ? onFocusModeButtonPressed : null,
),
// The exposure and focus mode are currently not supported on the web.
...(!kIsWeb
? [
IconButton(
icon: Icon(Icons.exposure),
color: Colors.blue,
onPressed: controller != null
? onExposureModeButtonPressed
: null,
),
IconButton(
icon: Icon(Icons.filter_center_focus),
color: Colors.blue,
onPressed:
controller != null ? onFocusModeButtonPressed : null,
)
]
: []),
IconButton(
icon: Icon(enableAudio ? Icons.volume_up : Icons.volume_mute),
color: Colors.blue,
Expand Down Expand Up @@ -616,7 +631,7 @@ class _CameraExampleHomeState extends State<CameraExampleHome>

final CameraController cameraController = CameraController(
cameraDescription,
ResolutionPreset.medium,
kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium,
enableAudio: enableAudio,
imageFormatGroup: ImageFormatGroup.jpeg,
);
Expand All @@ -635,12 +650,17 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
try {
await cameraController.initialize();
await Future.wait([
cameraController
.getMinExposureOffset()
.then((value) => _minAvailableExposureOffset = value),
cameraController
.getMaxExposureOffset()
.then((value) => _maxAvailableExposureOffset = value),
// The exposure mode is currently not supported on the web.
...(!kIsWeb
? [
cameraController
.getMinExposureOffset()
.then((value) => _minAvailableExposureOffset = value),
cameraController
.getMaxExposureOffset()
.then((value) => _maxAvailableExposureOffset = value)
]
: []),
cameraController
.getMaxZoomLevel()
.then((value) => _maxAvailableZoom = value),
Expand Down Expand Up @@ -708,16 +728,20 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
}

void onCaptureOrientationLockButtonPressed() async {
if (controller != null) {
final CameraController cameraController = controller!;
if (cameraController.value.isCaptureOrientationLocked) {
await cameraController.unlockCaptureOrientation();
showInSnackBar('Capture orientation unlocked');
} else {
await cameraController.lockCaptureOrientation();
showInSnackBar(
'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}');
try {
if (controller != null) {
final CameraController cameraController = controller!;
if (cameraController.value.isCaptureOrientationLocked) {
await cameraController.unlockCaptureOrientation();
showInSnackBar('Capture orientation unlocked');
} else {
await cameraController.lockCaptureOrientation();
showInSnackBar(
'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}');
}
}
} on CameraException catch (e) {
_showCameraException(e);
}
}

Expand Down Expand Up @@ -916,8 +940,10 @@ class _CameraExampleHomeState extends State<CameraExampleHome>
return;
}

final VideoPlayerController vController =
VideoPlayerController.file(File(videoFile!.path));
final VideoPlayerController vController = kIsWeb
? VideoPlayerController.network(videoFile!.path)
: VideoPlayerController.file(File(videoFile!.path));

videoPlayerListener = () {
if (videoController != null && videoController!.value.size != null) {
// Refreshing the state to update video player with the correct ratio.
Expand Down
Binary file added packages/camera/camera/example/web/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 39 additions & 0 deletions packages/camera/camera/example/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<!-- Copyright 2013 The Flutter Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<html>

<head>
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="An example of the camera on the web.">

<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">

<!-- Favicon -->
<link rel="shortcut icon" type="image/png" href="favicon.png" />

<title>Camera Web Example</title>
<link rel="manifest" href="manifest.json">
</head>

<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
});
}
</script>
<script src="main.dart.js" type="application/javascript"></script>
</body>

</html>
23 changes: 23 additions & 0 deletions packages/camera/camera/example/web/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "camera example",
"short_name": "camera",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "An example of the camera on the web.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
2 changes: 1 addition & 1 deletion packages/camera/camera/lib/src/camera_preview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class CameraPreview extends StatelessWidget {
}

Widget _wrapInRotatedBox({required Widget child}) {
if (defaultTargetPlatform != TargetPlatform.android) {
if (kIsWeb || defaultTargetPlatform != TargetPlatform.android) {
return child;
}

Expand Down
7 changes: 5 additions & 2 deletions packages/camera/camera/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
name: camera
description: A Flutter plugin for getting information about and controlling the
camera on Android and iOS. Supports previewing the camera feed, capturing images, capturing video,
camera on Android, iOS and Web. Supports previewing the camera feed, capturing images, capturing video,
and streaming image buffers to dart.
repository: https://github.com/flutter/plugins/tree/master/packages/camera/camera
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
version: 0.9.3+1
version: 0.9.4

environment:
sdk: ">=2.14.0 <3.0.0"
Expand All @@ -18,9 +18,12 @@ flutter:
pluginClass: CameraPlugin
ios:
pluginClass: CameraPlugin
web:
default_package: camera_web

dependencies:
camera_platform_interface: ^2.1.0
camera_web: ^0.2.1
flutter:
sdk: flutter
pedantic: ^1.10.0
Expand Down
2 changes: 1 addition & 1 deletion packages/camera/camera/test/camera_preview_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ void main() {

debugDefaultTargetPlatformOverride = null;
});
});
}, skip: kIsWeb);

testWidgets('when not on Android there should not be a rotated box',
(WidgetTester tester) async {
Expand Down