flutterdartcanvaspathpaint

How to draw a horizontal diamond (rhombus) with rounded corners using Canvas in flutter?


I want to have border/corner radius for my diamond widget.

Something like this: BorderRadius.all(Radius.circular(cornerRadius))

I should probably use arcToPoint, arcTo or quadraticBezierTo functions of the Path, but can't find any good documentation/examples.

import 'package:flutter/material.dart';

/// Diamond Widget
class Diamond extends StatelessWidget {
  /// Constructor
  const Diamond({
    required this.width,
    required this.height,
    this.lineColor = Colors.black,
    this.cornerRadius = 8.0,
    super.key,
  });

  /// Width of the diamond
  final double width;

  /// Height of the diamond
  final double height;

  /// Outline's color of the diamond
  final Color lineColor;

  /// Corner radius of the diamond
  final double cornerRadius;

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      size: Size(width, height),
      painter: _DiamondPainter(
        lineColor: lineColor,
        cornerRadius: cornerRadius,
      ),
    );
  }
}

/// Custom painter for drawing a diamond
class _DiamondPainter extends CustomPainter {
  _DiamondPainter({required this.lineColor, required this.cornerRadius});

  final Color lineColor;
  final double cornerRadius;

  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..style = PaintingStyle.stroke
      ..color = lineColor
      ..strokeWidth = 2.0;

    final Path path = Path()
      ..moveTo(size.width / 2, 0) // Top center
      ..lineTo(size.width, size.height / 2) // Right center
      ..lineTo(size.width / 2, size.height) // Bottom center
      ..lineTo(0, size.height / 2) // Left center
      ..close();

    canvas.drawPath(path, paint); // Draw the outlined diamond
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

This is the incomplete example code that draws a diamond correctly, but without rounded corners. I want to complete this code and use cornerRadius in the implementation.

I tried using RRect and clips, but they didn't work either.


Solution

  • I found your thought about using Bezier curves interesting and decided to give it a try. I'm not sure if this is the most efficient implementation, but it works as you described. If I got it right, of course! :)

    I slightly modified paint from your painter. Here it is :

      void paint(Canvas canvas, Size size) {
        final Paint paint = Paint()
          ..style = PaintingStyle.stroke
          ..color = lineColor
          ..strokeWidth = 2.0;
    
        final Path path = Path()
          ..moveTo(size.width / 2 + cornerRadius, 0 + cornerRadius) // Top center
          ..lineTo(size.width - cornerRadius,
              size.height / 2 - cornerRadius) // Right center
          ..quadraticBezierTo(size.width, size.height / 2,
              size.width - cornerRadius, size.height / 2 + cornerRadius)
          ..lineTo(size.width / 2 + cornerRadius,
              size.height - cornerRadius) // Bottom center
          ..quadraticBezierTo(size.width / 2, size.height,
              size.width / 2 - cornerRadius, size.height - cornerRadius)
          ..lineTo(0 + cornerRadius, size.height / 2 + cornerRadius) // Left center
          ..quadraticBezierTo(
              0, size.height / 2, 0 + cornerRadius, size.height / 2 - cornerRadius)
          ..lineTo(size.width / 2 - cornerRadius, 0 + cornerRadius)
          ..quadraticBezierTo(
              size.width / 2, 0, size.width / 2 + cornerRadius, 0 + cornerRadius);
        canvas.drawPath(path, paint); // Draw the outlined diamond
      }
    

    The key concept here is to provide offsets for each line, which would create "empty corners." Then, we fill these gaps with Bezier curves to achieve the rounded corners.

    plus, I believe you should somehow validate cornerRadius parameter, since it can't be greater than half of diamond's side size. And also, my implementation will only work for square diamond :( But I still think it's good starting point for your issue