GardenMobile & interfaces

Painting a rounded Flutter panel with an arrow

A small CustomPainter experiment that joins a rounded rectangle and a triangular pointer.

A tooltip-like surface can be drawn as two shapes: a rounded rectangle and a triangle centered along an edge. The original painter filled both with the same color:

class _MenuBoxBackground extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Colors.white
      ..strokeWidth = 1.W
      ..style = PaintingStyle.fill;
    final double triangleH = 10.H;
    final double triangleW = 25.0.W;
    final double width = size.width;
    final double height = size.height;

    final Path trianglePath = Path()
      ..moveTo(width / 2 - triangleW / 2, height)
      ..lineTo(width / 2, triangleH + height)
      ..lineTo(width / 2 + triangleW / 2, height)
      ..lineTo(width / 2 - triangleW / 2, height);
    canvas.drawPath(trianglePath, paint);
    final BorderRadius borderRadius = BorderRadius.circular(15.W);
    final Rect rect = Rect.fromLTRB(0, 0, width, height);
    final RRect outer = borderRadius.toRRect(rect);
    canvas.drawRRect(outer, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

Its usage excerpt was:

CustomPaint(
  painter: _MenuBoxBackground(),
  child: Container(
    padding: EdgeInsets.only(
        left: 15.W, right: 15.W, bottom: 20.H, top: 20.H),
    child: ...,
  ),
)

The .W and .H values are application-specific scale extensions. The usage’s child: ... is an incomplete source excerpt, not a standalone widget.

The triangle extends beyond the rectangle’s reported height, so layout space and clipping need to be considered together. shouldRepaint returning false also assumes the painter’s relevant inputs do not change. No UI was rendered from this code for the revision.

Source: Gist revision.

← Back to the garden