import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; typedef QRViewCreatedCallback = void Function(QRViewController); enum BarcodeFormat { /// Aztec 2D barcode format. aztec, /// CODABAR 1D format. codabar, /// Code 39 1D format. code39, /// Code 93 1D format. code93, /// Code 128 1D format. code128, /// Data Matrix 2D barcode format. dataMatrix, /// EAN-8 1D format. ean8, /// EAN-13 1D format. ean13, /// ITF (Interleaved Two of Five) 1D format. itf, /// MaxiCode 2D barcode format. maxicode, /// PDF417 format. pdf417, /// QR Code 2D barcode format. qrcode, /// RSS 14 rss14, /// RSS EXPANDED rssExpanded, /// UPC-A 1D format. upcA, /// UPC-E 1D format. upcE, /// UPC/EAN extension format. Not a stand-alone format. upcEanExtension } const _formatNames = { 'AZTEC': BarcodeFormat.aztec, 'CODABAR': BarcodeFormat.codabar, 'CODE_39': BarcodeFormat.code39, 'CODE_93': BarcodeFormat.code93, 'CODE_128': BarcodeFormat.code128, 'DATA_MATRIX': BarcodeFormat.dataMatrix, 'EAN_8': BarcodeFormat.ean8, 'EAN_13': BarcodeFormat.ean13, 'ITF': BarcodeFormat.itf, 'MAXICODE': BarcodeFormat.maxicode, 'PDF_417': BarcodeFormat.pdf417, 'QR_CODE': BarcodeFormat.qrcode, 'RSS_14': BarcodeFormat.rss14, 'RSS_EXPANDED': BarcodeFormat.rssExpanded, 'UPC_A': BarcodeFormat.upcA, 'UPC_E': BarcodeFormat.upcE, 'UPC_EAN_EXTENSION': BarcodeFormat.upcEanExtension, }; class Barcode { Barcode(this.code, this.format); final String code; final BarcodeFormat format; } class QRView extends StatefulWidget { const QRView({ @required Key key, @required this.onQRViewCreated, this.overlay, }) : assert(key != null), assert(onQRViewCreated != null), super(key: key); final QRViewCreatedCallback onQRViewCreated; final ShapeBorder overlay; @override State createState() => _QRViewState(); } class _QRViewState extends State { @override Widget build(BuildContext context) { return Stack( children: [ _getPlatformQrView(), if (widget.overlay != null) Container( decoration: ShapeDecoration( shape: widget.overlay, ), ) else Container(), ], ); } Widget _getPlatformQrView() { Widget _platformQrView; switch (defaultTargetPlatform) { case TargetPlatform.android: _platformQrView = AndroidView( viewType: 'net.touchcapture.qr.flutterqr/qrview', onPlatformViewCreated: _onPlatformViewCreated, ); break; case TargetPlatform.iOS: _platformQrView = UiKitView( viewType: 'net.touchcapture.qr.flutterqr/qrview', onPlatformViewCreated: _onPlatformViewCreated, creationParams: _CreationParams.fromWidget(0, 0).toMap(), creationParamsCodec: StandardMessageCodec(), ); break; default: throw UnsupportedError( "Trying to use the default webview implementation for $defaultTargetPlatform but there isn't a default one"); } return _platformQrView; } void _onPlatformViewCreated(int id) { if (widget.onQRViewCreated == null) { return; } widget.onQRViewCreated(QRViewController._(id, widget.key)); } } class _CreationParams { _CreationParams({this.width, this.height}); static _CreationParams fromWidget(double width, double height) { return _CreationParams( width: width, height: height, ); } final double width; final double height; Map toMap() { return { 'width': width, 'height': height, }; } } class QRViewController { QRViewController._(int id, GlobalKey qrKey) : _channel = MethodChannel('net.touchcapture.qr.flutterqr/qrview_$id') { if (defaultTargetPlatform == TargetPlatform.iOS) { final RenderBox renderBox = qrKey.currentContext.findRenderObject(); _channel.invokeMethod('setDimensions', {'width': renderBox.size.width, 'height': renderBox.size.height}); } _channel.setMethodCallHandler( (call) async { switch (call.method) { case scanMethodCall: if (call.arguments != null) { final args = call.arguments as Map; final code = args['code'] as String; final rawType = args['type'] as String; final format = _formatNames[rawType]; if (format != null) { final barcode = Barcode(code, format); _scanUpdateController.sink.add(barcode); } else { throw Exception('Unexpected barcode type $rawType'); } } } }, ); } static const scanMethodCall = 'onRecognizeQR'; final MethodChannel _channel; final StreamController _scanUpdateController = StreamController(); Stream get scannedDataStream => _scanUpdateController.stream; void flipCamera() { _channel.invokeMethod('flipCamera'); } void toggleFlash() { _channel.invokeMethod('toggleFlash'); } void pauseCamera() { _channel.invokeMethod('pauseCamera'); } void resumeCamera() { _channel.invokeMethod('resumeCamera'); } void dispose() { _scanUpdateController.close(); } }