Showing
8 changed files
with
119 additions
and
187 deletions
| @@ -28,6 +28,7 @@ | @@ -28,6 +28,7 @@ | ||
| 28 | 28 | ||
| 29 | <title>example</title> | 29 | <title>example</title> |
| 30 | <link rel="manifest" href="manifest.json"> | 30 | <link rel="manifest" href="manifest.json"> |
| 31 | +<!-- <script src="https://cdn.jsdelivr.net/npm/qr-scanner@1.4.1/qr-scanner.min.js"></script>--> | ||
| 31 | <script src="https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js"></script> | 32 | <script src="https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js"></script> |
| 32 | </head> | 33 | </head> |
| 33 | <body> | 34 | <body> |
| 1 | { | 1 | { |
| 2 | - "name": "example", | ||
| 3 | - "short_name": "example", | 2 | + "name": "Mobile Scanner Example", |
| 3 | + "short_name": "mobile_scanner_example", | ||
| 4 | "start_url": ".", | 4 | "start_url": ".", |
| 5 | "display": "standalone", | 5 | "display": "standalone", |
| 6 | "background_color": "#0175C2", | 6 | "background_color": "#0175C2", |
| 7 | "theme_color": "#0175C2", | 7 | "theme_color": "#0175C2", |
| 8 | - "description": "A new Flutter project.", | 8 | + "description": "A barcode and qr code scanner example.", |
| 9 | "orientation": "portrait-primary", | 9 | "orientation": "portrait-primary", |
| 10 | "prefer_related_applications": false, | 10 | "prefer_related_applications": false, |
| 11 | "icons": [ | 11 | "icons": [ |
| 1 | +import 'package:flutter/foundation.dart'; | ||
| 1 | import 'package:flutter/material.dart'; | 2 | import 'package:flutter/material.dart'; |
| 2 | import 'package:mobile_scanner/mobile_scanner.dart'; | 3 | import 'package:mobile_scanner/mobile_scanner.dart'; |
| 3 | 4 | ||
| @@ -67,7 +68,7 @@ class _MobileScannerState extends State<MobileScanner> | @@ -67,7 +68,7 @@ class _MobileScannerState extends State<MobileScanner> | ||
| 67 | @override | 68 | @override |
| 68 | Widget build(BuildContext context) { | 69 | Widget build(BuildContext context) { |
| 69 | if (kIsWeb) { | 70 | if (kIsWeb) { |
| 70 | - return createWebQrView( | 71 | + return WebScanner( |
| 71 | onDetect: (barcode) => widget.onDetect!(barcode, null), | 72 | onDetect: (barcode) => widget.onDetect!(barcode, null), |
| 72 | cameraFacing: CameraFacing.back, | 73 | cameraFacing: CameraFacing.back, |
| 73 | ); | 74 | ); |
| @@ -105,7 +106,6 @@ class _MobileScannerState extends State<MobileScanner> | @@ -105,7 +106,6 @@ class _MobileScannerState extends State<MobileScanner> | ||
| 105 | ); | 106 | ); |
| 106 | } | 107 | } |
| 107 | }); | 108 | }); |
| 108 | - } | ||
| 109 | }); | 109 | }); |
| 110 | } | 110 | } |
| 111 | } | 111 | } |
| @@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart'; | @@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart'; | ||
| 4 | import 'package:flutter/services.dart'; | 4 | import 'package:flutter/services.dart'; |
| 5 | import 'package:mobile_scanner/mobile_scanner.dart'; | 5 | import 'package:mobile_scanner/mobile_scanner.dart'; |
| 6 | 6 | ||
| 7 | +import 'mobile_scanner_arguments.dart'; | ||
| 7 | import 'objects/barcode_utility.dart'; | 8 | import 'objects/barcode_utility.dart'; |
| 8 | 9 | ||
| 9 | /// The facing of a camera. | 10 | /// The facing of a camera. |
lib/src/web/flutter_qr_stub.dart
deleted
100644 → 0
| @@ -3,7 +3,6 @@ | @@ -3,7 +3,6 @@ | ||
| 3 | import 'dart:async'; | 3 | import 'dart:async'; |
| 4 | import 'dart:core'; | 4 | import 'dart:core'; |
| 5 | import 'dart:html' as html; | 5 | import 'dart:html' as html; |
| 6 | -import 'dart:js_util'; | ||
| 7 | import 'dart:ui' as ui; | 6 | import 'dart:ui' as ui; |
| 8 | 7 | ||
| 9 | import 'package:flutter/material.dart'; | 8 | import 'package:flutter/material.dart'; |
| @@ -18,21 +17,21 @@ import 'media.dart'; | @@ -18,21 +17,21 @@ import 'media.dart'; | ||
| 18 | /// Copyright 2020 @treeder | 17 | /// Copyright 2020 @treeder |
| 19 | /// Copyright 2021 The one with the braid | 18 | /// Copyright 2021 The one with the braid |
| 20 | 19 | ||
| 21 | -class WebQrView extends StatefulWidget { | 20 | +class WebScanner extends StatefulWidget { |
| 22 | final Function(Barcode) onDetect; | 21 | final Function(Barcode) onDetect; |
| 23 | final CameraFacing? cameraFacing; | 22 | final CameraFacing? cameraFacing; |
| 24 | 23 | ||
| 25 | - const WebQrView( | 24 | + const WebScanner( |
| 26 | {Key? key, | 25 | {Key? key, |
| 27 | required this.onDetect, | 26 | required this.onDetect, |
| 28 | this.cameraFacing = CameraFacing.front}) | 27 | this.cameraFacing = CameraFacing.front}) |
| 29 | : super(key: key); | 28 | : super(key: key); |
| 30 | 29 | ||
| 31 | @override | 30 | @override |
| 32 | - _WebQrViewState createState() => _WebQrViewState(); | 31 | + _WebScannerState createState() => _WebScannerState(); |
| 33 | 32 | ||
| 34 | - static html.DivElement vidDiv = | ||
| 35 | - html.DivElement(); // need a global for the registerViewFactory | 33 | + // need a global for the registerViewFactory |
| 34 | + static html.DivElement vidDiv = html.DivElement(); | ||
| 36 | 35 | ||
| 37 | static Future<bool> cameraAvailable() async { | 36 | static Future<bool> cameraAvailable() async { |
| 38 | final sources = | 37 | final sources = |
| @@ -49,50 +48,65 @@ class WebQrView extends StatefulWidget { | @@ -49,50 +48,65 @@ class WebQrView extends StatefulWidget { | ||
| 49 | } | 48 | } |
| 50 | } | 49 | } |
| 51 | 50 | ||
| 52 | -class _WebQrViewState extends State<WebQrView> { | 51 | +class _WebScannerState extends State<WebScanner> { |
| 52 | + // Which way the camera is facing | ||
| 53 | + // late CameraFacing facing; | ||
| 54 | + | ||
| 55 | + // The camera stream to display to the user | ||
| 53 | html.MediaStream? _localStream; | 56 | html.MediaStream? _localStream; |
| 54 | - // html.CanvasElement canvas; | ||
| 55 | - // html.CanvasRenderingContext2D ctx; | 57 | + |
| 58 | + // Check if analyzer is processing barcode | ||
| 56 | bool _currentlyProcessing = false; | 59 | bool _currentlyProcessing = false; |
| 57 | 60 | ||
| 58 | // QRViewControllerWeb? _controller; | 61 | // QRViewControllerWeb? _controller; |
| 59 | 62 | ||
| 60 | - late Size _size = const Size(0, 0); | 63 | + // Set size of the webview |
| 64 | + // Size _size = const Size(0, 0); | ||
| 65 | + | ||
| 66 | + // TODO: Timer for capture? | ||
| 61 | Timer? timer; | 67 | Timer? timer; |
| 62 | - String? code; | 68 | + |
| 69 | + // String? code; | ||
| 70 | + | ||
| 71 | + // TODO: Error message if error | ||
| 63 | String? _errorMsg; | 72 | String? _errorMsg; |
| 73 | + | ||
| 74 | + // Video element to be played on | ||
| 64 | html.VideoElement video = html.VideoElement(); | 75 | html.VideoElement video = html.VideoElement(); |
| 65 | - String viewID = 'QRVIEW-' + DateTime.now().millisecondsSinceEpoch.toString(); | ||
| 66 | 76 | ||
| 67 | - final StreamController<Barcode> _scanUpdateController = | ||
| 68 | - StreamController<Barcode>(); | ||
| 69 | - late CameraFacing facing; | 77 | + // ID of the video feed |
| 78 | + String viewID = | ||
| 79 | + 'WebScanner-' + DateTime.now().millisecondsSinceEpoch.toString(); | ||
| 80 | + | ||
| 81 | + // final StreamController<Barcode> _scanUpdateController = | ||
| 82 | + // StreamController<Barcode>(); | ||
| 70 | 83 | ||
| 84 | + // Timer for interval capture | ||
| 71 | Timer? _frameIntervall; | 85 | Timer? _frameIntervall; |
| 72 | 86 | ||
| 73 | @override | 87 | @override |
| 74 | void initState() { | 88 | void initState() { |
| 75 | super.initState(); | 89 | super.initState(); |
| 90 | + // facing = widget.cameraFacing ?? CameraFacing.front; | ||
| 91 | + WebScanner.vidDiv.children = [video]; | ||
| 76 | 92 | ||
| 77 | - facing = widget.cameraFacing ?? CameraFacing.front; | ||
| 78 | - | ||
| 79 | - // video = html.VideoElement(); | ||
| 80 | - WebQrView.vidDiv.children = [video]; | ||
| 81 | // ignore: UNDEFINED_PREFIXED_NAME | 93 | // ignore: UNDEFINED_PREFIXED_NAME |
| 82 | ui.platformViewRegistry | 94 | ui.platformViewRegistry |
| 83 | - .registerViewFactory(viewID, (int id) => WebQrView.vidDiv); | 95 | + .registerViewFactory(viewID, (int id) => WebScanner.vidDiv); |
| 96 | + | ||
| 84 | // giving JavaScipt some time to process the DOM changes | 97 | // giving JavaScipt some time to process the DOM changes |
| 85 | Timer(const Duration(milliseconds: 500), () { | 98 | Timer(const Duration(milliseconds: 500), () { |
| 86 | start(); | 99 | start(); |
| 87 | }); | 100 | }); |
| 88 | } | 101 | } |
| 89 | 102 | ||
| 103 | + /// Initialize camera and capture frame | ||
| 90 | Future start() async { | 104 | Future start() async { |
| 91 | - await _makeCall(); | 105 | + await _startVideoStream(); |
| 92 | _frameIntervall?.cancel(); | 106 | _frameIntervall?.cancel(); |
| 93 | _frameIntervall = | 107 | _frameIntervall = |
| 94 | Timer.periodic(const Duration(milliseconds: 200), (timer) { | 108 | Timer.periodic(const Duration(milliseconds: 200), (timer) { |
| 95 | - _captureFrame2(); | 109 | + _captureFrame(); |
| 96 | }); | 110 | }); |
| 97 | } | 111 | } |
| 98 | 112 | ||
| @@ -102,7 +116,7 @@ class _WebQrViewState extends State<WebQrView> { | @@ -102,7 +116,7 @@ class _WebQrViewState extends State<WebQrView> { | ||
| 102 | timer = null; | 116 | timer = null; |
| 103 | } | 117 | } |
| 104 | if (_currentlyProcessing) { | 118 | if (_currentlyProcessing) { |
| 105 | - _stopStream(); | 119 | + _stopVideoStream(); |
| 106 | } | 120 | } |
| 107 | } | 121 | } |
| 108 | 122 | ||
| @@ -112,30 +126,42 @@ class _WebQrViewState extends State<WebQrView> { | @@ -112,30 +126,42 @@ class _WebQrViewState extends State<WebQrView> { | ||
| 112 | super.dispose(); | 126 | super.dispose(); |
| 113 | } | 127 | } |
| 114 | 128 | ||
| 115 | - // Platform messages are asynchronous, so we initialize in an async method. | ||
| 116 | - Future<void> _makeCall() async { | ||
| 117 | - if (_localStream != null) { | ||
| 118 | - return; | ||
| 119 | - } | 129 | + /// Starts a video stream if not started already |
| 130 | + Future<void> _startVideoStream() async { | ||
| 131 | + // Check if stream is running | ||
| 132 | + if (_localStream != null) return; | ||
| 120 | 133 | ||
| 121 | try { | 134 | try { |
| 122 | - var constraints = UserMediaOptions( | ||
| 123 | - video: VideoOptions( | ||
| 124 | - facingMode: (facing == CameraFacing.front ? 'user' : 'environment'), | ||
| 125 | - )); | ||
| 126 | - // dart style, not working properly: | ||
| 127 | - // var stream = | ||
| 128 | - // await html.window.navigator.mediaDevices.getUserMedia(constraints); | ||
| 129 | - // straight JS: | ||
| 130 | - var stream = await promiseToFuture(getUserMedia(constraints)); | ||
| 131 | - _localStream = stream; | 135 | + // Check if browser supports multiple camera's and set if supported |
| 136 | + Map? capabilities = | ||
| 137 | + html.window.navigator.mediaDevices?.getSupportedConstraints(); | ||
| 138 | + if (capabilities != null && capabilities['facingMode']) { | ||
| 139 | + UserMediaOptions constraints = UserMediaOptions( | ||
| 140 | + video: VideoOptions( | ||
| 141 | + facingMode: (widget.cameraFacing == CameraFacing.front | ||
| 142 | + ? 'user' | ||
| 143 | + : 'environment'), | ||
| 144 | + width: {'ideal': 4096}, | ||
| 145 | + height: {'ideal': 2160}, | ||
| 146 | + )); | ||
| 147 | + | ||
| 148 | + _localStream = | ||
| 149 | + await html.window.navigator.getUserMedia(video: constraints); | ||
| 150 | + } else { | ||
| 151 | + _localStream = await html.window.navigator.getUserMedia(video: true); | ||
| 152 | + } | ||
| 153 | + | ||
| 132 | video.srcObject = _localStream; | 154 | video.srcObject = _localStream; |
| 133 | - video.setAttribute('playsinline', | ||
| 134 | - 'true'); // required to tell iOS safari we don't want fullscreen | 155 | + |
| 156 | + // required to tell iOS safari we don't want fullscreen | ||
| 157 | + video.setAttribute('playsinline', 'true'); | ||
| 158 | + | ||
| 159 | + // TODO: Check controller | ||
| 135 | // if (_controller == null) { | 160 | // if (_controller == null) { |
| 136 | // _controller = QRViewControllerWeb(this); | 161 | // _controller = QRViewControllerWeb(this); |
| 137 | // widget.onPlatformViewCreated(_controller!); | 162 | // widget.onPlatformViewCreated(_controller!); |
| 138 | // } | 163 | // } |
| 164 | + | ||
| 139 | await video.play(); | 165 | await video.play(); |
| 140 | } catch (e) { | 166 | } catch (e) { |
| 141 | cancel(); | 167 | cancel(); |
| @@ -144,6 +170,7 @@ class _WebQrViewState extends State<WebQrView> { | @@ -144,6 +170,7 @@ class _WebQrViewState extends State<WebQrView> { | ||
| 144 | }); | 170 | }); |
| 145 | return; | 171 | return; |
| 146 | } | 172 | } |
| 173 | + | ||
| 147 | if (!mounted) return; | 174 | if (!mounted) return; |
| 148 | 175 | ||
| 149 | setState(() { | 176 | setState(() { |
| @@ -151,45 +178,43 @@ class _WebQrViewState extends State<WebQrView> { | @@ -151,45 +178,43 @@ class _WebQrViewState extends State<WebQrView> { | ||
| 151 | }); | 178 | }); |
| 152 | } | 179 | } |
| 153 | 180 | ||
| 154 | - Future<void> _stopStream() async { | 181 | + Future<void> _stopVideoStream() async { |
| 155 | try { | 182 | try { |
| 156 | - // await _localStream.dispose(); | 183 | + // Stop the camera stream |
| 157 | _localStream!.getTracks().forEach((track) { | 184 | _localStream!.getTracks().forEach((track) { |
| 158 | if (track.readyState == 'live') { | 185 | if (track.readyState == 'live') { |
| 159 | track.stop(); | 186 | track.stop(); |
| 160 | } | 187 | } |
| 161 | }); | 188 | }); |
| 162 | - // video.stop(); | 189 | + |
| 163 | video.srcObject = null; | 190 | video.srcObject = null; |
| 164 | _localStream = null; | 191 | _localStream = null; |
| 165 | - // _localRenderer.srcObject = null; | ||
| 166 | - // ignore: empty_catches | ||
| 167 | - } catch (e) {} | 192 | + } catch (e) { |
| 193 | + debugPrint('Failed to stop stream: $e'); | ||
| 194 | + } | ||
| 168 | } | 195 | } |
| 169 | 196 | ||
| 170 | - Future<dynamic> _captureFrame2() async { | ||
| 171 | - if (_localStream == null) { | ||
| 172 | - return null; | ||
| 173 | - } | ||
| 174 | - final canvas = | ||
| 175 | - html.CanvasElement(width: video.videoWidth, height: video.videoHeight); | 197 | + Future<dynamic> _captureFrame() async { |
| 198 | + if (_localStream == null) return null; | ||
| 199 | + final canvas = html.CanvasElement(width: video.videoWidth, height: video.videoHeight); | ||
| 176 | final ctx = canvas.context2D; | 200 | final ctx = canvas.context2D; |
| 177 | - // canvas.width = video.videoWidth; | ||
| 178 | - // canvas.height = video.videoHeight; | 201 | + |
| 179 | ctx.drawImage(video, 0, 0); | 202 | ctx.drawImage(video, 0, 0); |
| 180 | final imgData = ctx.getImageData(0, 0, canvas.width!, canvas.height!); | 203 | final imgData = ctx.getImageData(0, 0, canvas.width!, canvas.height!); |
| 181 | 204 | ||
| 182 | - final size = | ||
| 183 | - Size(canvas.width?.toDouble() ?? 0, canvas.height?.toDouble() ?? 0); | ||
| 184 | - if (size != _size) { | ||
| 185 | - setState(() { | ||
| 186 | - _setCanvasSize(size); | ||
| 187 | - }); | ||
| 188 | - } | 205 | + // final size = |
| 206 | + // Size(canvas.width?.toDouble() ?? 0, canvas.height?.toDouble() ?? 0); | ||
| 207 | + // if (size != _size) { | ||
| 208 | + // setState(() { | ||
| 209 | + // _setCanvasSize(size); | ||
| 210 | + // }); | ||
| 211 | + // } | ||
| 212 | + // debugPrint('img.data: ${imgData.data}'); | ||
| 189 | final code = jsQR(imgData.data, canvas.width, canvas.height); | 213 | final code = jsQR(imgData.data, canvas.width, canvas.height); |
| 190 | // ignore: unnecessary_null_comparison | 214 | // ignore: unnecessary_null_comparison |
| 191 | if (code != null) { | 215 | if (code != null) { |
| 192 | - widget.onDetect(Barcode(rawValue: code.data)); | 216 | + debugPrint('CODE: $code'); |
| 217 | + // widget.onDetect(Barcode(rawValue: code.data)); | ||
| 193 | // print('Barcode: ${code.data}'); | 218 | // print('Barcode: ${code.data}'); |
| 194 | // _scanUpdateController | 219 | // _scanUpdateController |
| 195 | // .add(Barcode(rawValue: code.data)); | 220 | // .add(Barcode(rawValue: code.data)); |
| @@ -204,118 +229,14 @@ class _WebQrViewState extends State<WebQrView> { | @@ -204,118 +229,14 @@ class _WebQrViewState extends State<WebQrView> { | ||
| 204 | if (_localStream == null) { | 229 | if (_localStream == null) { |
| 205 | return const Center(child: CircularProgressIndicator()); | 230 | return const Center(child: CircularProgressIndicator()); |
| 206 | } | 231 | } |
| 207 | - return LayoutBuilder( | ||
| 208 | - builder: (context, constraints) { | ||
| 209 | - var zoom = 1.0; | ||
| 210 | - | ||
| 211 | - if (_size.height != 0) zoom = constraints.maxHeight / _size.height; | ||
| 212 | - | ||
| 213 | - if (_size.width != 0) { | ||
| 214 | - final horizontalZoom = constraints.maxWidth / _size.width; | ||
| 215 | - if (horizontalZoom > zoom) { | ||
| 216 | - zoom = horizontalZoom; | ||
| 217 | - } | ||
| 218 | - } | ||
| 219 | 232 | ||
| 220 | - return SizedBox( | ||
| 221 | - width: constraints.maxWidth, | ||
| 222 | - height: constraints.maxHeight, | ||
| 223 | - child: Center( | ||
| 224 | - child: SizedBox.fromSize( | ||
| 225 | - size: _size, | ||
| 226 | - child: Transform.scale( | ||
| 227 | - alignment: Alignment.center, | ||
| 228 | - scale: zoom, | ||
| 229 | - child: HtmlElementView(viewType: viewID), | ||
| 230 | - ), | ||
| 231 | - ), | ||
| 232 | - ), | ||
| 233 | - ); | ||
| 234 | - }, | ||
| 235 | - ); | ||
| 236 | - } | ||
| 237 | - | ||
| 238 | - void _setCanvasSize(ui.Size size) { | ||
| 239 | - setState(() { | ||
| 240 | - _size = size; | ||
| 241 | - }); | 233 | + return SizedBox( |
| 234 | + width: MediaQuery.of(context).size.width, | ||
| 235 | + height: MediaQuery.of(context).size.height, | ||
| 236 | + child: FittedBox( | ||
| 237 | + child: SizedBox( | ||
| 238 | + width: video.videoWidth.toDouble(), | ||
| 239 | + height: video.videoHeight.toDouble(), | ||
| 240 | + child: HtmlElementView(viewType: viewID)))); | ||
| 242 | } | 241 | } |
| 243 | } | 242 | } |
| 244 | -// | ||
| 245 | -// class QRViewControllerWeb implements QRViewController { | ||
| 246 | -// final _WebQrViewState _state; | ||
| 247 | -// | ||
| 248 | -// QRViewControllerWeb(this._state); | ||
| 249 | -// @override | ||
| 250 | -// void dispose() => _state.cancel(); | ||
| 251 | -// | ||
| 252 | -// @override | ||
| 253 | -// Future<CameraFacing> flipCamera() async { | ||
| 254 | -// // TODO: improve error handling | ||
| 255 | -// _state.facing = _state.facing == CameraFacing.front | ||
| 256 | -// ? CameraFacing.back | ||
| 257 | -// : CameraFacing.front; | ||
| 258 | -// await _state.start(); | ||
| 259 | -// return _state.facing; | ||
| 260 | -// } | ||
| 261 | -// | ||
| 262 | -// @override | ||
| 263 | -// Future<CameraFacing> getCameraInfo() async { | ||
| 264 | -// return _state.facing; | ||
| 265 | -// } | ||
| 266 | -// | ||
| 267 | -// @override | ||
| 268 | -// Future<bool?> getFlashStatus() async { | ||
| 269 | -// // TODO: flash is simply not supported by JavaScipt. To avoid issuing applications, we always return it to be off. | ||
| 270 | -// return false; | ||
| 271 | -// } | ||
| 272 | -// | ||
| 273 | -// @override | ||
| 274 | -// Future<SystemFeatures> getSystemFeatures() { | ||
| 275 | -// // TODO: implement getSystemFeatures | ||
| 276 | -// throw UnimplementedError(); | ||
| 277 | -// } | ||
| 278 | -// | ||
| 279 | -// @override | ||
| 280 | -// // TODO: implement hasPermissions. Blocking: WebQrView.cameraAvailable() returns a Future<bool> whereas a bool is required | ||
| 281 | -// bool get hasPermissions => throw UnimplementedError(); | ||
| 282 | -// | ||
| 283 | -// @override | ||
| 284 | -// Future<void> pauseCamera() { | ||
| 285 | -// // TODO: implement pauseCamera | ||
| 286 | -// throw UnimplementedError(); | ||
| 287 | -// } | ||
| 288 | -// | ||
| 289 | -// @override | ||
| 290 | -// Future<void> resumeCamera() { | ||
| 291 | -// // TODO: implement resumeCamera | ||
| 292 | -// throw UnimplementedError(); | ||
| 293 | -// } | ||
| 294 | -// | ||
| 295 | -// @override | ||
| 296 | -// Stream<Barcode> get scannedDataStream => _state._scanUpdateController.stream; | ||
| 297 | -// | ||
| 298 | -// @override | ||
| 299 | -// Future<void> stopCamera() { | ||
| 300 | -// // TODO: implement stopCamera | ||
| 301 | -// throw UnimplementedError(); | ||
| 302 | -// } | ||
| 303 | -// | ||
| 304 | -// @override | ||
| 305 | -// Future<void> toggleFlash() async { | ||
| 306 | -// // TODO: flash is simply not supported by JavaScipt | ||
| 307 | -// return; | ||
| 308 | -// } | ||
| 309 | -// | ||
| 310 | -// @override | ||
| 311 | -// Future<void> scanInvert(bool isScanInvert) { | ||
| 312 | -// // TODO: implement scanInvert | ||
| 313 | -// throw UnimplementedError(); | ||
| 314 | -// } | ||
| 315 | -// } | ||
| 316 | - | ||
| 317 | -Widget createWebQrView({required Function(Barcode) onDetect, CameraFacing? cameraFacing}) => | ||
| 318 | - WebQrView( | ||
| 319 | - onDetect: onDetect, | ||
| 320 | - cameraFacing: cameraFacing, | ||
| 321 | - ); |
| 1 | -// This is here because dart doesn't seem to support this properly | ||
| 2 | -// https://stackoverflow.com/questions/61161135/adding-support-for-navigator-mediadevices-getusermedia-to-dart | 1 | +// // This is here because dart doesn't seem to support this properly |
| 2 | +// // https://stackoverflow.com/questions/61161135/adding-support-for-navigator-mediadevices-getusermedia-to-dart | ||
| 3 | 3 | ||
| 4 | @JS('navigator.mediaDevices') | 4 | @JS('navigator.mediaDevices') |
| 5 | library media_devices; | 5 | library media_devices; |
| @@ -22,9 +22,11 @@ class UserMediaOptions { | @@ -22,9 +22,11 @@ class UserMediaOptions { | ||
| 22 | class VideoOptions { | 22 | class VideoOptions { |
| 23 | external String get facingMode; | 23 | external String get facingMode; |
| 24 | // external DeviceIdOptions get deviceId; | 24 | // external DeviceIdOptions get deviceId; |
| 25 | + external Map get width; | ||
| 26 | + external Map get height; | ||
| 25 | 27 | ||
| 26 | external factory VideoOptions( | 28 | external factory VideoOptions( |
| 27 | - {String? facingMode, DeviceIdOptions? deviceId}); | 29 | + {String? facingMode, DeviceIdOptions? deviceId, Map? width, Map? height}); |
| 28 | } | 30 | } |
| 29 | 31 | ||
| 30 | @JS() | 32 | @JS() |
-
Please register or login to post a comment