How to Access Custom Hook functions from another class - flutter

i have a custom hook timer_hook.dart
The way this hooks works is whenever the user open's up the page, the counter begins to count down, so i am trying to access the method "restartTimer()"
from the main class where i am using the hook. but i dont know how to achieve that, sorry i am still learning flutter. and i don't want to use the stateful widget.
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
Duration useCountDownTimer() {
return use(const CountdownTimer());
}
class CountdownTimer extends Hook<Duration> {
const CountdownTimer();
#override
_CountdownTimerState createState() => _CountdownTimerState();
}
class _CountdownTimerState extends HookState<Duration, CountdownTimer> {
Timer? _timer;
Duration _duration = const Duration(seconds: 6);
void countDownTime() {
const reduceSeconds = 1;
setState(() {
final seconds = _duration.inSeconds - reduceSeconds;
_duration = Duration(seconds: seconds);
if (_duration.inSeconds == 0) {
_timer?.cancel();
}
});
}
void restartTimer() {
setState(() {
_duration = const Duration(seconds: 90);
_timer =
Timer.periodic(const Duration(seconds: 1), (_) => countDownTime());
});
}
#override
void initHook() {
super.initHook();
_timer = Timer.periodic(const Duration(seconds: 1), (_) => countDownTime());
}
#override
Duration build(BuildContext context) {
return _duration;
}
#override
void dispose() {
_timer?.cancel();
super.dispose();
}
}

Related

Autorefresh webview in flutter

I want to make a really basic app. Just load a website and refresh every let's say 1 hour. I use webview, and i've seen i can do this with timer. My webview code:
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
Timer timer;
void main() => runApp(WebViewExample());
class WebViewExample extends StatefulWidget {
#override
WebViewExampleState createState() => WebViewExampleState();
}
class WebViewExampleState extends State<WebViewExample> {
#override
void initState() {
super.initState();
// Enable hybrid composition.
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
}
#override
Widget build(BuildContext context) {
return WebView(
initialUrl: 'https://mywebsite.com',
);
}
}
timer(){
Timer _timer = new Timer.periodic(
oneDecimal,
(Timer timer) =>
setState(() {
if (_start < 100) {
_timer.cancel();
} else {
_start = _start - 100;
callRefresh();
}
}));
}
void initState() {
super.initState();
timer();
}

Repeating animations specific times e.g 20 times by Flutter

