Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update QrEyeStyle Rounded, QrDataModule Rounded & Logo Background #193

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 47 additions & 16 deletions example/lib/main_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,40 @@ class MainScreen extends StatefulWidget {
}

class _MainScreenState extends State<MainScreen> {
double qrSize = 200;
double logoSize = 40;
@override
Widget build(BuildContext context) {
const String message =
const message =
// ignore: lines_longer_than_80_chars
'Hey this is a QR code. Change this value in the main_screen.dart file.';

final FutureBuilder<ui.Image> qrFutureBuilder = FutureBuilder<ui.Image>(
final qrFutureBuilder = FutureBuilder<ui.Image>(
future: _loadOverlayImage(),
builder: (BuildContext ctx, AsyncSnapshot<ui.Image> snapshot) {
const double size = 280.0;
builder: (ctx, snapshot) {
if (!snapshot.hasData) {
return const SizedBox(width: size, height: size);
return SizedBox(width: qrSize, height: qrSize);
}
return CustomPaint(
size: const Size.square(size),
size: Size.square(qrSize),
painter: QrPainter(
data: message,
gapless: true,
version: QrVersions.auto,
eyeStyle: const QrEyeStyle(
eyeShape: QrEyeShape.square,
color: Color(0xff128760),
eyeShape: QrEyeShape.squareRounded,
radius: 15,
color: Colors.green,
),
dataModuleStyle: const QrDataModuleStyle(
dataModuleShape: QrDataModuleShape.circle,
color: Color(0xff1a5441),
dataModuleShape: QrDataModuleShape.squareRounded,
color: Colors.black,
radius: 3,
),
// size: 320.0,
embeddedImage: snapshot.data,
embeddedImageStyle: const QrEmbeddedImageStyle(
size: Size.square(60),
embeddedImageStyle: QrEmbeddedImageStyle(
size: Size.square(logoSize),
),
),
);
Expand All @@ -62,11 +66,39 @@ class _MainScreenState extends State<MainScreen> {
Expanded(
child: Center(
child: SizedBox(
width: 280,
width: qrSize,
child: qrFutureBuilder,
),
),
),
Expanded(
child: Center(
child: SizedBox(
width: qrSize,
child: QrImageView(
data: message,
gapless: true,
version: QrVersions.auto,
eyeStyle: const QrEyeStyle(
eyeShape: QrEyeShape.squareRounded,
radius: 15,
color: Colors.green,
),
dataModuleStyle: const QrDataModuleStyle(
dataModuleShape: QrDataModuleShape.squareRounded,
color: Colors.black,
radius: 3,
),
// size: 320.0,
embeddedImage:
ExactAssetImage('assets/images/4.0x/logo_yakka.png'),
embeddedImageStyle: QrEmbeddedImageStyle(
size: Size.square(logoSize),
),
),
),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 40)
.copyWith(bottom: 40),
Expand All @@ -79,9 +111,8 @@ class _MainScreenState extends State<MainScreen> {
}

Future<ui.Image> _loadOverlayImage() async {
final Completer<ui.Image> completer = Completer<ui.Image>();
final ByteData byteData =
await rootBundle.load('assets/images/4.0x/logo_yakka.png');
final completer = Completer<ui.Image>();
final byteData = await rootBundle.load('assets/images/4.0x/logo_yakka.png');
ui.decodeImageFromList(byteData.buffer.asUint8List(), completer.complete);
return completer.future;
}
Expand Down
3 changes: 1 addition & 2 deletions lib/src/qr_image_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,7 @@ class _QrImageViewState extends State<QrImageView> {
return _errorWidget(context, constraints, _validationResult.error);
}
// no error, build the regular widget
final widgetSize =
widget.size ?? constraints.biggest.shortestSide;
final widgetSize = widget.size ?? constraints.biggest.shortestSide;
if (widget.embeddedImage != null) {
// if requesting to embed an image then we need to load via a
// FutureBuilder because the image provider will be async.
Expand Down
178 changes: 131 additions & 47 deletions lib/src/qr_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import 'dart:async';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:qr/qr.dart';
Expand Down Expand Up @@ -221,20 +222,19 @@ class QrPainter extends CustomPainter {
);

// DEBUG: draw the inner content boundary
// final paint = Paint()..style = ui.PaintingStyle.stroke;
// paint.strokeWidth = 1;
// paint.color = const Color(0x55222222);
// canvas.drawRect(
// Rect.fromLTWH(paintMetrics.inset, paintMetrics.inset,
// paintMetrics.innerContentSize, paintMetrics.innerContentSize),
// paint);
// final paint = Paint()..style = ui.PaintingStyle.stroke;
// paint.strokeWidth = 1;
// paint.color = const Color(0x55222222);
// canvas.drawRect(
// Rect.fromLTWH(paintMetrics.inset, paintMetrics.inset,
// paintMetrics.innerContentSize, paintMetrics.innerContentSize),
// paint);

double left;
double top;
final gap = !gapless ? _gapSize : 0;
// get the painters for the pixel information
final pixelPaint =
_paintCache.firstPaint(QrCodeElement.codePixel);
final pixelPaint = _paintCache.firstPaint(QrCodeElement.codePixel);
if (color != null) {
pixelPaint!.color = color!;
} else {
Expand All @@ -251,8 +251,7 @@ class QrPainter extends CustomPainter {
if (_isFinderPatternPosition(x, y)) {
continue;
}
final paint =
_qrImage.isDark(y, x) ? pixelPaint : emptyPixelPaint;
final paint = _qrImage.isDark(y, x) ? pixelPaint : emptyPixelPaint;
if (paint == null) {
continue;
}
Expand All @@ -273,33 +272,100 @@ class QrPainter extends CustomPainter {
paintMetrics.pixelSize + pixelHTweak,
paintMetrics.pixelSize + pixelVTweak,
);
if (dataModuleStyle.dataModuleShape == QrDataModuleShape.square) {
canvas.drawRect(squareRect, paint);
} else {
final roundedRect = RRect.fromRectAndRadius(
squareRect,
Radius.circular(paintMetrics.pixelSize + pixelHTweak),
);
canvas.drawRRect(roundedRect, paint);

switch (dataModuleStyle.dataModuleShape) {
case QrDataModuleShape.square:
canvas.drawRect(squareRect, paint);
break;
case QrDataModuleShape.squareRounded:
double bottomLeft = 0;
double bottomRight = 0;
double topLeft = 0;
double topRight = 0;
if (_qrImage.isDark(y, x)) {
bottomLeft = dataModuleStyle.radius;
bottomRight = dataModuleStyle.radius;
topLeft = dataModuleStyle.radius;
topRight = dataModuleStyle.radius;
if (true) {
if (y - 1 >= 0 && _qrImage.isDark(y - 1, x)) {
topLeft = topRight = 0;
}
if (y + 1 < _qrImage.moduleCount && _qrImage.isDark(y + 1, x)) {
bottomRight = bottomLeft = 0;
}
if (x + 1 < _qrImage.moduleCount && _qrImage.isDark(y, x + 1)) {
bottomRight = topRight = 0;
}
if (x - 1 >= 0 && _qrImage.isDark(y, x - 1)) {
topLeft = bottomLeft = 0;
}
}
}
final roundedRect = RRect.fromRectAndCorners(
squareRect,
bottomLeft: Radius.circular(bottomLeft),
bottomRight: Radius.circular(bottomRight),
topLeft: Radius.circular(topLeft),
topRight: Radius.circular(topRight),
);
canvas.drawRRect(roundedRect, paint);
break;
case QrDataModuleShape.circle:
final roundedRect = RRect.fromRectAndRadius(squareRect,
Radius.circular(paintMetrics.pixelSize + pixelHTweak));
canvas.drawRRect(roundedRect, paint);
break;
default:
final roundedRect = RRect.fromRectAndRadius(squareRect,
Radius.circular(paintMetrics.pixelSize + pixelHTweak));
canvas.drawRRect(roundedRect, paint);
}
}
}

if (embeddedImage != null) {
/// background
final originalSizeBackground = Size(
embeddedImage!.width.toDouble() + 10,
embeddedImage!.height.toDouble() + 10,
);
final requestedSizeBackground = embeddedImageStyle != null
? Size(
embeddedImageStyle!.size!.width + 10,
embeddedImageStyle!.size!.height + 10,
)
: null;
final imageSizeBackground = _scaledAspectSize(
size, originalSizeBackground, requestedSizeBackground);
final positionBackground = Offset(
(size.width - imageSizeBackground.width) / 2.0,
(size.height - imageSizeBackground.height) / 2.0,
);
// draw the image overlay.
_drawImageOverlay(
canvas,
positionBackground,
imageSizeBackground,
QrEmbeddedImageStyle(
color: embeddedImageStyle?.color ?? Colors.white,
),
);

/// image
final originalSize = Size(
embeddedImage!.width.toDouble(),
embeddedImage!.height.toDouble(),
);
final requestedSize =
embeddedImageStyle != null ? embeddedImageStyle!.size : null;
final imageSize =
_scaledAspectSize(size, originalSize, requestedSize);
final imageSize = _scaledAspectSize(size, originalSize, requestedSize);
final position = Offset(
(size.width - imageSize.width) / 2.0,
(size.height - imageSize.height) / 2.0,
);
// draw the image overlay.
_drawImageOverlay(canvas, position, imageSize, embeddedImageStyle);
_drawImageOverlay(canvas, position, imageSize, null);
}
}

Expand Down Expand Up @@ -332,9 +398,8 @@ class QrPainter extends CustomPainter {
_PaintMetrics metrics,
) {
final totalGap = (_finderPatternLimit - 1) * metrics.gapSize;
final radius =
((_finderPatternLimit * metrics.pixelSize) + totalGap) -
metrics.pixelSize;
final radius = ((_finderPatternLimit * metrics.pixelSize) + totalGap) -
metrics.pixelSize;
final strokeAdjust = metrics.pixelSize / 2.0;
final edgePos =
(metrics.inset + metrics.innerContentSize) - (radius + strokeAdjust);
Expand All @@ -357,8 +422,8 @@ class QrPainter extends CustomPainter {
outerPaint.strokeWidth = metrics.pixelSize;
outerPaint.color = color != null ? color! : eyeStyle.color!;

final innerPaint = _paintCache
.firstPaint(QrCodeElement.finderPatternInner, position: position)!;
final innerPaint = _paintCache.firstPaint(QrCodeElement.finderPatternInner,
position: position)!;
innerPaint.strokeWidth = metrics.pixelSize;
innerPaint.color = emptyColor ?? const Color(0x00ffffff);

Expand All @@ -372,8 +437,7 @@ class QrPainter extends CustomPainter {
dotPaint!.color = eyeStyle.color!;
}

final outerRect =
Rect.fromLTWH(offset.dx, offset.dy, radius, radius);
final outerRect = Rect.fromLTWH(offset.dx, offset.dy, radius, radius);

final innerRadius = radius - (2 * metrics.pixelSize);
final innerRect = Rect.fromLTWH(
Expand All @@ -392,22 +456,43 @@ class QrPainter extends CustomPainter {
dotSize,
);

if (eyeStyle.eyeShape == QrEyeShape.square) {
canvas.drawRect(outerRect, outerPaint);
canvas.drawRect(innerRect, innerPaint);
canvas.drawRect(dotRect, dotPaint);
} else {
final roundedOuterStrokeRect =
RRect.fromRectAndRadius(outerRect, Radius.circular(radius));
canvas.drawRRect(roundedOuterStrokeRect, outerPaint);

final roundedInnerStrokeRect =
RRect.fromRectAndRadius(outerRect, Radius.circular(innerRadius));
canvas.drawRRect(roundedInnerStrokeRect, innerPaint);

final roundedDotStrokeRect =
RRect.fromRectAndRadius(dotRect, Radius.circular(dotSize));
canvas.drawRRect(roundedDotStrokeRect, dotPaint);
switch (eyeStyle.eyeShape) {
case QrEyeShape.square:
canvas.drawRect(outerRect, outerPaint);
canvas.drawRect(innerRect, innerPaint);
canvas.drawRect(dotRect, dotPaint);
break;
case QrEyeShape.squareRounded:
final roundedOuterStrokeRect = RRect.fromRectAndRadius(
outerRect, Radius.circular(eyeStyle.radius));
canvas.drawRRect(roundedOuterStrokeRect, outerPaint);
canvas.drawRect(innerRect, innerPaint);
final roundedDotStrokeRect = RRect.fromRectAndRadius(
dotRect, Radius.circular(eyeStyle.radius / 2));
canvas.drawRRect(roundedDotStrokeRect, dotPaint);
break;
case QrEyeShape.circle:
final roundedOuterStrokeRect =
RRect.fromRectAndRadius(outerRect, Radius.circular(radius));
canvas.drawRRect(roundedOuterStrokeRect, outerPaint);
final roundedInnerStrokeRect =
RRect.fromRectAndRadius(outerRect, Radius.circular(innerRadius));
canvas.drawRRect(roundedInnerStrokeRect, innerPaint);
final roundedDotStrokeRect =
RRect.fromRectAndRadius(dotRect, Radius.circular(dotSize));
canvas.drawRRect(roundedDotStrokeRect, dotPaint);
break;

default:
final roundedOuterStrokeRect =
RRect.fromRectAndRadius(outerRect, Radius.circular(radius));
canvas.drawRRect(roundedOuterStrokeRect, outerPaint);
final roundedInnerStrokeRect =
RRect.fromRectAndRadius(outerRect, Radius.circular(innerRadius));
canvas.drawRRect(roundedInnerStrokeRect, innerPaint);
final roundedDotStrokeRect =
RRect.fromRectAndRadius(dotRect, Radius.circular(dotSize));
canvas.drawRRect(roundedDotStrokeRect, dotPaint);
}
}

Expand Down Expand Up @@ -447,8 +532,7 @@ class QrPainter extends CustomPainter {
}
final srcSize =
Size(embeddedImage!.width.toDouble(), embeddedImage!.height.toDouble());
final src =
Alignment.center.inscribe(srcSize, Offset.zero & srcSize);
final src = Alignment.center.inscribe(srcSize, Offset.zero & srcSize);
final dst = Alignment.center.inscribe(size, position & size);
canvas.drawImageRect(embeddedImage!, src, dst, paint);
}
Expand Down
Loading