1+ @JS ()
2+ library jsqrscanner;
3+
4+ import 'dart:async' ;
5+ import 'dart:html' ;
6+
7+ import 'package:flutter/services.dart' ;
8+ import 'package:js/js.dart' ;
9+ import 'package:js/js_util.dart' ;
10+ import 'package:flutter_web_plugins/flutter_web_plugins.dart' ;
11+
12+ class BarcodeScanPlugin {
13+ Completer <String > _completer;
14+
15+ static void registerWith (Registrar registrar) {
16+ final MethodChannel channel = MethodChannel (
17+ 'de.mintware.barcode_scan' ,
18+ const StandardMethodCodec (),
19+ registrar.messenger
20+ );
21+ final BarcodeScanPlugin instance = new BarcodeScanPlugin ();
22+ channel.setMethodCallHandler (instance.handleMethodCall);
23+ }
24+
25+ Future <String > handleMethodCall (MethodCall call) async {
26+ _createCSS ();
27+ var script = document.createElement ('script' );
28+ script.setAttribute ('type' , 'text/javascript' );
29+ document.querySelector ('head' ).append (script);
30+ script.setAttribute ('src' , 'https://cdn.jsdelivr.net/gh/jbialobr/JsQRScanner@master/war/js/jsqrscanner.nocache.js' );
31+ _createHTML ();
32+ document.querySelector ('#toolbar p' ).addEventListener ('click' , (event) => _close ());
33+ setProperty (window, 'JsQRScannerReady' , allowInterop (this .scannerReady));
34+ _completer = new Completer <String >();
35+ return _completer.future;
36+ }
37+
38+ void _createCSS () {
39+ var style = document.createElement ('style' );
40+ style.appendText ('''
41+ * {
42+ margin: 0;
43+ padding: 0;
44+ }
45+
46+ body {
47+ overflow: hidden;
48+ }
49+
50+ #container {
51+ position: fixed;
52+ width: 100%;
53+ height: 100%;
54+ background-color: #2F2F2F;
55+ }
56+
57+ video {
58+ position: relative;
59+ top: 0;
60+ left: 0;
61+ height: 100vh;
62+ width: 100vw;
63+ object-fit: cover;
64+ }
65+
66+ #cover {
67+ position: absolute;
68+ top: 50%;
69+ left: 50%;
70+ width: 70vmin;
71+ height: 70vmin;
72+ transform: translateX(-50%) translateY(-50%);
73+ text-align: center;
74+ }
75+
76+ #cover:after {
77+ content:'';
78+ position: absolute;
79+ top: 0;
80+ left: 0;
81+ width: 100%;
82+ height: 100%;
83+ box-shadow: 0 0 0 999px rgba(0, 0, 0, 0.4);
84+ }
85+
86+ #cover img {
87+ width: 100%;
88+ height: 100%;
89+ }
90+
91+ #cover > div {
92+ background-color: #BFE47B;
93+ }
94+
95+ #topleft, #lefttop {
96+ position: absolute;
97+ top: 0;
98+ left: 0;
99+ }
100+
101+ #topleft {
102+ width: 8vmin;
103+ height: 2vmin;
104+ }
105+
106+ #lefttop {
107+ width: 2vmin;
108+ height: 8vmin;
109+ }
110+
111+ #topright, #righttop {
112+ position: absolute;
113+ top: 0;
114+ right: 0;
115+ }
116+
117+ #topright {
118+ width: 8vmin;
119+ height: 2vmin;
120+ }
121+
122+ #righttop {
123+ width: 2vmin;
124+ height: 8vmin;
125+ }
126+
127+ #bottomleft, #leftbottom {
128+ position: absolute;
129+ bottom: 0;
130+ left: 0;
131+ }
132+
133+ #bottomleft {
134+ width: 8vmin;
135+ height: 2vmin;
136+ }
137+
138+ #leftbottom {
139+ width: 2vmin;
140+ height: 8vmin;
141+ }
142+
143+ #bottomright, #rightbottom {
144+ position: absolute;
145+ bottom: 0;
146+ right: 0;
147+ }
148+
149+ #bottomright {
150+ width: 8vmin;
151+ height: 2vmin;
152+ }
153+
154+ #rightbottom {
155+ width: 2vmin;
156+ height: 8vmin;
157+ }
158+
159+ #toolbar {
160+ background-color: #2F2F2F;
161+ }
162+
163+ #toolbar p {
164+ float: left;
165+ font-size: 24px;
166+ color: white;
167+ margin-left: 16px;
168+ padding: 16px;
169+ }
170+
171+ #clear {
172+ clear: both;
173+ }
174+ ''' );
175+ document.querySelector ('head' ).append (style);
176+ }
177+
178+ void _createHTML () {
179+ var containerDiv = document.createElement ('div' );
180+ containerDiv.id = 'container' ;
181+ containerDiv.innerHtml = '''
182+ <div id="toolbar">
183+ <p>X</p>
184+ <div id="clear"></div>
185+ </div>
186+ <div id="scanner"></div>
187+ <div id="cover">
188+ <div id="topleft"></div>
189+ <div id="lefttop"></div>
190+ <div id="topright"></div>
191+ <div id="righttop"></div>
192+ <div id="bottomleft"></div>
193+ <div id="leftbottom"></div>
194+ <div id="bottomright"></div>
195+ <div id="rightbottom"></div>
196+ </div>
197+ ''' ;
198+ document.body.append (containerDiv);
199+ }
200+
201+ void onQRCodeScanned (String scannedText) {
202+ _completer.complete (scannedText);
203+ _close ();
204+ }
205+
206+ void _close () {
207+ document.getElementById ('container' ).remove ();
208+ }
209+
210+ void scannerReady () {
211+ var scanner = JsQRScanner (onQRCodeScanned, provideVideo);
212+ scanner.setSnapImageMaxSize (300 );
213+ var scannerParentElement = document.getElementById ('scanner' );
214+ scanner.appendTo (scannerParentElement);
215+ }
216+
217+ Promise <MediaStream > provideVideo () {
218+ return new Promise <MediaStream >(allowInterop ((resolve, reject) {
219+ window.navigator.getUserMedia (video: true ).then (resolve, onError: reject);
220+ }));
221+ }
222+ }
223+
224+ @JS ()
225+ class JsQRScanner {
226+ external factory JsQRScanner (Function onQRCodeScanned, Function provideVideo);
227+ external setSnapImageMaxSize (int maxSize);
228+ external appendTo (Element element);
229+ }
230+
231+ @JS ()
232+ class Promise <T > {
233+ external Promise (void executor (void resolve (T result), Function reject));
234+ external Promise then (void onFulfilled (T result), [Function onRejected]);
235+ }
0 commit comments