@@ -212,7 +212,9 @@ abstract class BoxBorder extends ShapeBorder {
212
212
///
213
213
/// See also:
214
214
///
215
- /// * [paintBorder] , which is used if the border is not uniform.
215
+ /// * [paintBorder] , which is used if the border has non-uniform colors or styles and no borderRadius.
216
+ /// * [Border.paint] , similar to this method, includes additional comments
217
+ /// and provides more details on each parameter than described here.
216
218
@override
217
219
void paint (
218
220
Canvas canvas,
@@ -240,6 +242,62 @@ abstract class BoxBorder extends ShapeBorder {
240
242
}
241
243
}
242
244
245
+ static void _paintNonUniformBorder (
246
+ Canvas canvas,
247
+ Rect rect, {
248
+ required BorderRadius ? borderRadius,
249
+ required BoxShape shape,
250
+ required TextDirection ? textDirection,
251
+ required BorderSide left,
252
+ required BorderSide top,
253
+ required BorderSide right,
254
+ required BorderSide bottom,
255
+ }) {
256
+ final RRect borderRect;
257
+ switch (shape) {
258
+ case BoxShape .rectangle:
259
+ borderRect = (borderRadius ?? BorderRadius .zero)
260
+ .resolve (textDirection)
261
+ .toRRect (rect);
262
+ case BoxShape .circle:
263
+ assert (borderRadius == null , 'A borderRadius cannot be given when shape is a BoxShape.circle.' );
264
+ borderRect = RRect .fromRectAndRadius (
265
+ Rect .fromCircle (center: rect.center, radius: rect.shortestSide / 2.0 ),
266
+ Radius .circular (rect.width),
267
+ );
268
+ }
269
+ final Paint paint = Paint ()..color = top.color;
270
+ final RRect inner = _deflateRRect (borderRect, EdgeInsets .fromLTRB (left.strokeInset, top.strokeInset, right.strokeInset, bottom.strokeInset));
271
+ final RRect outer = _inflateRRect (borderRect, EdgeInsets .fromLTRB (left.strokeOutset, top.strokeOutset, right.strokeOutset, bottom.strokeOutset));
272
+ canvas.drawDRRect (outer, inner, paint);
273
+ }
274
+
275
+ static RRect _inflateRRect (RRect rect, EdgeInsets insets) {
276
+ return RRect .fromLTRBAndCorners (
277
+ rect.left - insets.left,
278
+ rect.top - insets.top,
279
+ rect.right + insets.right,
280
+ rect.bottom + insets.bottom,
281
+ topLeft: (rect.tlRadius + Radius .elliptical (insets.left, insets.top)).clamp (minimum: Radius .zero), // ignore_clamp_double_lint
282
+ topRight: (rect.trRadius + Radius .elliptical (insets.right, insets.top)).clamp (minimum: Radius .zero), // ignore_clamp_double_lint
283
+ bottomRight: (rect.brRadius + Radius .elliptical (insets.right, insets.bottom)).clamp (minimum: Radius .zero), // ignore_clamp_double_lint
284
+ bottomLeft: (rect.blRadius + Radius .elliptical (insets.left, insets.bottom)).clamp (minimum: Radius .zero), // ignore_clamp_double_lint
285
+ );
286
+ }
287
+
288
+ static RRect _deflateRRect (RRect rect, EdgeInsets insets) {
289
+ return RRect .fromLTRBAndCorners (
290
+ rect.left + insets.left,
291
+ rect.top + insets.top,
292
+ rect.right - insets.right,
293
+ rect.bottom - insets.bottom,
294
+ topLeft: (rect.tlRadius - Radius .elliptical (insets.left, insets.top)).clamp (minimum: Radius .zero), // ignore_clamp_double_lint
295
+ topRight: (rect.trRadius - Radius .elliptical (insets.right, insets.top)).clamp (minimum: Radius .zero), // ignore_clamp_double_lint
296
+ bottomRight: (rect.brRadius - Radius .elliptical (insets.right, insets.bottom)).clamp (minimum: Radius .zero), // ignore_clamp_double_lint
297
+ bottomLeft: (rect.blRadius - Radius .elliptical (insets.left, insets.bottom)).clamp (minimum: Radius .zero), // ignore_clamp_double_lint
298
+ );
299
+ }
300
+
243
301
static void _paintUniformBorderWithCircle (Canvas canvas, Rect rect, BorderSide side) {
244
302
assert (side.style != BorderStyle .none);
245
303
final double radius = (rect.shortestSide + side.strokeOffset) / 2 ;
@@ -313,6 +371,10 @@ abstract class BoxBorder extends ShapeBorder {
313
371
/// * [BorderSide] , which is used to describe each side of the box.
314
372
/// * [Theme] , from the material layer, which can be queried to obtain appropriate colors
315
373
/// to use for borders in a [MaterialApp], as shown in the "divider" sample above.
374
+ /// * [paint] , which explains the behavior of [BoxDecoration] parameters.
375
+ /// * <https://pub.dev/packages/non_uniform_border>, a package that implements
376
+ /// a Non-Uniform Border on ShapeBorder, which is used by Material Design
377
+ /// buttons and other widgets, under the "shape" field.
316
378
class Border extends BoxBorder {
317
379
/// Creates a border.
318
380
///
@@ -407,24 +469,24 @@ class Border extends BoxBorder {
407
469
408
470
bool get _colorIsUniform {
409
471
final Color topColor = top.color;
410
- return right .color == topColor && bottom.color == topColor && left .color == topColor;
472
+ return left .color == topColor && bottom.color == topColor && right .color == topColor;
411
473
}
412
474
413
475
bool get _widthIsUniform {
414
476
final double topWidth = top.width;
415
- return right .width == topWidth && bottom.width == topWidth && left .width == topWidth;
477
+ return left .width == topWidth && bottom.width == topWidth && right .width == topWidth;
416
478
}
417
479
418
480
bool get _styleIsUniform {
419
481
final BorderStyle topStyle = top.style;
420
- return right .style == topStyle && bottom.style == topStyle && left .style == topStyle;
482
+ return left .style == topStyle && bottom.style == topStyle && right .style == topStyle;
421
483
}
422
484
423
485
bool get _strokeAlignIsUniform {
424
486
final double topStrokeAlign = top.strokeAlign;
425
- return right .strokeAlign == topStrokeAlign
487
+ return left .strokeAlign == topStrokeAlign
426
488
&& bottom.strokeAlign == topStrokeAlign
427
- && left .strokeAlign == topStrokeAlign;
489
+ && right .strokeAlign == topStrokeAlign;
428
490
}
429
491
430
492
@override
@@ -491,14 +553,16 @@ class Border extends BoxBorder {
491
553
492
554
/// Paints the border within the given [Rect] on the given [Canvas] .
493
555
///
494
- /// Uniform borders are more efficient to paint than more complex borders.
556
+ /// Uniform borders and non-uniform borders with similar colors and styles
557
+ /// are more efficient to paint than more complex borders.
495
558
///
496
559
/// You can provide a [BoxShape] to draw the border on. If the `shape` in
497
- /// [BoxShape.circle] , there is the requirement that the border [isUniform] .
560
+ /// [BoxShape.circle] , there is the requirement that the border has uniform
561
+ /// color and style.
498
562
///
499
563
/// If you specify a rectangular box shape ([BoxShape.rectangle] ), then you
500
564
/// may specify a [BorderRadius] . If a `borderRadius` is specified, there is
501
- /// the requirement that the border [isUniform] .
565
+ /// the requirement that the border has uniform color and style .
502
566
///
503
567
/// The [getInnerPath] and [getOuterPath] methods do not know about the
504
568
/// `shape` and `borderRadius` arguments.
@@ -507,7 +571,10 @@ class Border extends BoxBorder {
507
571
///
508
572
/// See also:
509
573
///
510
- /// * [paintBorder] , which is used if the border is not uniform.
574
+ /// * [paintBorder] , which is used if the border has non-uniform colors or styles and no borderRadius.
575
+ /// * <https://pub.dev/packages/non_uniform_border>, a package that implements
576
+ /// a Non-Uniform Border on ShapeBorder, which is used by Material Design
577
+ /// buttons and other widgets, under the "shape" field.
511
578
@override
512
579
void paint (
513
580
Canvas canvas,
@@ -523,7 +590,7 @@ class Border extends BoxBorder {
523
590
case BorderStyle .solid:
524
591
switch (shape) {
525
592
case BoxShape .circle:
526
- assert (borderRadius == null , 'A borderRadius can only be given for rectangular boxes .' );
593
+ assert (borderRadius == null , 'A borderRadius cannot be given when shape is a BoxShape.circle .' );
527
594
BoxBorder ._paintUniformBorderWithCircle (canvas, rect, top);
528
595
case BoxShape .rectangle:
529
596
if (borderRadius != null && borderRadius != BorderRadius .zero) {
@@ -536,36 +603,50 @@ class Border extends BoxBorder {
536
603
}
537
604
}
538
605
606
+ // Allow painting non-uniform borders if the color and style are uniform.
607
+ if (_colorIsUniform && _styleIsUniform) {
608
+ switch (top.style) {
609
+ case BorderStyle .none:
610
+ return ;
611
+ case BorderStyle .solid:
612
+ BoxBorder ._paintNonUniformBorder (canvas, rect,
613
+ shape: shape,
614
+ borderRadius: borderRadius,
615
+ textDirection: textDirection,
616
+ left: left,
617
+ top: top,
618
+ right: right,
619
+ bottom: bottom);
620
+ return ;
621
+ }
622
+ }
623
+
539
624
assert (() {
540
625
if (borderRadius != null ) {
541
626
throw FlutterError .fromParts (< DiagnosticsNode > [
542
- ErrorSummary ('A borderRadius can only be given for a uniform Border .' ),
627
+ ErrorSummary ('A borderRadius can only be given on borders with uniform colors and styles .' ),
543
628
ErrorDescription ('The following is not uniform:' ),
544
629
if (! _colorIsUniform) ErrorDescription ('BorderSide.color' ),
545
- if (! _widthIsUniform) ErrorDescription ('BorderSide.width' ),
546
630
if (! _styleIsUniform) ErrorDescription ('BorderSide.style' ),
547
- if (! _strokeAlignIsUniform) ErrorDescription ('BorderSide.strokeAlign' ),
548
631
]);
549
632
}
550
633
return true ;
551
634
}());
552
635
assert (() {
553
636
if (shape != BoxShape .rectangle) {
554
637
throw FlutterError .fromParts (< DiagnosticsNode > [
555
- ErrorSummary ('A Border can only be drawn as a circle if it is uniform.' ),
638
+ ErrorSummary ('A Border can only be drawn as a circle on borders with uniform colors and styles .' ),
556
639
ErrorDescription ('The following is not uniform:' ),
557
640
if (! _colorIsUniform) ErrorDescription ('BorderSide.color' ),
558
- if (! _widthIsUniform) ErrorDescription ('BorderSide.width' ),
559
641
if (! _styleIsUniform) ErrorDescription ('BorderSide.style' ),
560
- if (! _strokeAlignIsUniform) ErrorDescription ('BorderSide.strokeAlign' ),
561
642
]);
562
643
}
563
644
return true ;
564
645
}());
565
646
assert (() {
566
647
if (! _strokeAlignIsUniform || top.strokeAlign != BorderSide .strokeAlignInside) {
567
648
throw FlutterError .fromParts (< DiagnosticsNode > [
568
- ErrorSummary ('A Border can only draw strokeAlign different than BorderSide.strokeAlignInside on uniform borders .' ),
649
+ ErrorSummary ('A Border can only draw strokeAlign different than BorderSide.strokeAlignInside on borders with uniform colors and styles .' ),
569
650
]);
570
651
}
571
652
return true ;
@@ -626,6 +707,9 @@ class Border extends BoxBorder {
626
707
/// * [BorderSide] , which is used to describe each side of the box.
627
708
/// * [Theme] , from the material layer, which can be queried to obtain appropriate colors
628
709
/// to use for borders in a [MaterialApp], as shown in the "divider" sample above.
710
+ /// * <https://pub.dev/packages/non_uniform_border>, a package that implements
711
+ /// a Non-Uniform Border on ShapeBorder, which is used by Material Design
712
+ /// buttons and other widgets, under the "shape" field.
629
713
class BorderDirectional extends BoxBorder {
630
714
/// Creates a border.
631
715
///
@@ -698,33 +782,21 @@ class BorderDirectional extends BoxBorder {
698
782
}
699
783
700
784
@override
701
- bool get isUniform {
785
+ bool get isUniform => _colorIsUniform && _widthIsUniform && _styleIsUniform && _strokeAlignIsUniform;
786
+
787
+ bool get _colorIsUniform {
702
788
final Color topColor = top.color;
703
- if (start.color != topColor ||
704
- end.color != topColor ||
705
- bottom.color != topColor) {
706
- return false ;
707
- }
789
+ return start.color == topColor && bottom.color == topColor && end.color == topColor;
790
+ }
708
791
792
+ bool get _widthIsUniform {
709
793
final double topWidth = top.width;
710
- if (start.width != topWidth ||
711
- end.width != topWidth ||
712
- bottom.width != topWidth) {
713
- return false ;
714
- }
794
+ return start.width == topWidth && bottom.width == topWidth && end.width == topWidth;
795
+ }
715
796
797
+ bool get _styleIsUniform {
716
798
final BorderStyle topStyle = top.style;
717
- if (start.style != topStyle ||
718
- end.style != topStyle ||
719
- bottom.style != topStyle) {
720
- return false ;
721
- }
722
-
723
- if (_strokeAlignIsUniform == false ) {
724
- return false ;
725
- }
726
-
727
- return true ;
799
+ return start.style == topStyle && bottom.style == topStyle && end.style == topStyle;
728
800
}
729
801
730
802
bool get _strokeAlignIsUniform {
@@ -850,7 +922,7 @@ class BorderDirectional extends BoxBorder {
850
922
///
851
923
/// See also:
852
924
///
853
- /// * [paintBorder] , which is used if the border is not uniform.
925
+ /// * [paintBorder] , which is used if the border has non- uniform colors or styles and no borderRadius .
854
926
@override
855
927
void paint (
856
928
Canvas canvas,
@@ -866,7 +938,7 @@ class BorderDirectional extends BoxBorder {
866
938
case BorderStyle .solid:
867
939
switch (shape) {
868
940
case BoxShape .circle:
869
- assert (borderRadius == null , 'A borderRadius can only be given for rectangular boxes .' );
941
+ assert (borderRadius == null , 'A borderRadius cannot be given when shape is a BoxShape.circle .' );
870
942
BoxBorder ._paintUniformBorderWithCircle (canvas, rect, top);
871
943
case BoxShape .rectangle:
872
944
if (borderRadius != null && borderRadius != BorderRadius .zero) {
@@ -879,10 +951,6 @@ class BorderDirectional extends BoxBorder {
879
951
}
880
952
}
881
953
882
- assert (borderRadius == null , 'A borderRadius can only be given for uniform borders.' );
883
- assert (shape == BoxShape .rectangle, 'A border can only be drawn as a circle if it is uniform.' );
884
- assert (_strokeAlignIsUniform && top.strokeAlign == BorderSide .strokeAlignInside, 'A Border can only draw strokeAlign different than strokeAlignInside on uniform borders.' );
885
-
886
954
final BorderSide left, right;
887
955
assert (textDirection != null , 'Non-uniform BorderDirectional objects require a TextDirection when painting.' );
888
956
switch (textDirection! ) {
@@ -893,6 +961,29 @@ class BorderDirectional extends BoxBorder {
893
961
left = start;
894
962
right = end;
895
963
}
964
+
965
+ // Allow painting non-uniform borders if the color and style are uniform.
966
+ if (_colorIsUniform && _styleIsUniform) {
967
+ switch (top.style) {
968
+ case BorderStyle .none:
969
+ return ;
970
+ case BorderStyle .solid:
971
+ BoxBorder ._paintNonUniformBorder (canvas, rect,
972
+ shape: shape,
973
+ borderRadius: borderRadius,
974
+ textDirection: textDirection,
975
+ left: left,
976
+ top: top,
977
+ right: right,
978
+ bottom: bottom);
979
+ return ;
980
+ }
981
+ }
982
+
983
+ assert (borderRadius == null , 'A borderRadius can only be given for borders with uniform colors and styles.' );
984
+ assert (shape == BoxShape .rectangle, 'A Border can only be drawn as a circle on borders with uniform colors and styles.' );
985
+ assert (_strokeAlignIsUniform && top.strokeAlign == BorderSide .strokeAlignInside, 'A Border can only draw strokeAlign different than strokeAlignInside on borders with uniform colors and styles.' );
986
+
896
987
paintBorder (canvas, rect, top: top, left: left, bottom: bottom, right: right);
897
988
}
898
989
0 commit comments