Flutter Countdown End Time - flutter

I'm trying to create a 2 minute counter. The purpose of this counter is to start again when the time expires and make a request to the server.
I created a countdown widget for this, but I couldn't make it start again and send a request to the server when the time expires. I will be glad if you help
Countdown Widget
class Countdown extends AnimatedWidget {
Countdown({Key? key, required this.animation})
: super(key: key, listenable: animation);
Animation<int> animation;
#override
build(BuildContext context) {
Duration clockTimer = Duration(seconds: animation.value);
String timerText =
'${clockTimer.inMinutes.remainder(60).toString()}:${clockTimer.inSeconds.remainder(60).toString().padLeft(2, '0')}';
return Text(
"$timerText ",
style: TextStyle(
fontSize: 14.sp,
color: AppColors.DARK_GREY,
),
);
}
}
Use countdown widget
Countdown(
animation: StepTween(
begin: controller
.levelClock.value, // THIS IS A USER ENTERED NUMBER
end: 0,
).animate(controller.animationController!),
),
page controller:
RxInt levelClock = 120.obs;
AnimationController? animationController;
#override
void onInit() {
animationController = AnimationController(
vsync: this, duration: Duration(seconds: levelClock.value));
animationController!.forward();
super.onInit();
}

Rather than making a counter and doing countdown you can use Timer.
You can refer below code.
import 'dart:async';
Timer timer = Timer.periodic(const Duration(minutes: 2), (Timer t) {
//do whatever you want
});

Related

Postpone the start of the AnimationController animation in Flutter

I'm trying to delay the animation for 5 seconds from the beginning, here's my code snippet, but it doesn't work properly, as it would be better to delay for 5 seconds from the start of the startup screen. I tried to make a separate function that would be responsible for the delay but it also caused me to display errors on the emulator. I tried to use a ternary operator and delay it, but it also didn't work properly. I also want my animation to come in 7 seconds, I think it can also be done in a ternary operator. I don't yet know exactly how to stop the animation, so I used a long delay in the animation to make it invisible.
class BubbleBackground extends StatefulWidget {
final Widget child;
final double size;
const BubbleBackground({
Key key,
#required this.child,
#required this.size,
}) : super(key: key);
#override
State<BubbleBackground> createState() => _BubbleBackgroundState();
}
class _BubbleBackgroundState extends State<BubbleBackground>
with SingleTickerProviderStateMixin {
bool timer = false;
final longAnimationDuration = 100000;
final shortAnimationDuration = 700;
AnimationController _controller;
#override
void initState() {
setState(() {
// Future.delayed(Duration(seconds: 5), () {
// timer = true;
// });
timer = true;
});
_controller = AnimationController(
lowerBound: 0.9,
duration: Duration(
milliseconds: timer
? Future.delayed(
Duration(seconds: 5),
() {
shortAnimationDuration;
},
)
: longAnimationDuration,
),
vsync: this,
)..repeat(reverse: true);
super.initState();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (_, __) {
return Transform.scale(
scale: _controller.value,
child: Container(
width: widget.size,
height: widget.size,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
),
child: widget.child,
),
);
},
);
}
}
i figured out that I could just write initState normally, and I'll get desirable result for time delay
#override
void initState() {
super.initState();
_controller = AnimationController(
lowerBound: 0.9,
duration: Duration(milliseconds: shortAnimationDuration),
vsync: this,
);
Future.delayed(Duration(seconds: 5), () {
_controller.repeat(reverse: true);
});
}

Flutter: Perform an action when a countdown timer reaches 0

