Просмотр исходного кода

Scanner overlay

- Added optional parameter overlay to QrCodeScanner.
  - If overlay != null, a shapeBorder gets added on top of the scanner view.
  - Added class QrScannerOverlayShape, which is a customizable overlay.
  - updated example to use an overlay.
flutter-beta
Luis Thein 7 лет назад
Родитель
Сommit
237a2c8bde
3 измененных файлов: 299 добавлений и 99 удалений
  1. +83
    -71
      example/lib/main.dart
  2. +48
    -28
      lib/qr_code_scanner.dart
  3. +168
    -0
      lib/qr_scanner_overlay_shape.dart

+ 83
- 71
example/lib/main.dart Просмотреть файл

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:qr_code_scanner/qr_scanner_overlay_shape.dart';

void main() => runApp(MaterialApp(home: QRViewExample()));

@@ -33,83 +34,94 @@ class _QRViewExampleState extends State<QRViewExample> {
child: QRView(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
overlay: QrScannerOverlayShape(
borderColor: Colors.red,
borderRadius: 10,
borderLength: 30,
borderWidth: 10,
cutOutSize: 300,
),
),
flex: 4,
),
Expanded(
child: Column(
children: <Widget>[
Text("This is the result of scan: $qrText"),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
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(
children: <Widget>[
Text("This is the result of scan: $qrText"),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
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.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 8.0),
child: RaisedButton(
onPressed: () {
controller?.pauseCamera();
},
child: Text('pause', style: TextStyle(fontSize: 20)),
),
),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
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.only(bottom: 8.0),
child: RaisedButton(
onPressed: () {
controller.resumeCamera();
},
child: Text('resume', style: TextStyle(fontSize: 20)),
),
)
],
),
],
),
),
flex: 1,
)


+ 48
- 28
lib/qr_code_scanner.dart Просмотреть файл

@@ -10,10 +10,14 @@ class QRView extends StatefulWidget {
const QRView({
@required Key key,
@required this.onQRViewCreated,
}) : super(key: key);
this.overlay,
}) : assert(key != null),
super(key: key);

final QRViewCreatedCallback onQRViewCreated;

final ShapeBorder overlay;

