The QR Code Scanner package for Appeto SDK.
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

332 wiersze
9.7 KiB

  1. import 'dart:async';
  2. import 'package:flutter/foundation.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter/services.dart';
  5. import 'qr_scanner_overlay_shape.dart';
  6. import 'types/barcode.dart';
  7. import 'types/barcode_format.dart';
  8. import 'types/camera.dart';
  9. import 'types/camera_exception.dart';
  10. import 'types/features.dart';
  11. typedef QRViewCreatedCallback = void Function(QRViewController);
  12. typedef PermissionSetCallback = void Function(QRViewController, bool);
  13. /// The [QRView] is the view where the camera and the barcode scanner gets displayed.
  14. class QRView extends StatefulWidget {
  15. const QRView({
  16. @required Key key,
  17. @required this.onQRViewCreated,
  18. this.overlay,
  19. this.overlayMargin = EdgeInsets.zero,
  20. this.cameraFacing = CameraFacing.back,
  21. this.onPermissionSet,
  22. this.showNativeAlertDialog = false,
  23. }) : assert(key != null),
  24. assert(onQRViewCreated != null),
  25. super(key: key);
  26. final QRViewCreatedCallback onQRViewCreated;
  27. final ShapeBorder overlay;
  28. final EdgeInsetsGeometry overlayMargin;
  29. final CameraFacing cameraFacing;
  30. final PermissionSetCallback onPermissionSet;
  31. final bool showNativeAlertDialog;
  32. @override
  33. State<StatefulWidget> createState() => _QRViewState();
  34. }
  35. class _QRViewState extends State<QRView> {
  36. var _channel;
  37. @override
  38. Widget build(BuildContext context) {
  39. return NotificationListener(
  40. onNotification: onNotification,
  41. child: SizeChangedLayoutNotifier(
  42. child: (widget.overlay != null)
  43. ? _getPlatformQrViewWithOverlay()
  44. : _getPlatformQrView(),
  45. ),
  46. );
  47. }
  48. bool onNotification(notification) {
  49. Future.microtask(() => {
  50. QRViewController.updateDimensions(widget.key, _channel,
  51. scanArea: widget.overlay != null
  52. ? (widget.overlay as QrScannerOverlayShape).cutOutSize
  53. : 0.0)
  54. });
  55. return false;
  56. }
  57. Widget _getPlatformQrViewWithOverlay() {
  58. return Stack(
  59. children: [
  60. _getPlatformQrView(),
  61. Container(
  62. padding: widget.overlayMargin,
  63. decoration: ShapeDecoration(
  64. shape: widget.overlay,
  65. ),
  66. )
  67. ],
  68. );
  69. }
  70. Widget _getPlatformQrView() {
  71. Widget _platformQrView;
  72. switch (defaultTargetPlatform) {
  73. case TargetPlatform.android:
  74. _platformQrView = AndroidView(
  75. viewType: 'net.touchcapture.qr.flutterqr/qrview',
  76. onPlatformViewCreated: _onPlatformViewCreated,
  77. creationParams:
  78. _QrCameraSettings(cameraFacing: widget.cameraFacing).toMap(),
  79. creationParamsCodec: StandardMessageCodec(),
  80. );
  81. break;
  82. case TargetPlatform.iOS:
  83. _platformQrView = UiKitView(
  84. viewType: 'net.touchcapture.qr.flutterqr/qrview',
  85. onPlatformViewCreated: _onPlatformViewCreated,
  86. creationParams:
  87. _QrCameraSettings(cameraFacing: widget.cameraFacing).toMap(),
  88. creationParamsCodec: StandardMessageCodec(),
  89. );
  90. break;
  91. default:
  92. throw UnsupportedError(
  93. "Trying to use the default webview implementation for $defaultTargetPlatform but there isn't a default one");
  94. }
  95. return _platformQrView;
  96. }
  97. void _onPlatformViewCreated(int id) {
  98. // We pass the cutout size so that the scanner respects the scan area.
  99. var cutOutSize = 0.0;
  100. if (widget.overlay != null) {
  101. cutOutSize = (widget.overlay as QrScannerOverlayShape).cutOutSize;
  102. }
  103. _channel = MethodChannel('net.touchcapture.qr.flutterqr/qrview_$id');
  104. // Start scan after creation of the view
  105. final controller = QRViewController._(_channel, widget.key, cutOutSize,
  106. widget.onPermissionSet, widget.showNativeAlertDialog)
  107. .._startScan(widget.key, cutOutSize);
  108. // Initialize the controller for controlling the QRView
  109. if (widget.onQRViewCreated != null) {
  110. widget.onQRViewCreated(controller);
  111. }
  112. }
  113. }
  114. class _QrCameraSettings {
  115. _QrCameraSettings({
  116. this.cameraFacing,
  117. });
  118. final CameraFacing cameraFacing;
  119. Map<String, dynamic> toMap() {
  120. return <String, dynamic>{
  121. 'cameraFacing': cameraFacing.index,
  122. };
  123. }
  124. }
  125. const _formatNames = <String, BarcodeFormat>{
  126. 'AZTEC': BarcodeFormat.aztec,
  127. 'CODABAR': BarcodeFormat.codabar,
  128. 'CODE_39': BarcodeFormat.code39,
  129. 'CODE_93': BarcodeFormat.code93,
  130. 'CODE_128': BarcodeFormat.code128,
  131. 'DATA_MATRIX': BarcodeFormat.dataMatrix,
  132. 'EAN_8': BarcodeFormat.ean8,
  133. 'EAN_13': BarcodeFormat.ean13,
  134. 'ITF': BarcodeFormat.itf,
  135. 'MAXICODE': BarcodeFormat.maxicode,
  136. 'PDF_417': BarcodeFormat.pdf417,
  137. 'QR_CODE': BarcodeFormat.qrcode,
  138. 'RSS_14': BarcodeFormat.rss14,
  139. 'RSS_EXPANDED': BarcodeFormat.rssExpanded,
  140. 'UPC_A': BarcodeFormat.upcA,
  141. 'UPC_E': BarcodeFormat.upcE,
  142. 'UPC_EAN_EXTENSION': BarcodeFormat.upcEanExtension,
  143. };
  144. class QRViewController {
  145. QRViewController._(
  146. MethodChannel channel,
  147. GlobalKey qrKey,
  148. double scanArea,
  149. PermissionSetCallback onPermissionSet,
  150. bool showNativeAlertDialogOnError,
  151. ) : _channel = channel {
  152. _channel.setMethodCallHandler((call) async {
  153. switch (call.method) {
  154. case 'onRecognizeQR':
  155. if (call.arguments != null) {
  156. final args = call.arguments as Map;
  157. final code = args['code'] as String;
  158. final rawType = args['type'] as String;
  159. // Raw bytes are only supported by Android.
  160. final rawBytes = args['rawBytes'] as List<int>;
  161. final format = _formatNames[rawType];
  162. if (format != null) {
  163. final barcode = Barcode(code, format, rawBytes);
  164. _scanUpdateController.sink.add(barcode);
  165. } else {
  166. throw Exception('Unexpected barcode type $rawType');
  167. }
  168. }
  169. break;
  170. case 'onPermissionSet':
  171. await getSystemFeatures(); // if we have no permission all features will not be avaible
  172. if (call.arguments != null) {
  173. if (call.arguments as bool) {
  174. _hasPermissions = true;
  175. } else {
  176. _hasPermissions = false;
  177. if (showNativeAlertDialogOnError) {
  178. await showNativeAlertDialog();
  179. }
  180. }
  181. if (onPermissionSet != null) {
  182. onPermissionSet(this, call.arguments as bool);
  183. }
  184. }
  185. break;
  186. }
  187. });
  188. }
  189. final MethodChannel _channel;
  190. final StreamController<Barcode> _scanUpdateController =
  191. StreamController<Barcode>();
  192. Stream<Barcode> get scannedDataStream => _scanUpdateController.stream;
  193. SystemFeatures _features;
  194. bool _hasPermissions;
  195. SystemFeatures get systemFeatures => _features;
  196. bool get hasPermissions => _hasPermissions;
  197. /// Starts the barcode scanner
  198. Future<void> _startScan(
  199. GlobalKey key,
  200. double cutOutSize,
  201. ) async {
  202. // We need to update the dimension before the scan is started.
  203. QRViewController.updateDimensions(key, _channel, scanArea: cutOutSize);
  204. return _channel.invokeMethod('startScan');
  205. }
  206. Future<CameraFacing> getCameraInfo() async {
  207. try {
  208. return CameraFacing
  209. .values[await _channel.invokeMethod('getCameraInfo') as int];
  210. } on PlatformException catch (e) {
  211. throw CameraException(e.code, e.message);
  212. }
  213. }
  214. /// Flips the camera between available modes
  215. Future<CameraFacing> flipCamera() async {
  216. try {
  217. return CameraFacing
  218. .values[await _channel.invokeMethod('flipCamera') as int];
  219. } on PlatformException catch (e) {
  220. throw CameraException(e.code, e.message);
  221. }
  222. }
  223. /// Get flashlight status
  224. Future<bool> getFlashStatus() async {
  225. try {
  226. return await _channel.invokeMethod('getFlashInfo');
  227. } on PlatformException catch (e) {
  228. throw CameraException(e.code, e.message);
  229. }
  230. }
  231. /// Toggles the flashlight between available modes
  232. Future<void> toggleFlash() async {
  233. try {
  234. await _channel.invokeMethod('toggleFlash') as bool;
  235. } on PlatformException catch (e) {
  236. throw CameraException(e.code, e.message);
  237. }
  238. }
  239. /// Pauses barcode scanning
  240. Future<void> pauseCamera() async {
  241. try {
  242. await _channel.invokeMethod('pauseCamera');
  243. } on PlatformException catch (e) {
  244. throw CameraException(e.code, e.message);
  245. }
  246. }
  247. /// Resumes barcode scanning
  248. Future<void> resumeCamera() async {
  249. try {
  250. await _channel.invokeMethod('resumeCamera');
  251. } on PlatformException catch (e) {
  252. throw CameraException(e.code, e.message);
  253. }
  254. }
  255. Future<void> showNativeAlertDialog() async {
  256. try {
  257. await _channel.invokeMethod('showNativeAlertDialog');
  258. } on PlatformException catch (e) {
  259. throw CameraException(e.code, e.message);
  260. }
  261. }
  262. Future<void> setAllowedBarcodeTypes(List<BarcodeFormat> list) async {
  263. try {
  264. await _channel.invokeMethod('setAllowedBarcodeFormats',
  265. list?.map((e) => e.asInt())?.toList() ?? []);
  266. } on PlatformException catch (e) {
  267. throw CameraException(e.code, e.message);
  268. }
  269. }
  270. Future<SystemFeatures> getSystemFeatures() async {
  271. try {
  272. var features =
  273. await _channel.invokeMapMethod<String, dynamic>('getSystemFeatures');
  274. return SystemFeatures.fromJson(features);
  275. } on PlatformException catch (e) {
  276. throw CameraException(e.code, e.message);
  277. }
  278. }
  279. /// Disposes the barcode stream.
  280. void dispose() {
  281. _scanUpdateController.close();
  282. }
  283. /// Updates the view dimensions for iOS.
  284. static void updateDimensions(GlobalKey key, MethodChannel channel,
  285. {double scanArea}) {
  286. if (defaultTargetPlatform == TargetPlatform.iOS) {
  287. final RenderBox renderBox = key.currentContext.findRenderObject();
  288. channel.invokeMethod('setDimensions', {
  289. 'width': renderBox.size.width,
  290. 'height': renderBox.size.height,
  291. 'scanArea': scanArea ?? 0
  292. });
  293. }
  294. }
  295. }