I need to implement a stopwatch that is controlled by a FloatingActionButton
. However, even when I declare the stopwatch class statically, I am unable to access the SetState()
member function at all, which makes it impossible to update the start/stop status of the stopwatch externally.
Minimal example:
import 'package:flutter/material.dart';
import 'dart:async';
void main() {
runApp(
app(),
);
}
class StopWatch extends StatefulWidget {
const StopWatch({super.key});
@override
State<StopWatch> createState() => _StopWatchState();
}
class _StopWatchState extends State<StopWatch> {
static final Stopwatch _stopwatch = Stopwatch();
late Timer _timer;
String _result = "0";
void Start() {
_timer = Timer.periodic(
const Duration(milliseconds: 1),
(Timer t) {
setState(
() {
_result = _stopwatch.elapsed.inSeconds.toString();
},
);
},
);
_stopwatch.start();
}
void Stop() {
_timer.cancel();
_stopwatch.stop();
}
@override
Widget build(BuildContext context) {
return Text(_result);
}
}
class app extends StatelessWidget {
app({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: SingleChildScrollView(
child: Scaffold(
body: StopWatch(),
floatingActionButton: FloatingActionButton.small(
onPressed: () {},
),
),
),
);
}
}
I’ve tried looking at event listeners, but they don’t seem to be the correct tool for the job.
The current state of the app makes it unfeasible to abstract out the clock logic, and only use the stateful widget to update the text.
I’ve also attempted to look for a way to include the FloatingActionButton
within the StopWatch
class, but there doesn’t seem to be a way to do that such that it’s placed within the outermost ‘scope’ of the heirarchy, i.e. the MaterialApp
, let alone put it within the Scaffold
class.
2
Answers
Actually, you need a state management tool to properly handle this. But, well, you can do it without depending on other state management tools but just
setState
and using aKey
to identify your object.If that’s what you’re looking for, check the following code:
Note that I’ve added a
isRunning
method to expose the state of the Timer – whether the stop watch is currently running or not.But, the main thing here is the usage of GlobalKey to access the current state of the child widget.
setState
should NOT be accessed from outside a widget itself, and it’s part of the hidden state class of StatefulWidget’s.You could take a similar approach as what it is done with
TextField
and have a controller which allows you to listen and modify the internal state of your timer.Then any widget can listen to changes emitted by that controller: