David PHAM-VAN

Implement BoxShadow for rect and circle BoxDecorations

@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 ## 1.9.0 3 ## 1.9.0
4 4
5 - Allow MultiPage to relayout individual pages with support for flex 5 - Allow MultiPage to relayout individual pages with support for flex
  6 +- Implement BoxShadow for rect and circle BoxDecorations
6 7
7 ## 1.8.1 8 ## 1.8.1
8 9
@@ -19,6 +19,7 @@ library widget; @@ -19,6 +19,7 @@ library widget;
19 import 'dart:collection'; 19 import 'dart:collection';
20 import 'dart:math' as math; 20 import 'dart:math' as math;
21 import 'dart:typed_data'; 21 import 'dart:typed_data';
  22 +import 'package:image/image.dart' as im;
22 23
23 import 'package:barcode/barcode.dart'; 24 import 'package:barcode/barcode.dart';
24 import 'package:meta/meta.dart'; 25 import 'package:meta/meta.dart';
@@ -375,20 +375,94 @@ class RadialGradient extends Gradient { @@ -375,20 +375,94 @@ class RadialGradient extends Gradient {
375 } 375 }
376 } 376 }
377 377
  378 +class BoxShadow {
  379 + const BoxShadow({
  380 + this.color = PdfColors.black,
  381 + this.offset = PdfPoint.zero,
  382 + this.blurRadius = 0.0,
  383 + this.spreadRadius = 0.0,
  384 + });
  385 +
  386 + final PdfColor color;
  387 + final PdfPoint offset;
  388 + final double blurRadius;
  389 + final double spreadRadius;
  390 +
  391 + im.Image _rect(double width, double height) {
  392 + final im.Image shadow = im.Image(
  393 + (width + spreadRadius * 2).round(),
  394 + (height + spreadRadius * 2).round(),
  395 + );
  396 +
  397 + im.fillRect(
  398 + shadow,
  399 + spreadRadius.round(),
  400 + spreadRadius.round(),
  401 + (spreadRadius + width).round(),
  402 + (spreadRadius + height).round(),
  403 + color.toInt(),
  404 + );
  405 +
  406 + im.gaussianBlur(shadow, blurRadius.round());
  407 +
  408 + return shadow;
  409 + }
  410 +
  411 + im.Image _rRect(double width, double height, double rv, double rh) {
  412 + final im.Image shadow = im.Image(
  413 + (width + spreadRadius * 2).round(),
  414 + (height + spreadRadius * 2).round(),
  415 + );
  416 +
  417 + im.fillRect(
  418 + shadow,
  419 + spreadRadius.round(),
  420 + spreadRadius.round(),
  421 + (spreadRadius + width).round(),
  422 + (spreadRadius + height).round(),
  423 + color.toInt(),
  424 + );
  425 +
  426 + im.gaussianBlur(shadow, blurRadius.round());
  427 +
  428 + return shadow;
  429 + }
  430 +
  431 + im.Image _ellipse(double width, double height) {
  432 + final im.Image shadow = im.Image(
  433 + (width + spreadRadius * 2).round(),
  434 + (height + spreadRadius * 2).round(),
  435 + );
  436 +
  437 + im.fillCircle(
  438 + shadow,
  439 + (spreadRadius + width / 2).round(),
  440 + (spreadRadius + height / 2).round(),
  441 + (width / 2).round(),
  442 + color.toInt(),
  443 + );
  444 +
  445 + im.gaussianBlur(shadow, blurRadius.round());
  446 +
  447 + return shadow;
  448 + }
  449 +}
  450 +
