Ashish Rawat
Jul 22 · 4 min read

In this article, I will be building a count down timer with the help of the animation and using custom paint.

First of all, create an app which returns the MaterialApp.

Now create a new Widget CountDownTimer and make sure that it must be a Stateful Widget because we are using animation and we will need to add TickerProviderStateMixen .

class CountDownTimer extends StatefulWidget {
_CountDownTimerState createState() => _CountDownTimerState();

class _CountDownTimerState extends State<CountDownTimer>
with TickerProviderStateMixin {

Create an Animation Controller

Create an animation controller and initialise it in initState() .

AnimationController controller;

void initState() {
controller = AnimationController(
vsync: this,
duration: Duration(seconds: 5),

I am right now using only 5-second duration for my animation to happen.

Create a Circular progress bar using Custom paint

Create a class which extends CustomPainter

class CustomTimerPainter extends CustomPainter

Add properties

Now we will need some properties so we can customize my custom painter class from my widgets screen.

class CustomTimerPainter extends CustomPainter {
}) : super(repaint: animation);

final Animation<double> animation;
final Color backgroundColor, color;

I am using background color and color property for my Circular progress bar and to animate my progress bar, I will need an animation.

Override methods

We will override our paint method which will use to paint our circular progress bar.

First, we will paint a circle and we will create an arc will move around the circle.

In the paint method, create a Paint object and add properties.

Paint paint = Paint()
..color = backgroundColor
..strokeWidth = 10.0
..strokeCap = StrokeCap.butt = PaintingStyle.stroke;

Now draw a circle

canvas.drawCircle(, size.width / 2.0, paint);

When the circle is painted then will need to draw an arc to move.

paint.color = color;
double progress = (1.0 - animation.value) * 2 * math.pi;
canvas.drawArc( & size, math.pi * 1.5, -progress, false, paint);

Change the color of the paint and update the process using the current animation value and draw the arc.

Now we also have an override method shouldRepaint .

bool shouldRepaint(CustomTimerPainter old) {
return animation.value != old.animation.value ||
color != old.color ||
backgroundColor != old.backgroundColor;

Here is the code for the CustomTimerPainter class

Create the UI

To animate my Custom Progress bar we will wrap my an AnimatedBuilder and provide the animation and it returns a CustomPaint Widget. When My animation will start the value of the arc will update in the custom painter class and update the UI

animation: controller,
(BuildContext context, Widget child) {
return CustomPaint(
painter: CustomTimerPainter(
animation: controller,
backgroundColor: Colors.white,
color: themeData.indicatorColor,

And to start and pause the animation Ill use the Floating Action Button, we have also Wrapped my FAB with AnimatedBuilder so we can update my play and pause status.

animation: controller,
builder: (context, child) {
return FloatingActionButton.extended(
onPressed: () {
if (controller.isAnimating)
else {
from: controller.value == 0.0
? 1.0
: controller.value);
icon: Icon(controller.isAnimating
? Icons.pause
: Icons.play_arrow),
label: Text(
controller.isAnimating ? "Pause" : "Play"));

Now if we see the app, it will look like this

Now Wrap the CustomTimerPainter inside the Stack and add text to represent the value.

To convert the animation duration into time String.

String get timerString {
Duration duration = controller.duration * controller.value;
return '${duration.inMinutes}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}';

Here is the code of CountDownTimer Widget

I have wrapped the root body widget and added a new Container and animating with animation value.

color: Colors.amber,
controller.value * MediaQuery.of(context).size.height,

Here this complete code

Thanks for reading this article ❤

If I got something wrong, Let me know in the comments. I would love to improve.

Clap 👏 If this article helps you.

Connect with me on LinkedIn.

Check my GitHub repositories.

Follow me on Twitter.

FlutterDevs has been working on Flutter from quite some time now. You can connect with us on Facebook, GitHub, and Twitter for any flutter related queries.


FlutterDevs intent to deliver Flutter apps with high quality. We’ve adopted Design First attitude which helps us deliver applications of highest quality.

Ashish Rawat

Written by

Android and Flutter Developer


FlutterDevs intent to deliver Flutter apps with high quality. We’ve adopted Design First attitude which helps us deliver applications of highest quality.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade