Build a Simple Stopwatch in Flutter

Diganta Kalita
Jul 20, 2020 · 8 min read

Or is it really that simple?

Image for post
Image for post

So I was working on a project where I needed to implement a simple stopwatch to let the user know about the time interval spent doing a particular task. Okay, how difficult will that be right? Dart already has a Stopwatch() class that I can use directly.

But as I started working on it, I realized how awfully wrong I was and how such a simple feature is not that easy to implement and how the Stopwatch() class in Dart really sucks! (That is only one person’s opinion. It may not suck for you). And since I am a lazy person, I thought okay lets just search “create a stopwatch in flutter” and then copy the code and paste it. But OH MY GOD! how complex was the code. It was like 1000 lines of code for a simple stopwatch feature. (And its bad practice to use code that you can’t understand).

Image for post
Image for post

So instead of going through all those 1000 lines of code I decided to write the stopwatch feature myself in as simple way as possible. After 2 hours of coding and a broken glass I was able to manage something like that on the left side.

A Simple Stopwatch with a Start and Reset Button.

And it was accomplished in a fairly low amount of code.( I hope you have the same opinion too after reading the article. Fingers Crossed)

My Thought Process on building The Feature:

Okay So I need a timer which looks like 00:00. It should start with a START button and go back to zero with a RESET Button. Ummm… Dart has a stopwatch library right (which I used earlier to calculate time taken by a particular bunch of code). I can use that directly right and display it on the screen. BOOM. I just have to use a Text widget, start the stopwatch (using the Stopwatch() class) and display the elapsed time on Screen.

But wait. I will need to read a different value in the Text Widget every second. So I need to get the data as a Stream. But Unfortunately Stopwatch() class of dart has no method to get the values as a stream. It only outputs a single value whenever one of its methods is called.

So I can’t use it. Okay this is where I realized that this is not going to be as easy as I initially thought.

New Plan. I need to create a Stream which outputs a new value every second. Kind of Like 1,2,3,4,5,6,… Then I can listen to the stream and update the values in the Text Widget. Problem solved (at-least in my mind).

But wait. There is another problem. What happens when the value crossed 60. I can’t show 61,62,63,etc. right. I need to format the values and change it minutes and seconds so that I can display them beautifully.

Lets Get to the Code Now

Step 1: Creating a Stopwatch Stream

First we will create a stream which will give us the elapsed time in seconds after every second so that we can update it to the UI.

We defined a method called stopWatchStream() which will return a Stream of integers.

We will be using a StreamController to control the events of the Stream.

A StreamController gives you a new stream and a way to add events to the stream at any point, and from anywhere. The stream has all the logic necessary to handle listeners and pausing. You return the stream and keep the controller to yourself.

When instantiating a StreamController we need to pass few parameters there. First is onListen which will be called whenever we want to listen to the Stream. Next is OnCancel which will be called on cancelling the Stream. We will get to OnResume & OnPause in a bit.

startTimer()

You may use a different interval to instead of 1 second. Just change the Duration in timerInterval variable

Tick()

stopTimer()

Next we cancel the timer. Once cancelled it will stop running the tick function every second.

We also set it to null so that it starts from scratch and doesn’t start from the place it ended last time.

Next we set the counter to 0 so that it starts from 0 too next time the timer is started.

And finally we close the Stream using streamcontroller.close()

Step 2: Adding the timer stream to the UI

We have 3 primary widgets here:-

  • Text Widget to display the Time
  • A Start Button
  • A Reset Button

The Text widget is simple. It is simply displaying the time in HH:MM:SS format.

Now lets look at the Start Button.

timerStream = stopWatchStream();

First we are creating a new stream from our stopWatchStream() that we created previously and set it to var timerStream. Now you might be thinking why am I creating a new stream every time START button is pressed. I could create a new Stream in initState directly too right and that would be more efficient.

A BIG NO!

If you do that then yes when you click on the Start Button first time everything will work fine. But if you click on the Start button a 2nd time then you will get an error like this

Flutter : Bad state: Stream has already been listened to

This is because dart somehow allows you to listen to a stream only once even after cancelling the subscription to the stream.

One workaround around this problem is to use a BroadCast stream. But problem is it doesn’t close the stream fully. It only pauses the stream in a way and then resumes from the same place we left earlier when started again.

So in our stopwatch when I click on the START button, I want the timer to go like 1,2,3,4,5… and then clicking on the reset button should reset the timer to 0. If I then click on the START button a second time, it must start again from 0,1,2,3,4,5 and so on. But if you are using a broadcast stream what happens is if you click on the RESET button the timer will go back to 0 in the UI but in the background it will still be running. So when you click on the START button a second time, instead of starting from 0 again it will start from 11,12,13 or something like that(the number of seconds that has passed since you clicked on the START button first time).

So to fix this bug I just instantiate a new Stream every time START button is clicked.

Listening to timerStream

As you can see inside the listen function we are using 3 String variables hoursSTR, minutesStr and secondsStr. In each one of it we are using an algorithm to transform the tick to hours, minutes, seconds respectively and then updating the variables inside a setState() function.

Let me explain the formatting in the secondsStr with an example:

secondsStr = (newTick % 60).floor().toString().padLeft(2, ‘0’);

Suppose the newTick value is 81. Ofcourse we can’t show 81 because there are only 60 seconds in a clock. And after seconds must start from 1 again till 60 and so on. So 81 seconds should be shown as 21 in our stopwatch according to the HH:MM:SS format. Lets see how our code achieves that.

First 81 % 60 = 21

.floor() simply changes it to int. (I know its already int, just as a extra measure. Prevention is better than cure right!)

Next convert it to String using toString()

Well padLeft(2, ‘0’) will have no effect on 21. But if it was a single digit number then it will pad a 0 on the left of the number. (For example -> 4 to 04)

Well minutesStr is also same just the difference being that we are first dividing the tick value to 60 to convert from seconds to minutes.

Similarly in hoursStr we are dividing it by 3600 to convert to hours.

(You may also do Days by dividing by 3600*24 or years 3600*24*365)

Finally the RESET Button

I also set the timerStream to null just as a extra measure.

And DONE!

Clicking on the START button starts the timer and the RESET button resets the time back to ZERO.

You can find the complete code in github here: https://github.com/realdiganta/Flutter-Stopwatch

Please let me know If I made any mistakes in the code or while explaining the code or if you think that I can make the code simpler in any way. Because that was my primary motive behind this: To build a StopWatch in Flutter with as few lines of code as possible.

If I was able to add value to your day in any way please don’t forget to clap for the article. That really encourages me to write more articles like this.

You may contact me here: digantakalita.ai@gmail.com

References

  1. https://bloclibrary.dev/#/fluttertimertutorial

Analytics Vidhya

Analytics Vidhya is a community of Analytics and Data…

Sign up for Analytics Vidhya News Bytes

By Analytics Vidhya

Latest news from Analytics Vidhya on our Hackathons and some of our best articles! Take a look

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Diganta Kalita

Written by

I build cool stuff… Sometimes weird too

Analytics Vidhya

Analytics Vidhya is a community of Analytics and Data Science professionals. We are building the next-gen data science ecosystem https://www.analyticsvidhya.com

Diganta Kalita

Written by

I build cool stuff… Sometimes weird too

Analytics Vidhya

Analytics Vidhya is a community of Analytics and Data Science professionals. We are building the next-gen data science ecosystem https://www.analyticsvidhya.com

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store