378 enum BoxShape { circle, rectangle } 451 enum BoxShape { circle, rectangle }
379 452
380 enum PaintPhase { all, background, foreground } 453 enum PaintPhase { all, background, foreground }
381 454
382 @immutable 455 @immutable
383 class BoxDecoration { 456 class BoxDecoration {
384 - const BoxDecoration(  
385 - {this.color, 457 + const BoxDecoration({
  458 + this.color,
386 this.border, 459 this.border,
387 this.borderRadius, 460 this.borderRadius,
  461 + this.boxShadow,
388 this.gradient, 462 this.gradient,
389 this.image, 463 this.image,
390 - this.shape = BoxShape.rectangle})  
391 - : assert(shape != null); 464 + this.shape = BoxShape.rectangle,
  465 + }) : assert(shape != null);
392 466
393 /// The color to fill in the background of the box. 467 /// The color to fill in the background of the box.
394 final PdfColor color; 468 final PdfColor color;
@@ -397,6 +471,7 @@ class BoxDecoration { @@ -397,6 +471,7 @@ class BoxDecoration {
397 final BoxShape shape; 471 final BoxShape shape;
398 final DecorationImage image; 472 final DecorationImage image;
399 final Gradient gradient; 473 final Gradient gradient;
  474 + final List<BoxShadow> boxShadow;
400 475
401 void paint( 476 void paint(
402 Context context, 477 Context context,
@@ -413,13 +488,50 @@ class BoxDecoration { @@ -413,13 +488,50 @@ class BoxDecoration {
413 switch (shape) { 488 switch (shape) {
414 case BoxShape.rectangle: 489 case BoxShape.rectangle:
415 if (borderRadius == null) { 490 if (borderRadius == null) {
  491 + if (boxShadow != null) {
  492 + for (final BoxShadow s in boxShadow) {
  493 + final im.Image i = s._rect(box.width, box.height);
  494 + final PdfImage m =
  495 + PdfImage.fromImage(context.document, image: i);
  496 + context.canvas.drawImage(
  497 + m,
  498 + box.x + s.offset.x - s.spreadRadius,
  499 + box.y - s.offset.y - s.spreadRadius,
  500 + );
  501 + }
  502 + }
416 context.canvas.drawRect(box.x, box.y, box.width, box.height); 503 context.canvas.drawRect(box.x, box.y, box.width, box.height);
417 } else { 504 } else {
  505 + if (boxShadow != null) {
  506 + for (final BoxShadow s in boxShadow) {
  507 + final im.Image i = s._rRect(
  508 + box.width, box.height, borderRadius, borderRadius);
  509 + final PdfImage m =
  510 + PdfImage.fromImage(context.document, image: i);
  511 + context.canvas.drawImage(
  512 + m,
  513 + box.x + s.offset.x - s.spreadRadius,
  514 + box.y - s.offset.y - s.spreadRadius,
  515 + );
  516 + }
  517 + }
418 context.canvas.drawRRect(box.x, box.y, box.width, box.height, 518 context.canvas.drawRRect(box.x, box.y, box.width, box.height,
419 borderRadius, borderRadius); 519 borderRadius, borderRadius);
420 } 520 }
421 break; 521 break;
422 case BoxShape.circle: 522 case BoxShape.circle:
  523 + if (boxShadow != null && box.width == box.height) {
  524 + for (final BoxShadow s in boxShadow) {
  525 + final im.Image i = s._ellipse(box.width, box.height);
  526 + final PdfImage m =
  527 + PdfImage.fromImage(context.document, image: i);
  528 + context.canvas.drawImage(
  529 + m,
  530 + box.x + s.offset.x - s.spreadRadius,
  531 + box.y - s.offset.y - s.spreadRadius,
  532 + );
  533 + }
  534 + }
423 context.canvas.drawEllipse(box.x + box.width / 2.0, 535 context.canvas.drawEllipse(box.x + box.width / 2.0,
424 box.y + box.height / 2.0, box.width / 2.0, box.height / 2.0); 536 box.y + box.height / 2.0, box.width / 2.0, box.height / 2.0);
425 break; 537 break;
@@ -204,6 +204,27 @@ void main() { @@ -204,6 +204,27 @@ void main() {
204 )); 204 ));
205 }); 205 });
206 206
  207 + test('Container Widgets BoxShadow', () {
  208 + pdf.addPage(Page(
  209 + build: (Context context) => Container(
  210 + margin: const EdgeInsets.all(30),
  211 + padding: const EdgeInsets.all(20),
  212 + decoration: const BoxDecoration(
  213 + boxShadow: <BoxShadow>[
  214 + BoxShadow(
  215 + blurRadius: 4,
  216 + spreadRadius: 10,
  217 + offset: PdfPoint(2, 2),
  218 + ),
  219 + ],
  220 + color: PdfColors.blue,
  221 + ),
  222 + width: 200,
  223 + height: 400,
  224 + ),
  225 + ));
  226 + });
  227 +
207 tearDownAll(() { 228 tearDownAll(() {
208 final File file = File('widgets-container.pdf'); 229 final File file = File('widgets-container.pdf');
209 file.writeAsBytesSync(pdf.save()); 230 file.writeAsBytesSync(pdf.save());