Flutter Canvas Draw Circle with CustomPainter
Flutter provides the Canvas.drawCircle() method for drawing a circle during custom painting. You normally call it inside the paint() method of a CustomPainter.
A circle is defined by three main values: its center position, its radius, and the Paint object that controls how the circle is rendered. The paint can be configured for a filled circle, an outlined circle, a specific color, and other drawing properties.
Flutter Canvas.drawCircle() syntax and parameters
Following is the syntax of drawCircle() function in Flutter.
void drawCircle(Offset c, double radius, Paint paint)
The three arguments determine where and how the circle is drawn:
cis anOffsetrepresenting the center of the circle on the canvas.radiusis the distance from the center to the edge of the circle.paintis aPaintobject that controls properties such as color, fill or stroke style, and stroke width.
A quick example of drawCircle() is given below.
void drawCircle(Offset(200, 200), 50, Paint())
Of course you can change the offset and radius of circle and paint properties.
Canvas coordinates start at the top-left corner of the drawing area. The x-coordinate increases toward the right, and the y-coordinate increases downward. Therefore, Offset(200, 200) places the center 200 logical pixels from the left and 200 logical pixels from the top of the canvas.
Draw a filled circle on Flutter Canvas
To draw a circle in our Flutter Application, we shall follow the below steps.
Step 1: Create a class that extends CustomPainter. This is our widget that has canvas and allows user to paint on to the canvas.
class OpenPainter extends CustomPainter {
...
}
Step 2: Override paint() and shouldRepaint() methods of the CustomPainter class. In the paint() method, we have access to canvas and size of the canvas.
Step 3: Draw a circle on the canvas using canvas.drawCircle() method. You write this statement in paint() method of the class we created.
@override
void paint(Canvas canvas, Size size) {
//...
canvas.drawCircle(Offset(200, 200), 100, Paint());
}
Complete example program is
main.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter TutorialKart',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter - www.tutorialkart.com'),
),
body: ListView(children: <Widget>[
Container(
width: 400,
height: 400,
child: CustomPaint(
painter: OpenPainter(),
),
),
]),
);
}
}
class OpenPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
var paint1 = Paint()
..color = Color(0xffaa44aa)
..style = PaintingStyle.fill;
canvas.drawCircle(Offset(200, 200), 100, paint1);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
Run the application in your Android Device or Android Emulator.
)
Center a Flutter circle using the CustomPainter canvas size
Hard-coded coordinates such as Offset(200, 200) work when the drawing area has a known size. For a reusable painter, calculate the center from the Size supplied to paint(). This keeps the circle centered when the CustomPaint widget receives a different width or height.
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = size.shortestSide / 4;
final paint = Paint()
..color = Colors.blue
..style = PaintingStyle.fill;
canvas.drawCircle(center, radius, paint);
}
size.shortestSide is useful when the drawing area is not square because it bases the radius on the smaller dimension. This helps prevent the circle from extending outside the available canvas.
Draw an outlined circle with Paint.style in Flutter
PaintingStyle.fill paints the entire interior of the circle. To draw only the circumference, use PaintingStyle.stroke and set a stroke width.
final paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = 4;
canvas.drawCircle(
Offset(size.width / 2, size.height / 2),
80,
paint,
);
If you need both a fill and a border, draw the circle twice: first with a fill paint and then at the same center and radius with a stroke paint.
Update circle color and radius on Flutter Canvas
You can update any property of the circle you would like to change in runtime.
In the following example application, we are changing the color and radius of the circle.
To change a property of the circle you are painting, follow these steps.
Step 1: Declare all the properties you would like to change, as variables. Assign the default or initial value to these variables.
Color circleColor1 = Color(0xff000000);
double circleRadius1 = 100;
Step 2: Use this variables while you are painting the circle in CustomPainter class.
var paint1 = Paint()
..color = circleColor1
..style = PaintingStyle.fill;
canvas.drawCircle(Offset(150, 150), circleRadius1, paint1);
Step 3: When a change has to be made, like when user presses a button, assign the new value for property using the variable(declared in step 1).
circleColor1 = Color(0xff885599);
circleRadius1 = 80;
The complete program is
main.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
Color circleColor1 = Color(0xff000000);
double circleRadius1 = 100;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter TutorialKart',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter - www.tutorialkart.com'),
),
body: ListView(children: <Widget>[
Text(
'My Canvas',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20),
),
Container(
width: 400,
height: 400,
child: CustomPaint(
painter: OpenPainter(),
),
),
RaisedButton(
child: Text('Repaint Canvas'),
onPressed: (){
setState(() {
circleColor1 = Color(0xff885599);
circleRadius1 = 80;
});
},
),
]),
);
}
}
class OpenPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
var paint1 = Paint()
..color = circleColor1
..style = PaintingStyle.fill;
canvas.drawCircle(Offset(150, 150), circleRadius1, paint1);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => true;
}
Run the application in your Android Smartphone or Emulator, you will get the left screen as shown in the following screenshot. When you press the Repaint Canvas button, the color the circle and the radius change to a new value.
)
Repaint a Flutter circle when its properties change
The earlier example demonstrates the basic repainting idea. For reusable Flutter code, it is usually clearer to pass the current color and radius into the painter instead of relying on global variables. The painter can then decide whether a new frame needs to be painted by comparing its current values with the previous painter.
class CirclePainter extends CustomPainter {
const CirclePainter({
required this.color,
required this.radius,
});
final Color color;
final double radius;
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final paint = Paint()
..color = color
..style = PaintingStyle.fill;
canvas.drawCircle(center, radius, paint);
}
@override
bool shouldRepaint(CirclePainter oldDelegate) {
return oldDelegate.color != color ||
oldDelegate.radius != radius;
}
}
You can rebuild CustomPaint with a new CirclePainter after changing state. shouldRepaint() then returns true only when a property that affects the drawing has changed.
Complete responsive Flutter Canvas circle example
The following example uses the available canvas size rather than fixed center coordinates. It also uses an explicit CustomPaint size so that the painter receives a predictable drawing area.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: CustomPaint(
size: Size(300, 300),
painter: CirclePainter(),
),
),
),
);
}
}
class CirclePainter extends CustomPainter {
const CirclePainter();
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = size.shortestSide * 0.3;
final paint = Paint()
..color = Colors.purple
..style = PaintingStyle.fill;
canvas.drawCircle(center, radius, paint);
}
@override
bool shouldRepaint(CirclePainter oldDelegate) => false;
}
Because this painter has no changing fields, shouldRepaint() returns false. If the painter receives values such as a new radius, color, or center, compare those values with the old delegate as shown in the previous section.
Why a circle may not appear correctly on Flutter Canvas
- The center is outside the canvas: check the x and y values passed to
Offset. - The radius is too large: part of the circle can be clipped when it extends beyond the bounds of the painting area.
- The CustomPaint widget has no useful size: give it constraints through its parent or provide a
sizewhen appropriate. - The paint is not configured as expected: check
color,style, andstrokeWidthwhen drawing an outline. - The painter is not repainting after state changes: make sure the widget rebuilds with updated painter values and that
shouldRepaint()correctly reports whether the drawing changed.
Canvas.drawCircle() compared with a circular Container
Use Canvas.drawCircle() when the circle is part of custom graphics, diagrams, charts, gauges, animations, or a drawing that combines several shapes on the same canvas. It gives direct control over coordinates and painting.
If you only need a circular widget in a normal Flutter layout, custom painting may be unnecessary. A Container with BoxDecoration(shape: BoxShape.circle) or a widget such as CircleAvatar can be simpler because it participates directly in Flutter’s widget layout system.
Flutter Canvas drawCircle() key points
In this Flutter Tutorial, we learned how to draw a circle on a Canvas using CustomPainter class. Use Canvas.drawCircle() with an Offset for the center, a radius, and a configured Paint. For layouts that can change size, calculate the center from the Size passed to paint(). Use PaintingStyle.fill for a solid circle and PaintingStyle.stroke for an outline.
TutorialKart.com