From 4512a3d28bde07e1be38ae47e5e54c4de12900f6 Mon Sep 17 00:00:00 2001 From: Luis Thein Date: Fri, 19 Jul 2019 00:19:46 +0200 Subject: [PATCH 1/3] Added scanDataStream & simplified Widget creation - the QrViewController has a getter that returns a Stream of data that gets updated on every scan result - theres no need to init the QrViewController through a method, because the GlobalKey is generated inside the QrViewWidget - a dispose method was added to correctly dispose the stream --- example/lib/main.dart | 23 ++++++++++------------- lib/qr_code_scanner.dart | 32 +++++++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index cc85f8a..c050628 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; void main() => runApp(MaterialApp(home: QRViewExample())); @@ -19,7 +18,6 @@ class QRViewExample extends StatefulWidget { } class _QRViewExampleState extends State { - final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); var qrText = ""; var flashState = flash_on; var cameraState = front_camera; @@ -32,7 +30,6 @@ class _QRViewExampleState extends State { children: [ Expanded( child: QRView( - key: qrKey, onQRViewCreated: _onQRViewCreated, ), flex: 4, @@ -128,17 +125,17 @@ class _QRViewExampleState extends State { } void _onQRViewCreated(QRViewController controller) { - final channel = controller.channel; - controller.init(qrKey); this.controller = controller; - channel.setMethodCallHandler((MethodCall call) async { - switch (call.method) { - case "onRecognizeQR": - dynamic arguments = call.arguments; - setState(() { - qrText = arguments.toString(); - }); - } + controller.scannedData.listen((scanData) { + setState(() { + qrText = scanData; + }); }); } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } } diff --git a/lib/qr_code_scanner.dart b/lib/qr_code_scanner.dart index 2f1cde2..d944a80 100644 --- a/lib/qr_code_scanner.dart +++ b/lib/qr_code_scanner.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -17,6 +19,7 @@ class QRView extends StatefulWidget { } class _QRViewState extends State { + final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); @override Widget build(BuildContext context) { var androidView = AndroidView( @@ -45,7 +48,7 @@ class _QRViewState extends State { if (widget.onQRViewCreated == null) { return; } - widget.onQRViewCreated(new QRViewController._(id)); + widget.onQRViewCreated(QRViewController._(id, qrKey)); } } @@ -71,18 +74,33 @@ class _CreationParams { } class QRViewController { - QRViewController._(int id) - : channel = MethodChannel('net.touchcapture.qr.flutterqr/qrview_$id'); - final MethodChannel channel; + static const scanMethodCall = "onRecognizeQR"; + + StreamController _scanUpdateController = StreamController(); - void init(GlobalKey qrKey) { + Stream get scannedData => _scanUpdateController.stream; + + 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( + (MethodCall call) async { + switch (call.method) { + case scanMethodCall: + if (call.arguments != null) { + _scanUpdateController.sink.add(call.arguments.toString()); + } + } + }, + ); } + final MethodChannel channel; + void flipCamera() { channel.invokeMethod("flipCamera"); } @@ -98,4 +116,8 @@ class QRViewController { void resumeCamera() { channel.invokeMethod("resumeCamera"); } + + void dispose() { + _scanUpdateController.close(); + } } From 8f1c4256dc6069180a90a2d9aa4520830df9f98c Mon Sep 17 00:00:00 2001 From: Luis Thein Date: Fri, 19 Jul 2019 09:01:07 +0200 Subject: [PATCH 2/3] Globalkey gets added in Widget creation --- example/lib/main.dart | 2 ++ lib/qr_code_scanner.dart | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index c050628..2e01987 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -22,6 +22,7 @@ class _QRViewExampleState extends State { var flashState = flash_on; var cameraState = front_camera; QRViewController controller; + final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); @override Widget build(BuildContext context) { @@ -30,6 +31,7 @@ class _QRViewExampleState extends State { children: [ Expanded( child: QRView( + key: qrKey, onQRViewCreated: _onQRViewCreated, ), flex: 4, diff --git a/lib/qr_code_scanner.dart b/lib/qr_code_scanner.dart index d944a80..e707de1 100644 --- a/lib/qr_code_scanner.dart +++ b/lib/qr_code_scanner.dart @@ -8,8 +8,8 @@ typedef void QRViewCreatedCallback(QRViewController controller); class QRView extends StatefulWidget { const QRView({ - Key key, - this.onQRViewCreated, + @required Key key, + @required this.onQRViewCreated, }) : super(key: key); final QRViewCreatedCallback onQRViewCreated; @@ -19,7 +19,6 @@ class QRView extends StatefulWidget { } class _QRViewState extends State { - final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); @override Widget build(BuildContext context) { var androidView = AndroidView( @@ -48,7 +47,7 @@ class _QRViewState extends State { if (widget.onQRViewCreated == null) { return; } - widget.onQRViewCreated(QRViewController._(id, qrKey)); + widget.onQRViewCreated(QRViewController._(id, widget.key)); } } From 224d1ebb7165bb58621fc817cc7127efdaf599a5 Mon Sep 17 00:00:00 2001 From: Luis Thein Date: Mon, 22 Jul 2019 07:56:53 +0200 Subject: [PATCH 3/3] QRViewController updated - MethodChannel is now handled inside the controller - Removed controller init, pass globalKey as required parameter - Controller now returns a stream of data - Updated README.md + example --- README.md | 60 ++++++++++------ example/lib/main.dart | 151 ++++++++++++++++++++------------------- lib/qr_code_scanner.dart | 24 ++++--- 3 files changed, 132 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index 09332c5..888c668 100644 --- a/README.md +++ b/README.md @@ -60,31 +60,39 @@ class _QRViewExampleState extends State { Widget build(BuildContext context) { return Scaffold( body: Column( - children: [ - QRView( - key: qrKey, - onQRViewCreated: _onQRViewCreated, - ), - ], - ), - ); + children: [ + Expanded( + flex: 5, + child: QRView( + key: qrKey, + onQRViewCreated: _onQRViewCreated, + ), + ), + Expanded( + flex: 1, + child: Center( + child: Text('Scan result: $qrText'), + ), + ) + ], + ), + ); } void _onQRViewCreated(QRViewController controller) { - final channel = controller.channel; - controller.init(qrKey); this.controller = controller; - channel.setMethodCallHandler((MethodCall call) async { - switch (call.method) { - case "onRecognizeQR": - dynamic arguments = call.arguments; - setState(() { - qrText = arguments.toString(); - }); - } + controller.scannedDataStream.listen((scanData) { + setState(() { + qrText = scanData; + }); }); } - + + @override + void dispose() { + controller?.dispose(); + super.dispose(); + } } ``` @@ -104,9 +112,21 @@ controller.flipCamera(); ## Flash (Off/On) By default, flash is OFF. ```dart -controller.flipFlash(); +controller.toggleFlash(); +``` + +## Resume/Pause +Pause camera stream and scanner. +```dart +controller.pause(); +``` +Resume camera stream and scanner. +```dart +controller.resume(); ``` + + # TODO'S: * iOS Native embedding is written to match what is supported in the framework as of the date of publication of this package. It needs to be improved as the framework support improves. * In future, options will be provided for default states. diff --git a/example/lib/main.dart b/example/lib/main.dart index 2e01987..49d97d3 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -37,79 +37,86 @@ class _QRViewExampleState extends State { flex: 4, ), Expanded( - child: Column( - children: [ - Text("This is the result of scan: $qrText"), - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - margin: EdgeInsets.all(8.0), - child: RaisedButton( - onPressed: () { - if (controller != null) { - controller.toggleFlash(); - if (_isFlashOn(flashState)) - setState(() { - flashState = flash_off; - }); - else - setState(() { - flashState = flash_on; - }); - } - }, - child: Text(flashState, style: TextStyle(fontSize: 20)), + child: FittedBox( + fit: BoxFit.contain, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text("This is the result of scan: $qrText"), + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + margin: EdgeInsets.all(8.0), + child: RaisedButton( + onPressed: () { + if (controller != null) { + controller.toggleFlash(); + if (_isFlashOn(flashState)) { + setState(() { + flashState = flash_off; + }); + } else { + setState(() { + flashState = flash_on; + }); + } + } + }, + child: + Text(flashState, style: TextStyle(fontSize: 20)), + ), ), - ), - Container( - margin: EdgeInsets.all(8.0), - child: RaisedButton( - onPressed: () { - if (controller != null) { - controller.flipCamera(); - if (_isBackCamera(cameraState)) - setState(() { - cameraState = front_camera; - }); - else - setState(() { - cameraState = back_camera; - }); - } - }, - child: - Text(cameraState, style: TextStyle(fontSize: 20)), + Container( + margin: EdgeInsets.all(8.0), + child: RaisedButton( + onPressed: () { + if (controller != null) { + controller.flipCamera(); + if (_isBackCamera(cameraState)) { + setState(() { + cameraState = front_camera; + }); + } else { + setState(() { + cameraState = back_camera; + }); + } + } + }, + child: + Text(cameraState, style: TextStyle(fontSize: 20)), + ), + ) + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + margin: EdgeInsets.all(8.0), + child: RaisedButton( + onPressed: () { + controller?.pauseCamera(); + }, + child: Text('pause', style: TextStyle(fontSize: 20)), + ), ), - ) - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - margin: EdgeInsets.only(bottom: 8.0), - child: RaisedButton( - onPressed: () { - controller?.pauseCamera(); - }, - child: Text('pause', style: TextStyle(fontSize: 20)), - ), - ), - Container( - margin: EdgeInsets.only(bottom: 8.0), - child: RaisedButton( - onPressed: () { - controller.resumeCamera(); - }, - child: Text('resume', style: TextStyle(fontSize: 20)), - ), - ) - ], - ), - ], + Container( + margin: EdgeInsets.all(8.0), + child: RaisedButton( + onPressed: () { + controller?.resumeCamera(); + }, + child: Text('resume', style: TextStyle(fontSize: 20)), + ), + ) + ], + ), + ], + ), ), flex: 1, ) @@ -128,7 +135,7 @@ class _QRViewExampleState extends State { void _onQRViewCreated(QRViewController controller) { this.controller = controller; - controller.scannedData.listen((scanData) { + controller.scannedDataStream.listen((scanData) { setState(() { qrText = scanData; }); diff --git a/lib/qr_code_scanner.dart b/lib/qr_code_scanner.dart index e707de1..60e08e3 100644 --- a/lib/qr_code_scanner.dart +++ b/lib/qr_code_scanner.dart @@ -10,7 +10,9 @@ class QRView extends StatefulWidget { const QRView({ @required Key key, @required this.onQRViewCreated, - }) : super(key: key); + }) : assert(key != null), + assert(onQRViewCreated != null), + super(key: key); final QRViewCreatedCallback onQRViewCreated; @@ -75,18 +77,20 @@ class _CreationParams { class QRViewController { static const scanMethodCall = "onRecognizeQR"; + final MethodChannel _channel; + StreamController _scanUpdateController = StreamController(); - Stream get scannedData => _scanUpdateController.stream; + Stream get scannedDataStream => _scanUpdateController.stream; QRViewController._(int id, GlobalKey qrKey) - : channel = MethodChannel('net.touchcapture.qr.flutterqr/qrview_$id') { + : _channel = MethodChannel('net.touchcapture.qr.flutterqr/qrview_$id') { if (defaultTargetPlatform == TargetPlatform.iOS) { final RenderBox renderBox = qrKey.currentContext.findRenderObject(); - channel.invokeMethod("setDimensions", + _channel.invokeMethod("setDimensions", {"width": renderBox.size.width, "height": renderBox.size.height}); } - channel.setMethodCallHandler( + _channel.setMethodCallHandler( (MethodCall call) async { switch (call.method) { case scanMethodCall: @@ -98,22 +102,20 @@ class QRViewController { ); } - final MethodChannel channel; - void flipCamera() { - channel.invokeMethod("flipCamera"); + _channel.invokeMethod("flipCamera"); } void toggleFlash() { - channel.invokeMethod("toggleFlash"); + _channel.invokeMethod("toggleFlash"); } void pauseCamera() { - channel.invokeMethod("pauseCamera"); + _channel.invokeMethod("pauseCamera"); } void resumeCamera() { - channel.invokeMethod("resumeCamera"); + _channel.invokeMethod("resumeCamera"); } void dispose() {