To be more precise, I would like to display the number of minutes passed since a timestamp fetched from an API. Should Stopwatch or Timer be used, or something else?
To be more precise, I would like to display the number of minutes passed since a timestamp fetched from an API. Should Stopwatch or Timer be used, or something else?
In your case, you should first parse your timestamp to a DateTime object. Then you can retrieve the time delta between the fetched DateTime and the current time by using the different method of the DateTime class.
final Duration myDuration = DateTime.parse(myTimeStamp).difference(DateTime.now));
For more information, please visit https://api.dart.dev/stable/2.10.4/dart-core/DateTime/difference.html.
Timer and Stopwatch are used for async work; you do not need such complexity level in your case.
I'd suggest something like the below. You can update the duration associated with the Timer to define how often it rebuilds the widget.
I've also left it up to you to decide how you would like to format the duration.
class ElapsedTime extends StatefulWidget {
final String timestamp;
const ElapsedTime({
Key key,
@required this.timestamp,
}) : super(key: key);
@override
_ElapsedTimeState createState() => _ElapsedTimeState();
}
class _ElapsedTimeState extends State<ElapsedTime> {
Timer _timer;
DateTime _initialTime;
String _currentDuration;
@override
void didUpdateWidget(ElapsedTime oldWidget) {
super.didUpdateWidget(oldWidget);
if(widget.timestamp != oldWidget.timestamp) {
_initialTime = _parseTimestamp();
_currentDuration = _formatDuration(_calcElapsedTime());
}
}
@override
void initState() {
super.initState();
_initialTime = _parseTimestamp();
_currentDuration = _formatDuration(_calcElapsedTime());
_timer = Timer.periodic(const Duration(seconds: 1), (Timer t) {
setState(() {
_currentDuration = _formatDuration(_calcElapsedTime());
});
});
}
Duration _calcElapsedTime() => _initialTime.difference(DateTime.now());
DateTime _parseTimestamp() => DateTime.parse(widget.timestamp);
// TODO update this to fit your own needs
String _formatDuration(final Duration duration) => duration.toString();
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Text(_currentDuration);
}
}
Another solution, based on JayDev's answer, has a property that represents the number of milliseconds since the "Unix epoch", e.g. DateTime.now().millisecondsSinceEpoch corresponds to the current number of milliseconds passed since 1st of January 1970.
In the initialize() method a timer initialization is delayed by the number of seconds passed since the whole minute, otherwise, it would initially wait for 60 seconds to increment a counter.
This widget only shows the number of minutes since a timestamp, the same logic can be applied to show more information.
class ElapsedTime extends StatefulWidget {
final int timestamp;
const _lapsedTime({Key key, this.timestamp}) : super(key: key);
@override
__ElapsedTimeState createState() => _ElapsedTimeState();
}
class _ElapsedTimeState extends State<ElapsedTime> {
Timer _timer;
int counter; // represents the number of minutes
@override
void didUpdateWidget(ElapsedTime oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.timestamp != oldWidget.timestamp) initialize();
}
@override
void initState() {
super.initState();
initialize();
}
void initialize() {
counter = calculateElapsedMinutes();
Future.delayed(Duration(milliseconds: widget.timestamp % 60000))
.then((_) => setupTimerAndIncrement());
}
int calculateElapsedMinutes() =>
((DateTime.now().millisecondsSinceEpoch - widget.timestamp) / 60000)
.floor();
void setupTimerAndIncrement() {
if (!mounted) return; //to prevent calling setState() after dispose()
incrementCounter();
_timer = Timer.periodic(
const Duration(minutes: 1), (Timer t) => incrementCounter());
}
@override
Widget build(BuildContext context) => Text(counter.toString());
void incrementCounter() => setState(() => counter++);
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
}