I've created a screen in Flutter that displays a countdown timer. I'm able to play, pause and restart the timer, but I am trying to figure out how to perform an action when the timer reaches 0 (for example, restart itself).
As the dart file is fairly lengthy, I'm just copying below what I believe to be the relevant portions here. But I can add more if needed.
First I create a widget/class for the countdown timer:
class Countdown extends AnimatedWidget {
Countdown({ Key key, this.animation }) : super(key: key, listenable: animation);
Animation<int> animation;
#override
build(BuildContext context){
return Text(
animation.value.toString(),
style: TextStyle(
fontSize: 120,
color: Colors.deepOrange
),
);
}
}
I then have a stateful widget which creates the controller and also imports some data (gameData.countdownClock is the countdown timer's start time, it comes from user input at an earlier screen):
class _GameScreenState extends State<GameScreen> with TickerProviderStateMixin {
AnimationController _controller;
_GameScreenState(this.gameData);
GameData gameData;
#override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: gameData.countdownClock),
);
}
And then the container that displays the clock:
Container(
child: Countdown(
animation: StepTween(
begin: gameData.countdownClock,
end: 0,
).animate(_controller),
),
),
Do I have to add a listener in that last container? Or somewhere else? (Or something else entirely!)
Any help is appreciated. Thank you
I found the answer on this page:
After complete the widget animation run a function in Flutter
I needed to add .addStatusListener to the animation controller in the initState().
So the new initState() code looks like this:
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: gameData.countdownClock),
);
_controller.addStatusListener((status){
if(status == AnimationStatus.completed){
_controller.reset();
}
}
);
}
Possible value of Animation controller is between 0 to 1.
So I think you have to add listener on gamedata.countDownClock
Please check the below code you might get some idea from it.
import 'dart:async';
import 'package:flutter/material.dart';
class GameScreen extends StatefulWidget {
#override
_GameScreenState createState() => _GameScreenState();
}
class _GameScreenState extends State<GameScreen> with SingleTickerProviderStateMixin {
int countdownClock = 10;
#override
void initState() {
super.initState();
// Your Game Data Counter Change every one Second
const oneSec = const Duration(seconds:1);
new Timer.periodic(oneSec, (Timer t) {
// Restart The Counter Logic
if (countdownClock == 0)
countdownClock = 11;
setState(() { countdownClock = countdownClock - 1; });
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("")),
body: Center(
child: Text('$countdownClock')
),
);
}
}

Flutter AnimationController with dynamic duration - error: Const variables must be initialized with a constant value

I want to set the animation duration from my widget parameter but it doesn't work because duration wants to be initialized with a constant
class CircularTimer extends StatefulWidget {
CircularTimer({#required this.seconds});
_CircularTimer createState() => _CircularTimer();
final seconds;
}
class _CircularTimer extends State<CircularTimer>
with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
#override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(/*not working*/seconds: widget.seconds), vsync: this);
animation = Tween<double>(begin: 0, end: 300).animate(controller);
controller.forward();
}
#override
Widget build(BuildContext context) =>
CircularTimerWidget(animation: animation);
}
You can't pass data like this to const, so the solution would be either to remove const from Duration or simply use some const value.
Solution:1
controller = AnimationController(
duration: Duration(seconds: widget.seconds), // remove const
vsync: this,
);
Solution:2
controller = AnimationController(
duration: const Duration(seconds: 1), // some const value
vsync: this,
);

Flutter - Restart CountdownTimer

