-
-
Save CalvinGonsalves/3d01cf88a6660ca87068f17f1c99a5dd to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart'; | |
import 'dart:html' as html; | |
import 'dart:ui' as ui; | |
void main() { | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Demo', | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
), | |
home: MyHomePage(title: 'Flutter Demo Home Page'), | |
); | |
} | |
} | |
class MyHomePage extends StatefulWidget { | |
MyHomePage({Key key, this.title}) : super(key: key); | |
final String title; | |
@override | |
_MyHomePageState createState() => _MyHomePageState(); | |
} | |
class _MyHomePageState extends State<MyHomePage> { | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text(widget.title), | |
), | |
body: Center( | |
child: Container(), | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () async { | |
await showDialog( | |
context: context, | |
builder: (BuildContext context) { | |
return FractionallySizedBox( | |
heightFactor: 0.5, | |
widthFactor: 0.5, | |
child: WebCam(), | |
); | |
}); | |
}, | |
// tooltip: 'Increment', | |
child: Icon(Icons.add), | |
), | |
); | |
} | |
} | |
class WebCam extends StatefulWidget { | |
@override | |
_WebCamState createState() => _WebCamState(); | |
} | |
class _WebCamState extends State<WebCam> { | |
static html.VideoElement _webcamVideoElement = html.VideoElement(); | |
@override | |
void initState() { | |
super.initState(); | |
// Register a webcam | |
// ignore: undefined_prefixed_name | |
ui.platformViewRegistry.registerViewFactory('webcamVideoElement', | |
(int viewId) { | |
getMedia(); | |
return _webcamVideoElement; | |
}); | |
} | |
getMedia() { | |
html.window.navigator.mediaDevices | |
.getUserMedia({"video": true}).then((streamHandle) { | |
_webcamVideoElement | |
..srcObject = streamHandle | |
..autoplay = true; | |
}).catchError((onError) { | |
print(onError); | |
}); | |
} | |
switchCameraOff() { | |
if (_webcamVideoElement.srcObject != null && | |
_webcamVideoElement.srcObject.active) { | |
var tracks = _webcamVideoElement.srcObject.getTracks(); | |
//stopping tracks and setting srcObject to null to switch camera off | |
_webcamVideoElement.srcObject = null; | |
tracks.forEach((track) { | |
track.stop(); | |
}); | |
} | |
} | |
@override | |
void dispose() { | |
switchCameraOff(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: Stack( | |
children: [ | |
Container( | |
child: new HtmlElementView( | |
key: UniqueKey(), | |
viewType: 'webcamVideoElement', | |
)), | |
Container( | |
child: Column( | |
children: [ | |
RaisedButton( | |
child: Text('Play/Pause'), | |
onPressed: () async { | |
if (_webcamVideoElement.paused) { | |
_webcamVideoElement.play(); | |
} else { | |
_webcamVideoElement.pause(); | |
} | |
}, | |
), | |
RaisedButton( | |
child: Text('Switch off'), | |
onPressed: () { | |
switchCameraOff(); | |
}, | |
), | |
RaisedButton( | |
child: Text('Switch on'), | |
onPressed: () { | |
if (_webcamVideoElement.srcObject == null) getMedia(); | |
}, | |
), | |
], | |
), | |
), | |
], | |
), | |
); | |
} | |
} |
Thanks for the Gist!
Put registerViewFactory
in postFrameCallback
like this
WidgetsBinding.instance.addPostFrameCallback((timeStamp) => ui.platformViewRegistry.registerViewFactory('webcamVideoElement', (int viewId) => _webcamVideoElement));
Add getMedia()
, if required. Now it also works without it.
This also avoids having flickering issues when restarting the camera or camera from not stoping.
Thanks for the Gist!
@JayParikh20 Im glad it helped 😃
Put
registerViewFactory
in postFrameCallbacklike this
WidgetsBinding.instance.addPostFrameCallback((timeStamp) => ui.platformViewRegistry.registerViewFactory('webcamVideoElement', (int viewId) => _webcamVideoElement));
Registering it after the first frame will cause an error when rendering HtmlElementView
since viewType of "webcamVideoElement" does not exist yet. Also, I haven't experienced flickering issue when testing on chrome browser.
Made a small change and Im using getUserMedia
on the mediaDevices
instead of navigator
because it is not recommended
Im using
getUserMedia
on themediaDevices
instead ofnavigator
because it is not recommended
Oh got it, thanks for the info!
Registering it after the first frame will cause an error when rendering HtmlElementView since viewType of "webcamVideoElement" does not exist yet
Yes, true. I forgot to mention one more change that I did, made it return SizedBox when the webcamVideoElement viewType
was null (with the help of some variables). The only reason I wanted to add postFramCallback
was to remove getMedia()
in the initState
(to prevent the camera from opening when the page loads). But I guess the gist is perfect the way it is. thanks again! Other people can always look at the comments.
Did you find any way to record it?
For information, I have to add 3 attributes to the HTML element VideoElement
to make it works with Safari browser:
// init the VideoElement
_webcamVideoElement = html.VideoElement()
// this 3 following attributes are mandatory for iOS Safari compatibility, thx to https://leemartin.dev/hello-webrtc-on-safari-11-e8bcb5335295
..setAttribute('autoplay', '')
..setAttribute('muted', '')
..setAttribute('playsinline', '');
Thanks @Thithip
@JayParikh20 I found a way to record the MediaStream. Got some help from link and MediaStream Recording API .
you can add this when you get the MediaStream
from getUserMedia
.
import 'dart:js' as js;
...
//_mediaRecorder is html.MediaRecorder type variable declared
//streamHandle is the MediaStream
_mediaRecorder =
new html.MediaRecorder(streamHandle, {'mimeType': 'video/webm'});
_mediaRecorder.addEventListener('dataavailable',
(html.Event event) async {
final chunks = <html.Blob>[];
Completer<String> _completer = Completer<String>();
final html.Blob blob = js.JsObject.fromBrowserObject(event)['data'];
if (blob.size > 0) {
chunks.add(blob);
}
if (_mediaRecorder.state == 'inactive') {
final blob = html.Blob(chunks, 'video/webm');
_completer.complete(html.Url.createObjectUrlFromBlob(blob));
String pathFile = await _completer.future;
print('url of video :: ${pathFile}');
html.window.open(pathFile, '');
}
});
and you can create two additional button to start the recording _mediaRecorder.start()
and _mediaRecorder.stop()
.
@CalvinGonsalves Awesome! thank you.
Thanks for the Gist, very helpful! ✌️