nifesi
nifesi

Reputation: 170

How to add border radius to CustomPaint Widget Flutter

I'm trying to add border radius to my custom shaped widget using Custom Paint, but I don't know how to add rounded edges to the custom shape.

I achieved the shape, but not the rounded edges.This is what I am trying to achieve.This is what I have done so far.

Below is the code for the custom paint. How can I add border radius to the edges.

``


class RPSCustomPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Paint paint0 = Paint()
      ..color = const Color.fromARGB(255, 33, 150, 243)
      ..style = PaintingStyle.stroke
      ..strokeWidth = 1.4900000095367432;

    Path path0 = Path();
    path0.moveTo(3.03, 197.85);
    path0.quadraticBezierTo(0.87, 47.28, 1.9, 1.36);
    path0.lineTo(207.0, 2.0);
    path0.lineTo(170.24, 197.9);
    path0.quadraticBezierTo(16.26, 197.13, 3.03, 197.85);
    canvas.drawPath(path0, paint0);
  }

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

``

Upvotes: 2

Views: 1420

Answers (2)

Alexander Kremenchuk
Alexander Kremenchuk

Reputation: 406

I followed this answer and after some tweaks this is what we have:

Radius 10: radius10 Radius 30: radius30 Radius 90: radius 90

import 'dart:math' as math;
import 'dart:ui';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Application',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Home'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(40),
          child: CustomPaint(
            painter: RPSCustomPainter(borderRadius: 90),
            child: Container(),
          ),
        ));
  }
}

class RPSCustomPainter extends CustomPainter {
  RPSCustomPainter({this.borderRadius = 10});

  final double borderRadius;

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint0 = Paint()
      ..color = const Color.fromARGB(255, 33, 150, 243)
      ..style = PaintingStyle.stroke
      ..strokeWidth = 1.4900000095367432;

    final points = [
      const Offset(3.03, 197.9),
      const Offset(1.9, 1.36),
      const Offset(207.0, 2.0),
      const Offset(170.24, 197.9),
    ];

    final (lines, arcs) = getLinesAndArcs(points, borderRadius: borderRadius);

    for (var i = 0; i < lines.length; i++) {
      final line = lines[i];
      final arc = arcs[i];

      TextSpan span = TextSpan(
          style: TextStyle(color: Colors.blue[800]),
          text:
              '${i.toString()}\nsweepAngle: ${arc.sweepAngle.toDegrees().toStringAsFixed(2)}\nstartAngle: ${arc.startAngle.toDegrees().toStringAsFixed(2)}\nendAngle: ${arc.endAngle.toDegrees().toStringAsFixed(2)}');
      TextPainter tp = TextPainter(
          text: span,
          textAlign: TextAlign.left,
          textDirection: TextDirection.ltr);
      tp.layout();
      tp.paint(canvas, points[i]);

      canvas.drawPoints(
          PointMode.points,
          [...points, line.end, line.start, arc.center],
          Paint()
            ..strokeWidth = 5
            ..strokeCap = StrokeCap.round
            ..color = Colors.green);

      canvas.drawLine(line.start, line.end, paint0);
      canvas.drawArc(Rect.fromCircle(center: arc.center, radius: arc.radius),
          arc.startAngle, arc.sweepAngle, false, paint0);
    }
  }

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

  double getLength(double dx, double dy) {
    return math.sqrt(dx * dx + dy * dy);
  }

  List<List<Offset>> getVerticeTriplets(List<Offset> points) {
    final triplets = <List<Offset>>[];

    for (var i = 0; i < points.length; i += 1) {
      final triplet = <Offset>[];
      for (var j = 0; j < 3; j++) {
        triplet.add(points[(i + j) % points.length]);
      }
      triplets.add(triplet);
    }

    return triplets;
  }

  (List<Line>, List<Arc>) getLinesAndArcs(
    List<Offset> points, {
    double borderRadius = 10,
  }) {
    var radius = borderRadius;

    final starts = <Offset>[];
    final ends = <Offset>[];

    final arcs = <Arc>[];

    final triplets = getVerticeTriplets(points);

    for (var i = 0; i < triplets.length; i++) {
      final [p1, angularPoint, p2] = triplets[i];

      //Vector 1
      final dx1 = angularPoint.dx - p1.dx;
      final dy1 = angularPoint.dy - p1.dy;

      //Vector 2
      final dx2 = angularPoint.dx - p2.dx;
      final dy2 = angularPoint.dy - p2.dy;

      //Angle between vector 1 and vector 2 divided by 2
      final angle = (math.atan2(dy1, dx1) - math.atan2(dy2, dx2)) / 2;

      // The length of segment between angular point and the
      // points of intersection with the circle of a given radius
      final tan = math.tan(angle).abs();
      var segment = radius / tan;

      //Check the segment
      final length1 = getLength(dx1, dy1);
      final length2 = getLength(dx2, dy2);

      final length = math.min(length1, length2);

      if (segment > length) {
        segment = length;
        radius = (length * tan);
      }

      // Points of intersection are calculated by the proportion between
      // the coordinates of the vector, length of vector and the length of the segment.
      var p1Cross =
          getProportionPoint(angularPoint, segment, length1, dx1, dy1);
      ends.add(p1Cross);
      var p2Cross =
          getProportionPoint(angularPoint, segment, length2, dx2, dy2);
      starts.add(p2Cross);

      // Calculation of the coordinates of the circle
      // center by the addition of angular vectors.
      final dx = angularPoint.dx * 2 - p1Cross.dx - p2Cross.dx;
      final dy = angularPoint.dy * 2 - p1Cross.dy - p2Cross.dy;

      final L = getLength(dx, dy);
      final d = getLength(segment, radius);

      final circlePoint = getProportionPoint(angularPoint, d, L, dx, dy);

      //StartAngle and EndAngle of arc
      var startAngle =
          math.atan2(p1Cross.dy - circlePoint.dy, p1Cross.dx - circlePoint.dx);
      final endAngle =
          math.atan2(p2Cross.dy - circlePoint.dy, p2Cross.dx - circlePoint.dx);

      //Sweep angle
      var sweepAngle = endAngle - startAngle;

      //Some additional checks
      if (sweepAngle < 0) {
        startAngle = endAngle;
        sweepAngle = -sweepAngle;
      }

      if (sweepAngle > math.pi) sweepAngle = math.pi - sweepAngle;

      arcs.add(
        Arc(
          startAngle: startAngle,
          sweepAngle: sweepAngle,
          endAngle: endAngle,
          center: Offset(circlePoint.dx, circlePoint.dy),
          radius: radius,
        ),
      );
    }

    final lines = <Line>[];

    final shiftedStarts = [
      starts.last,
      ...starts.sublist(0, starts.length - 1)
    ];

    for (var i = 0; i < ends.length; i++) {
      lines.add(Line(start: shiftedStarts[i], end: ends[i]));
    }

    return (lines, [arcs.last, ...arcs.sublist(0, arcs.length - 1)]);
  }

  Offset getProportionPoint(
      Offset point, double segment, double length, double dx, double dy) {
    double factor = segment / length;

    return Offset((point.dx - dx * factor), (point.dy - dy * factor));
  }
}

class Arc {
  const Arc({
    required this.startAngle,
    required this.sweepAngle,
    required this.center,
    required this.radius,
    required this.endAngle,
  });

  final double startAngle;
  final double endAngle;
  final double sweepAngle;
  final Offset center;
  final double radius;
}

class Line {
  const Line({
    required this.start,
    required this.end,
  });

  final Offset start;
  final Offset end;
}

extension on double {
  double toDegrees() => this * 180 / math.pi;
}

Upvotes: 4

Gwhyyy
Gwhyyy

Reputation: 9206

you can use drawRRect() to draw the border radius for corners of your shape.

canvas.drawRRect(RRect.fromRectAndRadius(Rect.fromLTWH(size.width / 2 - gap - smallMarkWidth - 15,gap * 8,gap + 70,gap * 5,),Radius.circular(15.0)),backgroundPaint);

Upvotes: 1

Related Questions