How to access webcam video stream in Flutter for Web

Pyts Mike
4 min readDec 26, 2019

--

Due to the young age of Flutter for Web probably all of you have been searching for some things which are in common use for web developers but for most of the questions which were raised about particular things in Flutter for Web, you probably will find very few info so it is the right time to fill the gap with accessing webcam video stream.

For that we will need the next packages:

import 'dart:html';
import 'dart:ui' as ui;

I recommend scroll down to the whole code snippet at the end of the article and check it first, later on, all code magic will be more clear.

Let’s consider that all initialization should be done just once and initState() of the State is the right place for that. We start from the easiest part: some widget should help us to insert video into the widget’s tree so usual variable with Widget type will be helpful and another one of VideoElement:

Widget _webcamWidget;
VideoElement _webcamVideoElement;

Now we are on the most interesting point: how to get connected to the user’s webcam and how to transfer video stream to the widget, let’s clear this up:

First, we need a connector between our widget and any video source: tons of pages and official documents navigate us to the class called VideoElement() from the dart:html :

_webcamVideoElement = VideoElement();

But VideoElement() doesn’t extend any of the widget class, so there should be a way to register it somehow or wrap into another Flutter widget, and here is an implementation which takes _webcamVideoElement created earlier and registers it in the ViewFactory with ‘webcamVideoElement’ alias :

ui.platformViewRegistry.registerViewFactory('webcamVideoElement', (int viewId) => _webcamVideoElement);

That probably is part where most of the people stop to investigate, on the moment when this article has been written, Android Studio was displaying static analysis warn that there is no such class inside the dart:ui package, but actually, there is and more to say build is compiling without problems, so don’t be curios is you will see that warning in static code analysis:

Finally, it is time to get widget which we can be inserted into the widget tree, so we will use already registered _webcamVideoElement and attach it to the HtmlElementView() from dart:html and yes, this one extends Widget:

_webcamWidget = HtmlElementView(key: UniqueKey(), viewType: 'webcamVideoElement');

So far, we have a Flutter widget and its connector with HTML. It is time to access Webcam and get a stream from, and this is where dart:html helps us again, we can access connected user media devices from window.navigator and receive MediaStream as a result of the Future + set it as a source of the VideoElement():

window.navigator.getUserMedia(video: true).then((MediaStream stream) {
_webcamVideoElement.srcObject = stream;
});

Last part will be to build simple UI which displays _webcamWidget in the build() method with the exact size and a floating button allowing you to avoid browser security warning, by clicking the button browser is confident that user has already interacted with the window:

@override
Widget build(BuildContext context) => Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Webcam MediaStream:',
style: TextStyle(fontSize: 30, fontStyle: FontStyle.italic),
),
Container(width: 750, height: 750, child: _webcamWidget),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => _webcamVideoElement.srcObject.active ? _webcamVideoElement.play() : _webcamVideoElement.pause(),
tooltip: 'Start stream, stop stream',
child: Icon(Icons.camera_alt),
),
);

Full code snippet:

// ignore: avoid_web_libraries_in_flutter
import 'dart:html';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
void main() => runApp(WebcamApp());class WebcamApp extends StatelessWidget {
@override
Widget build(BuildContext context) => MaterialApp(
home: WebcamPage(),
);
}
class WebcamPage extends StatefulWidget {
@override
_WebcamPageState createState() => _WebcamPageState();
}
class _WebcamPageState extends State<WebcamPage> {
// Webcam widget to insert into the tree
Widget _webcamWidget;
// VideoElement
VideoElement _webcamVideoElement;
@override
void initState() {
super.initState();
// Create a video element which will be provided with stream source
_webcamVideoElement = VideoElement();
// Register an webcam
ui.platformViewRegistry.registerViewFactory('webcamVideoElement', (int viewId) => _webcamVideoElement);
// Create video widget
_webcamWidget = HtmlElementView(key: UniqueKey(), viewType: 'webcamVideoElement');
// Access the webcam stream
window.navigator.getUserMedia(video: true).then((MediaStream stream) {
_webcamVideoElement.srcObject = stream;
});
}
@override
Widget build(BuildContext context) => Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Webcam MediaStream:',
style: TextStyle(fontSize: 30, fontStyle: FontStyle.italic),
),
Container(width: 750, height: 750, child: _webcamWidget),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => _webcamVideoElement.srcObject.active ? _webcamVideoElement.play() : _webcamVideoElement.pause(),
tooltip: 'Start stream, stop stream',
child: Icon(Icons.camera_alt),
),
);
}

I have tested this on my Mac’s built-in webcam and external one connected to the Mac via USB and fortunately both of them were performing well.

That’s all, thanks for reading

--

--