Using the Custom Paint widget to Create a Linear Gauge - Part 1
Tags: flutter, dart, custompainterwidget, lineargauge
Published: 2022-10-14 15:47:30

## 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.


![simple linear gauge final](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5dj5mc05tacvx4fx2bmd.png)

## 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.

```dart
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
```

```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;

1. A main axis - this is the vertical part of the gauge.

2. Major ticks - This is the long horizontal lines

3. Minor ticks - This is the short horizontal lines.

4. Scale - The numbers that the ticks represents.

5. Pointer - The vertical line that colors the main axis from the minimum value to the actual value.

6. The actual value indicator - The icon that shows where the actual value is.

7. The actual Value - The TextPainter that displays the actual value.

```
$ touch lib/linear_gauge_custom_painter.dart
```

```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.

```dart
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.

```dart
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.

```dart
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;


![main axis](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5j67ptomtm660rcbvv55.png)

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.