Search code examples
flutterdarttimerstopwatch

Flutter start and stop stopwatch from parent widget


I have a stateful widget of stopwatch and I am using that widget on multiple pages. I am starting stopwatch on load but I want to stop stopwatch on some condition from my parent widget but that is not working.

My Stop Watch Code

class StopWatch extends StatefulWidget {
  start() => createState().start();
  stop() => createState().stop();

  @override
  _StopWatchState createState() => _StopWatchState();
}

class _StopWatchState extends State<StopWatch> {

  String timeToDisplay = '00:00:00';
  Stopwatch swatch = Stopwatch();
  final duration = const Duration(seconds: 1);

  void startTimer() {
    Timer(duration, keepRunning);
  }

  void keepRunning() {
    if (swatch.isRunning) {
      startTimer();
    }

    setState(() {
      var hours = swatch.elapsed.inHours.toString().padLeft(2, "0");
      var minutes = (swatch.elapsed.inMinutes % 60).toString().padLeft(2, "0");
      var seconds = (swatch.elapsed.inSeconds % 60).toString().padLeft(2, "0");

      timeToDisplay = hours + ':' + minutes + ':' + seconds;
    });
  }

  void start() {
    swatch.start();
    startTimer();
  }

  void stop() {
    swatch.stop();
  }

  void pause() {

  }

  @override
  void initState() {
    // TODO: implement initState
    start();
    super.initState();
  }

  @override
  void setState(fn) {
    if(mounted){
      super.setState(fn);
    }
  }
  
  @override
  void dispose() {
    // TODO: implement dispose
    swatch.stop();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text(
        timeToDisplay,
        style: TextStyle(
            fontSize: 50.0, fontWeight: FontWeight.bold),
      ),
    );
  }
}

Parent widget.

class RecordTrackScreen extends StatefulWidget {
  static String id = 'record-track';

  @override
  _RecordTrackScreenState createState() => _RecordTrackScreenState();
}

class _RecordTrackScreenState extends State<RecordTrackScreen> {
  stopTimer() {
    if (condition is true) {
      StopWatch().stop();
    }
  }
}

StopTimer is calling stop method if condition is satisfy but stopwatch is not stopping. I tried to stop stopwatch in StopWatch class just to cross check but that is working fine.

Dart Pad Url - https://dartpad.dev/70a97f62f6bf2d3c51e322c651566240

I am not sure what I am doing wrong. Please help.


Solution

  • This line of code can't stop the stop watch because it is stopping a new instance of StopWatch which is different from the StopWatch you have used.

    StopWatch().stop();
    

    Use a boolean to control the stop and start of the stopWatch

    MyWidget change to stateful widget

     bool isStop = false;
    
      @override
      Widget build(BuildContext context) {
        return Column(children: [
          StopWatch(isStop: isStop),
          RaisedButton(
              onPressed: () {
                setState(() {
                  isStop = true;
                });
              },
              child: Text('stop')),
          RaisedButton(
              onPressed: () {
                setState(() {
                  isStop = false;
                });
              },
              child: Text('start'))
        ]);
      }
    

    StopWatch:

    class StopWatch extends StatefulWidget {
      final bool isStop;
    
      const StopWatch({Key key, this.isStop}) : super(key: key);
    
      @override
      _StopWatchState createState() => _StopWatchState();
    }
    
    class _StopWatchState extends State<StopWatch> {
      //...some code
    
    
      @override
      void didUpdateWidget(StopWatch oldWidget) {
        super.didUpdateWidget(oldWidget);
        // Check isStop value is different from the previous state
        // this function will trigger when every time this widget is build
        if (oldWidget.isStop != widget.isStop) {
          widget.isStop ? stop() : start();
        }
      }
    

    Not suggest to call the Widget function in the parent Widget, since every time build, there will be a new instance of the Widget