I have 2 function that call NTP to get local date time. First one is working like a clock using Timer.periodic and the second one for get the time when user tapped the button. But when I display the time in the second function its still runing the Timer.periodic. How to avoid that? Thanks
My Code:
Future<String> getNTPTime() async {
DateTime startDate = await NTP.now();
String currentDate = DateFormat('d MMMM yyyy').format(startDate);
String currentTime = DateFormat('kk : mm : ss').format(startDate);
print('NTP DateTime: $startDate');
if (mounted) {
setState(() {
_currentDate = currentDate;
_currentTime = currentTime;
});
}}
Future<String> getNTPTimeIn() async {
DateTime ntpTimeIn = await NTP.now();
String timeIn = DateFormat('kk : mm : ss').format(ntpTimeIn);
print('NTP DateTime: $ntpTimeIn');
if (mounted) {
setState(() {
_ntpTimeIn = timeIn;
});
}}
and call it in the initState
#override
super.initState();
getNTPTime();
Timer.periodic(Duration(seconds: 1), (timer) => getNTPTime());
}
when I display the _ntpTimeIn the Timer.periodic is still running.
This is how you should implement it.
class YourClas extends StatefulWidget{
Timer _timer;
#override
void initState(){
super.initState();
getNTPTime();
_timer = Timer.periodic(Duration(seconds: 1), (timer) => getNTPTime());
}
#override
void dispose(){
if(_timer.isActive) _timer.cancel();
super.dispose();
}
}
Then when user taps the button call _timer.cancel(); to stop the Timer
Related
I want a page that has a timer and also displays math problems. and whenever the correct answer has been entered, a new task should appear. But the problem is that whenever a new task appears, the timer is reset. How can I prevent this ?
late Timer timer;
double value = 45;
void startTimer() {
timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (value > 0) {
setState(() {
value--;
});
} else {
setState(() {
timer.cancel();
});
}
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
startTimer();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
void userTextFieldInput() {
controller.addListener(
() {
String rightResult = (firstIntValue + secondIntValue).toString();
String userResult = controller.text;
if (rightResult == userResult) {
setState(() {
DatabaseHelper(
firstUserValue: firstIntValue,
secondUserValue: secondIntValue,
finalUserResult: int.parse(controller.text),
).setDB();
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const CalculatePage(),
),
);
});
} else if (controller.text.length >= 2) {
controller.clear();
}
},
);
}
You should create two different state, one for timer and another one for the problems.
You can use the package flutter bloc to manage these state easily.
I am trying to make a simple timer which run till a given time. This is how I have tried to call the timer function. It gives the error as mentioned in the title. I believe the error is there because I am calling set state method in the init state, but I really need to make this functionality that, when this widget enters the screen, a timer begins and do something when the timer ends. Any help is greatly appreciated.
late double timeRemaining;
late Timer _timer;
void startTimer(double timeRemaing) {}
#override
void initState() {
timeRemaining =
widget.startDate.difference(widget.endDate).inSeconds / 1000 - 80;
const Duration seconds = Duration(seconds: 1);
_timer = Timer.periodic(seconds, (timer) {
setState(() {
timeRemaining--;
if (timeRemaining <= 0) {
// done = true;
done = true;
timer.cancel();
}
});
});
super.initState();
}
as the title says, you're building widget during another build (when you call setState in the timer).So the solution is to wait for the widget to finish building, then start your timer, this can be done by using addPostFrameCallback, like the following:
#override
void initState() {
timeRemaining =
widget.startDate.difference(widget.endDate).inSeconds / 1000 - 80;
const Duration seconds = Duration(seconds: 1);
// this will schedule a callback for the end of this frame.
WidgetsBinding.instance.addPostFrameCallback((_) {
_timer = Timer.periodic(seconds, (timer) {
setState(() {
timeRemaining--;
if (timeRemaining <= 0) {
// done = true;
done = true;
timer.cancel();
}
});
});
});
super.initState();
}
try it and tell me if this works
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?
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);
}
}
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.
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();
}
}
I'm building an app with Flutter and I need to show a clock that includes milliseconds
Here's what I have so far. It works, but only shows seconds.
#override
void initState() {
_timeString = DateFormat('hh:mm:ss').format(DateTime.now());
Timer.periodic(Duration(milliseconds: 50), (Timer t) => _getTime());
super.initState();
}
void _getTime() {
final String formattedDateTime =
DateFormat('hh:mm:ss').format(DateTime.now());
setState(() {
_timeString = formattedDateTime;
});
}
If anyone is interested, using 'hh:mm:ss:SS' is enough.
I am trying to run a timer function and when the timer value reached a particular value i need to trigger another function. so i need to listen to the value change in the int start
import 'dart:async';
class CustomTimer{
Timer _timer;
int start = 0;
void startTimer(){
const oneSec = Duration(seconds: 1);
_timer = Timer.periodic(oneSec, (Timer timer){
start++;
print('start value $start');
});
}
void cancelTimer()
{
_timer.cancel();
}
}
I am calling this function from another class, How can i do that?
You should be implement below way
class CustomTimer {
Timer _timer;
int start = 0;
StreamController streamController;
void startTimer() {
const oneSec = Duration(seconds: 1);
streamController = new StreamController<int>();
_timer = Timer.periodic(oneSec, (Timer timer) {
start++;
streamController.sink.add(start);
print('start value $start');
});
}
void cancelTimer() {
streamController.close();
_timer.cancel();
}
}
Other class when you listen updated value
class _EditEventState extends State<EditEvent> {
CustomTimer customTimer = new CustomTimer();
#override
void initState() {
customTimer.startTimer();
customTimer.streamController.stream.listen((data) {
print("listen value- $data");
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Container()
);
}
#override
void dispose() {
customTimer.cancelTimer();
super.dispose();
}
}
Here, I have created on streambuilder for listen int value
int doesn't have something like a listener. But you could check for your event inside your regulary called timer function and then run a submitted method:
import 'dart:async';
class CustomTimer{
Timer _timer;
int start = 0;
Function callback;
void startTimer(Function callback, int triggerValue){
const oneSec = Duration(seconds: 1);
_timer = Timer.periodic(oneSec, (Timer timer){
start++;
print('start value $start');
if (start >= triggerValue)
callback();
});
}
void cancelTimer()
{
_timer.cancel();
}
}
You can call it with this:
CustomTimer timer = CustomTimer();
timer.startTimer(10, () {
print('timer has reached the submitted value');
timer.cancelTimer();
});