The number of circles has to be adapted to the size of the box:
final nbShapes = [
((bounds.width + gap) / (shapeSize + gap)).floor(),
((bounds.height + gap) / (shapeSize + gap)).floor(),
];
In my Solution, the CustomDecorationBox can be configured with:
double shapeSize
, the size of the shapes,
double shapeGap
, the gap between two shapes,
Paint shapePaint
, the paint used to draw the shapes (support for both strokes and fill paints)
ShapePainter paintShape
, a function painting the shapes
I provided 3 ShapePainter samples:
paintCircle()
paintRectangle()
ShapePainter createNGonPainter(int n)
, a factory for n-gon painters
Full source code:
import 'dart:math';
import 'package:flutter/material.dart';
typedef ShapePainter = void Function(Canvas, Rect, Paint);
Random random = Random();
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'CustomDecoration Demo',
home: Scaffold(
body: MyWidget(),
),
),
);
}
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) {
final size = Size(1, .6) * constraints.biggest.width;
print(size);
return GridView.count(
crossAxisCount: 3,
children: List.generate(
120,
(index) => Padding(
padding: EdgeInsets.all(size.width * .01),
child: Container(
decoration: CustomDecoration(
shapeSize: 5.0 + random.nextInt(10),
shapeGap: 2.0 + random.nextInt(3),
shapePaint: Paint()
..color = Color(0x99000000 + random.nextInt(0xffffff))
..strokeWidth = random.nextInt(3).toDouble()
..style = random.nextInt(3) == 2
? PaintingStyle.fill
: PaintingStyle.stroke,
paintShape: random.nextInt(4) == 0
? paintCircle
: createNGonPainter(3 + random.nextInt(5)),
),
child: Center(
child: Text(
index.toString(),
style: TextStyle(fontSize: 24.0),
),
),
),
),
),
);
},
),
);
}
}
class CustomDecoration extends Decoration {
final double shapeSize;
final double shapeGap;
final Paint shapePaint;
final ShapePainter paintShape;
CustomDecoration({
this.shapeSize,
this.shapeGap,
this.shapePaint,
this.paintShape,
}) : super();
@override
BoxPainter createBoxPainter([void Function() onChanged]) {
return BoxDecorationPainter(
shapeSize: shapeSize ?? 10,
shapeGap: shapeGap ?? 4,
shapePaint: shapePaint ?? Paint(),
paintShape: paintShape ?? paintCircle);
}
}
class BoxDecorationPainter extends BoxPainter {
final double shapeSize;
final double shapeGap;
final Paint shapePaint;
final ShapePainter paintShape;
BoxDecorationPainter({
this.shapeSize,
this.shapeGap,
this.shapePaint,
this.paintShape,
}) : super();
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
final Rect bounds = offset & configuration.size;
_drawDecoration(canvas, bounds, shapeSize, shapeGap);
}
void _drawDecoration(
Canvas canvas, Rect bounds, double shapeSize, double gap) {
final nbShapes = [
((bounds.width + gap) / (shapeSize + gap)).floor(),
((bounds.height + gap) / (shapeSize + gap)).floor(),
];
final correctedGaps = [
(bounds.width - nbShapes[0] * shapeSize) / (nbShapes[0] - 1),
(bounds.height - nbShapes[1] * shapeSize) / (nbShapes[1] - 1),
];
final steps = [
correctedGaps[0] + shapeSize,
correctedGaps[1] + shapeSize,
];
for (int i = 0; i < nbShapes[0]; i++) {
paintShape(
canvas,
Rect.fromLTWH(
bounds.left + steps[0] * i,
bounds.top,
shapeSize,
shapeSize,
),
shapePaint,
);
paintShape(
canvas,
Rect.fromLTWH(
bounds.left + steps[0] * i,
bounds.bottom - shapeSize,
shapeSize,
shapeSize,
),
shapePaint,
);
}
for (int i = 1; i < nbShapes[1] - 1; i++) {
paintShape(
canvas,
Rect.fromLTWH(
bounds.left,
bounds.top + steps[1] * i,
shapeSize,
shapeSize,
),
shapePaint,
);
paintShape(
canvas,
Rect.fromLTWH(
bounds.right - shapeSize,
bounds.top + steps[1] * i,
shapeSize,
shapeSize,
),
shapePaint,
);
}
}
}
void paintCircle(Canvas canvas, Rect bounds, Paint paint) {
canvas.drawCircle(
Offset(
bounds.left + bounds.width / 2,
bounds.top + bounds.height / 2,
),
bounds.shortestSide / 2,
paint,
);
}
void paintRectangle(Canvas canvas, Rect bounds, Paint paint) {
canvas.drawRect(bounds, paint);
}
ShapePainter createNGonPainter(int n) => (canvas, bounds, paint) {
Path path = Path();
path.moveTo(
bounds.left + (bounds.width + cos(2 * pi / n) * bounds.width) / 2,
bounds.top + (bounds.height + sin(2 * pi / n) * bounds.height) / 2,
);
for (var k = 2; k <= n; k++) {
path.lineTo(
bounds.left + (bounds.width + cos(2 * k * pi / n) * bounds.width) / 2,
bounds.top +
(bounds.height + sin(2 * k * pi / n) * bounds.height) / 2,
);
}
path.close();
canvas.drawPath(path, paint);
};