@override
State<StatefulWidget> createState() => _QRViewState();
}
@@ -21,26 +25,42 @@ class QRView extends StatefulWidget {
class _QRViewState extends State<QRView> {
@override
Widget build(BuildContext context) {
var androidView = AndroidView(
viewType: 'net.touchcapture.qr.flutterqr/qrview',
onPlatformViewCreated: _onPlatformViewCreated,
return Stack(
children: [
_getPlatformQrView(),
widget.overlay != null
? Container(
decoration: ShapeDecoration(
shape: widget.overlay,
),
)
: Container(),
],
);
}

if (defaultTargetPlatform == TargetPlatform.android) {
return androidView;
}

if (defaultTargetPlatform == TargetPlatform.iOS) {
return UiKitView(
viewType: 'net.touchcapture.qr.flutterqr/qrview',
onPlatformViewCreated: _onPlatformViewCreated,
creationParams: _CreationParams.fromWidget(0, 0).toMap(),
creationParamsCodec: StandardMessageCodec(),
);
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 Text(
'$defaultTargetPlatform is not yet supported by the text_view plugin');
return _platformQrView;
}

void _onPlatformViewCreated(int id) {
@@ -79,14 +99,16 @@ class QRViewController {

Stream<String> get scannedData => _scanUpdateController.stream;

QRViewController._(int id, GlobalKey qrKey)
: channel = MethodChannel('net.touchcapture.qr.flutterqr/qrview_$id') {
MethodChannel _channel;

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",
_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 +120,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() {


+ 168
- 0
lib/qr_scanner_overlay_shape.dart Просмотреть файл

@@ -0,0 +1,168 @@
import 'dart:ui';

import 'package:flutter/material.dart';

class QrScannerOverlayShape extends ShapeBorder {
final Color borderColor;
final double borderWidth;
final Color overlayColor;
final double borderRadius;
final double borderLength;
final double cutOutSize;

QrScannerOverlayShape({
this.borderColor = Colors.red,
this.borderWidth = 3.0,
this.overlayColor = const Color.fromRGBO(0, 0, 0, 80),
this.borderRadius = 0,
this.borderLength = 40,
this.cutOutSize = 250,
}) : assert(
cutOutSize != null
? cutOutSize != null
? borderLength <= cutOutSize / 2 + borderWidth * 2
: true
: true,
"Border can't be larger than ${cutOutSize / 2 + borderWidth * 2}");

@override
EdgeInsetsGeometry get dimensions => const EdgeInsets.all(10.0);

@override
Path getInnerPath(Rect rect, {TextDirection textDirection}) {
return Path()
..fillType = PathFillType.evenOdd
..addPath(getOuterPath(rect), Offset.zero);
}

@override
Path getOuterPath(Rect rect, {TextDirection textDirection}) {
Path _getLeftTopPath(Rect rect) {
return Path()
..moveTo(rect.left, rect.bottom)
..lineTo(rect.left, rect.top)
..lineTo(rect.right, rect.top);
}

return _getLeftTopPath(rect)
..lineTo(
rect.right,
rect.bottom,
)
..lineTo(
rect.left,
rect.bottom,
)
..lineTo(
rect.left,
rect.top,
);
}

@override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {
final width = rect.width;
final borderWidthSize = width / 2;
final height = rect.height;
final borderOffset = borderWidth / 2;
final _borderLength = borderLength > cutOutSize / 2 + borderWidth * 2
? borderWidthSize / 2
: borderLength;
final _cutOutSize = cutOutSize != null && cutOutSize < width
? cutOutSize
: width - borderOffset;

final backgroundPaint = Paint()
..color = overlayColor
..style = PaintingStyle.fill;

final borderPaint = Paint()
..color = borderColor
..style = PaintingStyle.stroke
..strokeWidth = borderWidth;

final boxPaint = Paint()
..color = borderColor
..style = PaintingStyle.fill
..blendMode = BlendMode.dstOut;

final cutOutRect = Rect.fromLTWH(
width / 2 - _cutOutSize / 2 + borderOffset,
height / 2 - _cutOutSize / 2 + borderOffset,
_cutOutSize - borderOffset * 2,
_cutOutSize - borderOffset * 2,
);

canvas.saveLayer(
rect,
backgroundPaint,
);

canvas
..drawRect(
rect,
backgroundPaint,
)
// Draw top right corner
..drawRRect(
RRect.fromLTRBAndCorners(
cutOutRect.right - _borderLength,
cutOutRect.top,
cutOutRect.right,
cutOutRect.top + _borderLength,
topRight: Radius.circular(borderRadius),
),
borderPaint,
)
// Draw top left corner
..drawRRect(
RRect.fromLTRBAndCorners(
cutOutRect.left,
cutOutRect.top,
cutOutRect.left + _borderLength,
cutOutRect.top + _borderLength,
topLeft: Radius.circular(borderRadius),
),
borderPaint,
)
// Draw bottom right corner
..drawRRect(
RRect.fromLTRBAndCorners(
cutOutRect.right - _borderLength,
cutOutRect.bottom - _borderLength,
cutOutRect.right,
cutOutRect.bottom,
bottomRight: Radius.circular(borderRadius),
),
borderPaint,
)
// Draw bottom left corner
..drawRRect(
RRect.fromLTRBAndCorners(
cutOutRect.left,
cutOutRect.bottom - _borderLength,
cutOutRect.left + _borderLength,
cutOutRect.bottom,
bottomLeft: Radius.circular(borderRadius),
),
borderPaint,
)
..drawRRect(
RRect.fromRectAndRadius(
cutOutRect,
Radius.circular(borderRadius),
),
boxPaint,
)
..restore();
}

@override
ShapeBorder scale(double t) {
return QrScannerOverlayShape(
borderColor: borderColor,
borderWidth: borderWidth,
overlayColor: overlayColor,
);
}
}

Загрузка…
Отмена
Сохранить