I have a countdown timer (5 mins). I need something to restart this countdown and start from the beginning.
I try to change state of countdown variable but doesn´t works, stops and restarting from the last number of counter.
My code:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:quiver/async.dart';
class ProgressIndicatorDemo extends StatefulWidget {
#override
_ProgressIndicatorDemoState createState() =>
new _ProgressIndicatorDemoState();
}
class _ProgressIndicatorDemoState extends State<ProgressIndicatorDemo>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> animation;
var countdown;
int actual;
#override
void initState() {
super.initState();
animationStart();
startTimer();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
void animationStart() {
controller =
AnimationController(duration: Duration(minutes: 5), vsync: this);
animation = Tween(begin: 0.0, end: 1.0).animate(controller)
..addListener(() {
setState(() {});
});
controller.forward();
}
void startTimer() {
CountdownTimer countDownTimer = new CountdownTimer(
new Duration(minutes: 5),
new Duration(seconds: 1),
);
countdown = countDownTimer.listen(null);
countdown.onData((duration) {
setState(() {
actual = 300 - duration.elapsed.inSeconds;
});
});
countdown.onDone(() {
countdown.cancel();
});
}
#override
Widget build(BuildContext context) {
return new Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
animation.value != 1.0
? CircularProgressIndicator(
semanticsLabel:
(animation.value * 100.0).toStringAsFixed(1).toString() +
'%',
semanticsValue:
(animation.value * 100.0).toStringAsFixed(1).toString() +
'%',
backgroundColor: Colors.black.withOpacity(0.25),
valueColor:
new AlwaysStoppedAnimation<Color>(Colors.green[700]),
value: animation.value)
: Icon(Icons.info_outline),
actual != null
? Text(
"Tiempo:" +
DateFormat.ms()
.format(DateTime.fromMillisecondsSinceEpoch(
actual * 1000))
.toString(),
textAlign: TextAlign.center,
)
: Container(),
FlatButton(
child: Text('Reset ' + actual.toString()), onPressed: () {}),
],
),
);
}
}
So the question is: How can i restart from the beggining the countdown?
You could use a cleaner solution by using the RestartableTimer
import 'package:async/async.dart';
RestartableTimer _timer = new RestartableTimer(_timerDuration, _startNewPage);
Then you can restart your countdown by simply calling _timer.reset();
Hope it helps.
Try cancel the timer first and start the timer again :
void restartTimer() {
countDownTimer.cancel();
startTimer();
}
try to declare your Timer at the top and whenever you want to restart it call this method.
void restartTimer() {
countDownTimer = new CountdownTimer(
new Duration(minutes: 5),
new Duration(seconds: 1),
);
don't forget to declare the variable

Triggering widget animation from outside widget

I have a custom widget that has normal / animated state. Sometimes I want to be it animated, and sometimes static.
I have made a simple test project to demonstrate my problem: test page contains my custom widget (ScoreBoard) and 2 buttons to start / stop animating scoreboard. My problem, that ScoreBoard is not animated, even if I start animation.
Here is my code:
TestPage:
class TestPage extends StatefulWidget {
#override
_TestPageState createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
bool _animate;
#override
void initState() {
_animate = false;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
ScoreBoard(
text: "Hello, world!",
animate: _animate,
),
FlatButton(
child: Text("START animation"),
onPressed: () {
setState(() {
_animate = true;
});
},
),
FlatButton(
child: Text("STOP animation"),
onPressed: () {
setState(() {
_animate = false;
});
},
),
],
),
);
}
}
ScoreBoard widget:
class ScoreBoard extends StatefulWidget {
final String text;
final bool animate;
const ScoreBoard({Key key, this.text, this.animate}) : super(key: key);
#override
_ScoreBoardState createState() => _ScoreBoardState();
}
class _ScoreBoardState extends State<ScoreBoard>
with SingleTickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
super.initState();
_controller = new AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
)..forward();
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return widget.animate
? ScaleTransition(
child:
Text(widget.text, style: Theme.of(context).textTheme.display1),
scale: new CurvedAnimation(
parent: _controller,
curve: Curves.easeIn,
),
)
: Container(
child:
Text(widget.text, style: Theme.of(context).textTheme.display1),
);
}
}
Would you be so kind to help me? Thanks in advance!
Answer
If you initialize an AnimationController widget and do not specify arguments for lowerBound and upperBound (which is the case here), your animation is going to start by default with lowerBound 0.0.
AnimationController({double value, Duration duration, String debugLabel, double lowerBound: 0.0, double upperBound: 1.0, AnimationBehavior animationBehavior: AnimationBehavior.normal, #required TickerProvider vsync })
Creates an animation controller. [...]
https://docs.flutter.io/flutter/animation/AnimationController-class.html
If you initialize the state of your widget ScoreBoard, the method forward gets called only once during the whole lifetime of your app. The method forward makes that your animation increases from the lowerBound (0.0) to the upperBound (1.0) within 1 second.
Starts running this animation forwards (towards the end).
https://docs.flutter.io/flutter/animation/AnimationController/forward.html
In our case, there is no way back once the method forward got called. We are only able to stop the animation.
Press Ctrl + F5 for a full restart of the app to see the animation.
To make it even clearer, change the duration of your animation to 10 seconds.
Btw. since Dart 2 you do not need to use the new keyword.
#override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 10),
vsync: this,
)..forward();
}
Example
To see what happens you could add this to your build method:
#override
Widget build(BuildContext context) {
// Execute 'reverse' if the animation is completed
if (_controller.isCompleted)
_controller.reverse();
else if (_controller.isDismissed)
_controller.forward();
// ...
... and do not call the method forward in the initState method:
#override
void initState() {
super.initState();
_controller = new AnimationController(
duration: const Duration(seconds: 10),
vsync: this,
);
}