Creating custom graphics in Flutter can be done with the CustomPainter
class, which allows you to draw shapes, text, and images directly onto the screen. The Canvas
object provides a wide array of drawing functions to help you create your custom visuals.
Custom Painting in Flutter π¨
1. Introduction to CustomPainter ποΈ
CustomPainter
allows you to draw directly to the screen. Itβs used in conjunction with a CustomPaint
widget.
Copy class MyCustomPainter extends CustomPainter {
@override
void paint ( Canvas canvas, Size size) {
// Your painting code here...
}
@override
bool shouldRepaint ( CustomPainter oldDelegate) {
return false ;
}
}
2. Drawing Shapes π¦
Use the Canvas
object to draw shapes like circles, rectangles, arcs, and paths.
Copy void paint ( Canvas canvas, Size size) {
final paint = Paint ()
..color = Colors .blue
..style = PaintingStyle .fill;
final rect = Rect . fromLTWH ( 0 , 0 , 100 , 100 );
canvas. drawRect (rect, paint);
}
3. Drawing Text π
°οΈ
Draw text by first creating a TextPainter
, setting the text and style, then painting it onto the canvas.
Copy void paint ( Canvas canvas, Size size) {
final textPainter = TextPainter (
text : TextSpan (
text : 'Hello, world!' ,
style : TextStyle (color : Colors .black, fontSize : 30 ),
),
textDirection : TextDirection .ltr,
);
textPainter. layout ();
textPainter. paint (canvas, Offset ( 50 , 50 ));
}
4. Drawing Images πΌοΈ
Load images and draw them onto the canvas.
Copy ui. Image image;
void loadImage () async {
final ByteData data = await rootBundle. load ( 'assets/image.png' );
final List < int > bytes = data.buffer. asUint8List ();
final ui. Codec codec = await ui. instantiateImageCodec (bytes);
final ui. FrameInfo frameInfo = await codec. getNextFrame ();
image = frameInfo.image;
}
void paint ( Canvas canvas, Size size) {
if (image != null ) {
canvas. drawImage (image, Offset .zero, Paint ());
}
}
Custom Dial Example:
Code Result
Copy import 'dart:math' ;
import 'package:flutter/material.dart' ;
void main () => runApp ( MyApp ());
class MyApp extends StatelessWidget {
@override
Widget build ( BuildContext context) {
return MaterialApp (
home : DialScreen (),
);
}
}
class DialScreen extends StatefulWidget {
@override
_DialScreenState createState () => _DialScreenState ();
}
class _DialScreenState extends State < DialScreen > {
double dialValue = 0.0 ;
void _updateDialValue ( double value) {
setState (() {
dialValue = value;
});
}
@override
Widget build ( BuildContext context) {
return Scaffold (
appBar : AppBar (
title : Text ( 'Custom Dial Example' ),
),
body : Center (
child : CustomDial (
value : dialValue,
onChanged : _updateDialValue,
),
),
);
}
}
class CustomDial extends StatelessWidget {
final double value;
final ValueChanged < double > onChanged;
CustomDial ({ required this.value, required this.onChanged});
@override
Widget build ( BuildContext context) {
return GestureDetector (
onPanUpdate : (details) {
RenderBox box = context. findRenderObject () as RenderBox ;
Offset position = box. globalToLocal (details.globalPosition);
final double angle = atan2 (position.dy - box.size.height / 2 , position.dx - box.size.width / 2 );
final double newValue = angle / ( 2 * pi) + 0.5 ;
onChanged (newValue. clamp ( 0.0 , 1.0 ));
},
child : CustomPaint (
painter : DialPainter (value),
size : Size ( 200 , 200 ),
),
);
}
}
class DialPainter extends CustomPainter {
final double value;
DialPainter (this.value);
@override
void paint ( Canvas canvas, Size size) {
double angle = 2 * pi * (value - 0.25 );
Offset center = size. center ( Offset .zero);
double radius = size.width / 2 ;
Paint paintLine = Paint ()
..color = Colors .blue
..style = PaintingStyle .stroke
..strokeWidth = 8.0 ;
Paint paintOutline = Paint ()
..color = Colors .black
..style = PaintingStyle .stroke;
Paint paintKnob = Paint ()..color = Colors .black;
canvas. drawCircle (center, radius, paintOutline);
canvas. drawLine (center, Offset (center.dx + radius * cos (angle), center.dy + radius * sin (angle)), paintLine);
canvas. drawCircle ( Offset (center.dx + radius * cos (angle), center.dy + radius * sin (angle)), 25 , paintKnob);
}
@override
bool shouldRepaint ( CustomPainter oldDelegate) {
return oldDelegate != this;
}
}
In this example:
We create a DialScreen
widget to hold the current value of the dial and to create a CustomDial
widget.
The CustomDial
widget takes a value and a callback for when the value changes. It creates a GestureDetector
to handle drag updates, and a CustomPaint
widget to actually draw the dial.
Inside the onPanUpdate
callback, we calculate the new value of the dial based on the angle of the drag and call onChanged
to update the value.
We create a DialPainter
class that extends CustomPainter
, which draws the dial using the current value.
The paint
method of DialPainter
, we draw an outline circle, a line from the centre of the circle to the current position of the dial, and a knob at the end of the line.
Assignments π
Last updated 6 months ago