I have a simple animation :
Animation<double> animation;
Tween<double> tween = Tween(begin: 1, end: 1.15);
AnimationController animationController;
#override
void initState() {
super.initState();
animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 300));
animation = animationController.drive(tween);
}
What i want ?
make this animation repeats e.g 20 times .
What i tried before posted this question ?
1- This method not have a parameter or way that makes me repeats this animation e.g 20 times.
it repeats and repeats and repeats forever :
animationController.repeat();
2- Use simple loops . It hangs my app, it requires to stop entire application and rerun it to solve that hanging . It's seems that loops completely not dealing with futures :
do {
animationController.forward().then((x) {
animationController.reverse().then((x) {
repeats++;
});
});
} while (repeats < 20);
3- Making int variable and add a listener ..etc,it's working , but seems that it's not the best way to do that matter :
int repeats = 0;
animation.addStatusListener((status) {
if (repeats < 20) {
if (status == AnimationStatus.completed) {
animationController.reverse();
} else if (status == AnimationStatus.dismissed) {
animationController.forward();
}
repeats++;
} });
4- make chain of then . **No comment on it ** 😂😂😂 :
animationController.forward().then((x) {
animationController.reverse().then((x) {
animationController.forward().then((x) {
animationController.reverse().then((x) {
animationController.forward().then((x) {
animationController.reverse();
});
});
});
});
});
Now , shortly how can i repeat animation 20 times
You can try it:
5 - Use TickerFuture return from repeat, set timeout then stop animation.
AnimationController _animationController;
#override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 3),
);
_animationController.addListener(() => setState(() {}));
TickerFuture tickerFuture = _animationController.repeat();
tickerFuture.timeout(Duration(seconds: 3 * 10), onTimeout: () {
_animationController.forward(from: 0);
_animationController.stop(canceled: true);
});
}
This can be easily done without the timer with extension method:
extension on AnimationController {
void repeatEx({#required int times}) {
var count = 0;
addStatusListener((status) {
if (status == AnimationStatus.completed) {
if (++count < times) {
reverse();
}
} else if (status == AnimationStatus.dismissed) {
forward();
}
});
}
}
And you could use it like this:
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
)
..repeatEx(times: 10)
..forward();
If you have a Widget with a playing argument that can set to true or false dynamically (to start and stop animation dynamically), you can improve behaviour by using a cancellable Timer() instead of Future.timeout :
void updatePlaying() {
if (widget.playing != false && !_controller.isAnimating && !_loopHasCompleted) {
_controller.repeat();
_timeout?.cancel();
_timeout = Timer(widget.duration * widget.loops, () {
_controller.reset();
_loopHasCompleted = true;
});
} else if (widget.playing == false) {
if (_controller.isAnimating)
_controller.reset();
_loopHasCompleted = false;
}
}
AnimationController(
lowerBound: 0.0,
upperBound: 20.0,
vsync: this, duration: Duration(milliseconds: 300))
It's easy to use just like below
var counter = 0;
int repeatTimes = 10 // how many times you want to repeat animation
animationController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
if (++counter < repeatTimes) {
animation.reverse();
}
} else if (status == AnimationStatus.dismissed) {
animation.forward();
}
});
I created a simple widget for this purpose
import 'package:flutter/widgets.dart';
import 'package:lottie/lottie.dart';
class LottieRepeat extends StatefulWidget {
final String asset;
final int repeatTimes;
final VoidCallback onComplete;
const LottieRepeat(
{Key? key,
required this.asset,
required this.repeatTimes,
required this.onComplete})
: super(key: key);
#override
State<LottieRepeat> createState() => _MyLottieRepeatState();
}
class _MyLottieRepeatState extends State<LottieRepeat>
with TickerProviderStateMixin {
late final AnimationController _controller;
int count = 0;
#override
void initState() {
super.initState();
/// validate
if (widget.repeatTimes <= 0) throw Exception('invalid repeat time');
_controller = AnimationController(vsync: this);
_controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
count++;
if (count < widget.repeatTimes) {
_controller.reset();
_controller.forward();
} else {
widget.onComplete();
}
}
});
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Lottie.asset(
widget.asset,
controller: _controller,
onLoaded: (composition) {
// Configure the AnimationController with the duration of the
// Lottie file and start the animation.
_controller
..duration = composition.duration
..forward();
},
);
}
}
sample usage:
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: LottieRepeat(
asset: 'assets/image/tick.json',
repeatTimes: 2,
onComplete: () => Navigator.of(context).pop(true),
),
);
});

Flutter: how to listen to a int change?

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();
});

Show timer progress on a CircularProgressIndicator in flutter

I'm using a RestartableTimer (subclass of Timer) as a countdown timer, to "kick people out" of a form after a certain duration.
I would like to display the progress of that timer, and I like the idea of a circular progress slowly filling up.
I'm not showing my code because I don't really have anything to show. I have a completely static progress indicator and a working timer, in a widget (stateful or stateless, whichever works best).
I face two issues and this is where I need help for :
I don't know how to check every x milliseconds for the timer progress. How can I do that? I don't need copy-pasta code, but more of "what object / which direction" should I go for?
The timer progress in ticks is not implemented (NotImplementedException) ; is there any way to have an equivalent somewhere else? That object works really well for me, except for that part.
Am I SOL or is there a way to make it?
There's nothing to be implemented in the getter tick, since RestartableTimer is not periodic. What you want is a much more complex thing, and RestartableTimer is not able to help you with that.
First, you need something to control the progress of the CircularProgressIndicator:
class ProgressController {
static const double smoothnessConstant = 250;
final Duration duration;
final Duration tickPeriod;
Timer _timer;
Timer _periodicTimer;
Stream<void> get progressStream => _progressController.stream;
StreamController<void> _progressController = StreamController<void>.broadcast();
Stream<void> get timeoutStream => _timeoutController.stream;
StreamController<void> _timeoutController = StreamController<void>.broadcast();
double get progress => _progress;
double _progress = 0;
ProgressController({#required this.duration})
: assert(duration != null),
tickPeriod = _calculateTickPeriod(duration);
void start() {
_timer = Timer(duration, () {
_cancelTimers();
_setProgressAndNotify(1);
_timeoutController.add(null);
});
_periodicTimer = Timer.periodic(
tickPeriod,
(Timer timer) {
double progress = _calculateProgress(timer);
_setProgressAndNotify(progress);
},
);
}
void restart() {
_cancelTimers();
start();
}
Future<void> dispose() async {
await _cancelStreams();
_cancelTimers();
}
double _calculateProgress(Timer timer) {
double progress = timer.tick / smoothnessConstant;
if (progress > 1) return 1;
if (progress < 0) return 0;
return progress;
}
void _setProgressAndNotify(double value) {
_progress = value;
_progressController.add(null);
}
Future<void> _cancelStreams() async {
if (!_progressController.isClosed) await _progressController.close();
if (!_timeoutController.isClosed) await _timeoutController.close();
}
void _cancelTimers() {
if (_timer?.isActive == true) _timer.cancel();
if (_periodicTimer?.isActive == true) _periodicTimer.cancel();
}
static Duration _calculateTickPeriod(Duration duration) {
double tickPeriodMs = duration.inMilliseconds / smoothnessConstant;
return Duration(milliseconds: tickPeriodMs.toInt());
}
}
Then you can implement a CircularProgressIndicator that listens to the Streams from ProgressController:
class RestartableCircularProgressIndicator extends StatefulWidget {
final ProgressController controller;
final VoidCallback onTimeout;
RestartableCircularProgressIndicator({
Key key,
#required this.controller,
this.onTimeout,
}) : assert(controller != null),
super(key: key);
#override
_RestartableCircularProgressIndicatorState createState() =>
_RestartableCircularProgressIndicatorState();
}
class _RestartableCircularProgressIndicatorState
extends State<RestartableCircularProgressIndicator> {
ProgressController get controller => widget.controller;
VoidCallback get onTimeout => widget.onTimeout;
#override
void initState() {
super.initState();
controller.progressStream.listen((_) => updateState());
controller.timeoutStream.listen((_) => onTimeout());
}
#override
Widget build(BuildContext context) {
return CircularProgressIndicator(
value: controller.progress,
);
}
void updateState() => setState(() {});
}
You can also pass some of the paramers of CircularProgressIndicator to RestartableCircularProgressIndicator, so you can customize it.
A usage example:
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ProgressController controller;
#override
void initState() {
super.initState();
controller = ProgressController(
duration: Duration(seconds: 5),
);
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RestartableCircularProgressIndicator(
controller: controller,
onTimeout: () => print('timeout'),
),
RaisedButton(
onPressed: controller.start,
child: Text('Start'),
),
RaisedButton(
onPressed: controller.restart,
child: Text('Restart'),
),
],
),
),
),
);
}
}
I'll convert this into a library someday, but until then I cannot provide the tests and documentation to this code, so you have to study it if you want to understand what's going on here (I'm sorry...).

How to make a count down timer in Flutter

I'm looking for the best way to make a countdown timer in Flutter.
I made it this way, but i'm not sure this efficient way to do it, this is my code :
class CountDown extends StatefulWidget {
final int secondsNum;
CountDown({Key key, #required this.secondsNum}) : super(key: key);
#override
_CountDownState createState() => _CountDownState(secondsNum);
}
class _CountDownState extends State<CountDown> {
Timer _timer;
int _start;
_CountDownState(int start) {
this._start = start;
startTimer();
}
void startTimer() {
const oneSec = const Duration(seconds: 1);
_timer = new Timer.periodic(
oneSec,
(Timer timer) => setState(() {
if (_start < 1) {
timer.cancel();
} else {
_start = _start - 1;
}
})
);
}
#override
Widget build(BuildContext context) {
return Text("$_start");
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}
}
And then import that file and call it this way: CountDown(secondsNum: 50).
The problem is that I'm going to call CountDown(secondsNum: 50) like 10 times in the same screen.
There are a lot of Javascript libraries that can do the same thing for Web, but I didn't find any for Flutter so far. If anyone has an idea about the best way to do this, please post it below. Thanks in advance.