Introduction
At the end of this tutorial series you will learn how to use the custom paint widget by creating a Linear Gauge. The custom paint widget allows you to draw and paint directly to the canvas. This is useful when you can't find or combine a pre build widget to meet your needs.
At the end of this series your linear gauge will look like this.
Creating a flutter project
Before we start building a linear gauge, let's create our flutter project. In your terminal run;
$ flutter create linear_gauge
In main.dart
delete everything starting from line 7 and replace it with the following.
import 'linear_gauge.dart';
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Linear Gauge',
home: LinearGauge(maxValue: 100, minValue: 0, actualValue: 75),
);
}
}
Now we have to create the LinearGauge()
widget.
How a linear gauge works
From the image in the introduction we can deduce that the gauge need to be provided with a maximum value, a minimum value and an actual value. The gauge then displays the actual value in proportion to the maximum and minimum value.
Let's create our stateful widget to represent this;
$ touch lib/linear_gauge.dart
import 'package:flutter/material.dart';
import 'linear_gauge_custom_painter.dart';
class LinearGauge extends StatefulWidget {
LinearGauge({
required this.maxValue,
required this.minValue,
required this.actualValue,
Key? key,
}) : super(key: key);
final double maxValue;
final double minValue;
final double actualValue;
@override
State<LinearGauge> createState() => _LinearGaugeState();
}
class _LinearGaugeState extends State<LinearGauge> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Linear Gauge')),
body: CustomPaint(
size: MediaQuery.of(context).size,
painter: LinearGaugeCustomPainter(),
),
);
}
}
You will notice our body
property accepts a CustomPaint
that takes in a LinearGaugeCustomPainter
. This class will be used to draw and paint our linear gauge into existence.
We used MediaQuery.of(context).size
to ensure our gauge scales to the device size.
Components of a linear gauge
Before we start painting it is important to breakdown our linear gauge into its components. From the image above we notice that the gauge comprises of;
A main axis - this is the vertical part of the gauge.
Major ticks - This is the long horizontal lines
Minor ticks - This is the short horizontal lines.
Scale - The numbers that the ticks represents.
Pointer - The vertical line that colors the main axis from the minimum value to the actual value.
The actual value indicator - The icon that shows where the actual value is.
The actual Value - The TextPainter that displays the actual value.
$ touch lib/linear_gauge_custom_painter.dart
import 'package:flutter/material.dart';
class LinearGaugeCustomPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// TODO: implement paint
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
throw UnimplementedError();
}
}
Our LinearGaugeCustomPainter extends CustomPainter which gives us access to the paint
and shouldRepaint
methods.
We will use our paint method to create the components mentioned above.
Drawing the main axis
Before we draw the main axis we first have to determine where it will begin and end. the size
parameter contains the height and width of the canvas. We must draw within the limits of the width and height parameters or our drawing won't be visible on the screen.
Let's define the start point and end point of our main axis.
class LinearGaugeCustomPainter extends CustomPainter {
///find Main Axis start point
Offset getMainAxisStartPoint(Size size) => Offset(size.width / 2, size.height / 7);
///find Main Axis endpoint
Offset getMainAxisEndPoint(Size size) => Offset(size.width / 2, size.height / 1.2);
...
}
To draw the main axis we need to draw a line from a start point of our choosing to an endpoint of our choosing. We determine these point using the Offset
class. This class accepts a x-axis value and a y-axis value.
To place the main axis of the gauge in the middle of the canvas along the x-axis we divide the size.width
by 2. We then set the y-axis to the lowest and highest point of the main axis by dividing the height of the canvas by a constant.
The the constants 7 and 1.2 was chosen because those are the height position at with I want the main axis to start and end. Feel free to play around with the constants to determine your prefered starting and endpoints. The size
parameter is used to ensure the main axis scale in proportion to the height of the canvas on any device.
class LinearGaugeCustomPainter extends CustomPainter {
...
/// Draws the main axis of the Gauge
void drawGaugeMainAxis(Canvas canvas, Size size) {
final gaugeMainAxisPainter = Paint()
..color = Colors.grey.shade300
..strokeWidth = 5.0;
canvas.drawLine(getMainAxisStartPoint(size), getMainAxisEndPoint(size), gaugeMainAxisPainter);
}
...
}
To draw the main axis we call the canvas.drawLine()
method and pass in the start point offset value and endpoint offset value. It also expects a Paint()
class that describes the style to use when drawing on the canvas.
class LinearGaugeCustomPainter extends CustomPainter {
...
@override
void paint(Canvas canvas, Size size) {
drawGaugeMainAxis(canvas, size);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
We call our drawGaugeMainAxis()
method in inside the paint
method since this method is called whenever the object needs to paint.
We return true to our shouldRepaint()
since we'll be adding animation to our gauge and we'll need to repaint during the animation from 0 to the actual value.
Run the code and our scale widget will look like;
Conclusions
We've created the main axis of our linear gauge and in the process learnt how to draw a line on the canvas at a start and endpoint of our choosing. In the next article in this series we'll learn how to position our major ticks along the main axis.
Top comments (0)