Search code examples
flutterdartasynchronoustimerstopwatch

stopwatch and timer, problems with flutter


I'm new with dart and flutter and currently I'm studying so please don't judge me for this (maybe) symple question. I am trying to build a timer but all my code started from a stopwatch so I guess that's the first problem. In few words, I'm trying to create a timer from 3 minutes that I can stop and start anytime I want (it suppose to be a referee tool) but my starting string is '03:00' and I can see that, so that's fine, I can't find any answer though on how the time can run from 03:00 minutes to 00:00. Also, as you can see in my code I created a button to reset the time but it always goes back to 00:00 and not to 03:00. Anyone who can help? I'm definitely missing something.

import 'dart:async';
import 'package:flutter/material.dart';

class NewStopWatch extends StatefulWidget {
  @override
  _NewStopWatchState createState() => _NewStopWatchState();
}

class _NewStopWatchState extends State<NewStopWatch> {
  Stopwatch watch = Stopwatch();
  Timer timer;
  bool startStop = true;

  IconData btnPlayStatus = Icons.play_arrow;

  String elapsedTime = '03:00';

  updateTime(Timer timer) {
    if (watch.isRunning) {
      setState(() {
        elapsedTime = transformMilliSeconds(watch.elapsedMilliseconds);
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(20.0),
      child: Column(
        children: <Widget>[
          Text(elapsedTime, style: TextStyle(fontSize: 60.0)),
          SizedBox(height: 20.0),
          Row(
            crossAxisAlignment: CrossAxisAlignment.end,
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Container(
                width: 100,
                height: 50,
                child: FloatingActionButton(
                    shape: ContinuousRectangleBorder(),
                    heroTag: "btn1",
                    backgroundColor: Colors.blueGrey,
                    onPressed: () => startOrStop(),
                    child: Icon(btnPlayStatus)),
              ),
              SizedBox(width: 20.0),
              Container(
                width: 30,
                height: 50,
                child: FloatingActionButton(
                  shape: ContinuousRectangleBorder(),
                    heroTag: "btn2",
                    backgroundColor: Colors.blueGrey,
                    onPressed: () => resetWatch(), //resetWatch,
                    child: Icon(Icons.subdirectory_arrow_left)),
              ),
            ],
          )
        ],
      ),
    );
  }

  resetWatch() {
    setState(() {
      watch.reset();
      setTime();
    });
  }

  startOrStop() {
    if(startStop) {
      setState(() {
        btnPlayStatus = Icons.pause;
      });
      startWatch();
    } else {
      setState(() {
        btnPlayStatus = Icons.play_arrow;
      });
      stopWatch();
    }
  }

  startWatch() {
    setState(() {
      startStop = false;
      watch.start();
      timer = Timer.periodic(Duration(milliseconds: 100), updateTime);
    });
  }

  stopWatch() {
    setState(() {
      startStop = true;
      watch.stop();
      setTime();
    });
  }

  setTime() {
    var timeSoFar = watch.elapsedMilliseconds;
    setState(() {
      elapsedTime = transformMilliSeconds(timeSoFar);
    });
  }

  transformMilliSeconds(int milliseconds) {
    int hundreds = (milliseconds / 10).truncate();
    int seconds = (hundreds / 100).truncate();
    int minutes = (seconds / 60).truncate();

    String minutesStr = (minutes % 60).toString().padLeft(2, '0');
    String secondsStr = (seconds % 60).toString().padLeft(2, '0');

    return "$minutesStr:$secondsStr";
  }
}

Solution

  • First of all you need to think if you always want to start from 3 minutes; if so create a static field as follows:

    static duration = new Duration(minutes:3);
    

    Edit: I've refactored your code and made it working.

    updateTimer(Timer t) {
        if (watch.isRunning) {
          setState(() {
            Duration newDuration = _NewStopWatchState.duration -
                new Duration(milliseconds: watch.elapsedMilliseconds);
            elapsedTime = durationToMinutesAndSeconds(newDuration);
          });
        }
    }
    

    This is the updateTimer function. Next, you didn't update your stopWatch() function to take care of the new changes so I changed it for you.

    stopWatch() {
        setState(() {
          startStop = true;
          watch.stop();
          Duration newDuration = _NewStopWatchState.duration -
              new Duration(milliseconds: watch.elapsedMilliseconds);
          elapsedTime = durationToMinutesAndSeconds(newDuration);
        });
    }
    

    I've also updated the resetWatch() function

    resetWatch() {
        setState(() {
          watch.reset();
          elapsedTime = durationToMinutesAndSeconds(_NewStopWatchState.duration);
        });
    }
    

    I've also created an utility function to convert a duration to minutes and seconds.

    String durationToMinutesAndSeconds(Duration d) {
        return "${d.inMinutes.toString().padLeft(2, '0')}" +
                ":${d.inSeconds.remainder(60).toString().padLeft(2, '0')}";
    }
    

    I've tried it on my machine and the code is working, hope this time is working even on your side.