I just got back to Flutter, and wanted to test the countdown for a simple practice app.
I can scroll the countdown second by second but I haven't figured out how to do it with the milliseconds.
the current code:
import 'package:quiver/async.dart';
int _start = 10;
int _current = 10;
void startTimer() {
CountdownTimer countDownTimer = CountdownTimer(
Duration(seconds: _start),
const Duration(seconds: 1),
);
var sub = countDownTimer.listen(null);
sub.onData((duration) {
setState(() {
int _current = _start - duration.elapsed.inSeconds;
dureedefaut = _current;
});
});
}
I updated on my page the variable : dureedefaut
Thank you for your help
import 'package:quiver/async.dart';
int _start = 10;
int _current = 10;
void startTimer() {
CountdownTimer countDownTimer = CountdownTimer(
Duration(milliseconds: _start),
const Duration(milliseconds: 1),
);
var sub = countDownTimer.listen(null);
sub.onData((duration) {
setState(() {
int _current = _start - duration.elapsed.inSeconds;
dureedefaut = _current;
});
});
}
Simply use this :
Duration(milliseconds: )
you can use milisecond , second and etc in Duration Widget
Related
void valuerandomer() {
Future.delayed(Duration(milliseconds: 500), () {
int count = 0;
int max = 1000;
int min = 1;
Random rnd = new Random();
while (count != -1) {
count++;
value += rnd.nextInt(6) + (-5);
}
if (value > (max - 1)) {
value = 999;
} else if (value < 0) {
value = 0;
}
print(value);
});
}
I want the function to print every 500 miliseconds in the text widget so the value parameter starts with the value of 75 and changes every 500 milliseconds with this function.
How do I do that?
How do I declare this function in the text widget like Text('$valuerandomer')? cuz its just dont work. I tried just to type there $value but still doesnt work
For every x time, try using Timer.periodic
Timer? _timer;
String text = "initText";
#override
void dispose() {
_timer?.cancel();
super.dispose();
}
void valuerandomer() {
_timer = Timer.periodic(
Duration(milliseconds: 500),
(t) {
//perform your work
text = "newText ";
setState(() {});
},
);
}
Use funtion
Timer.periodic(Duration(/..),(timer){
//Put your logic
setState((){});
})
I am a beginner to flutter. I successfully created a stopwatch but once I updated flutter it stopped working entirely. It won't start or stop anymore. When running the code the stopwatch does not display the count up of numbers while still running fine behind the scenes. Does any know how to fix this, is my code now invalid, or do I need to add sometime new to make it work again? I will paste my code below, the first is the button to start the stopwatch and the second is my actual stopwatch code please let me know if you can help or need any more information.
child: GridView.count(
primary: false,
padding: const EdgeInsets.all(20),
crossAxisSpacing: 10,
mainAxisSpacing: 10,
crossAxisCount: 3,
children: widget.tasks.map((element) {
final isActive = activeTask != null && activeTask == element;
return GestureDetector(
onTap: () {
// set active task or toggle is active
if (isActive) {
setState(() {
activeTask = null;
StudyViewModel.stopwatch.start();
disable = true;
});
} else {
setState(() {
activeTask = element;
StudyViewModel.stopwatch.stop();
disable = false;
});
}
},
child: Container(
color: isActive ? Colors.amber : Colors.green,
padding: EdgeInsets.all(8),
child: Center(
child: Text(
element.name,
style: TextStyle(color: Colors.white, fontSize: 25),
textAlign: TextAlign.center,
),
),
),
);
}).toList(),
study_viewmodel.dart
import 'dart:convert';
import 'dart:async' show Future;
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:timestudyapp/models/elapsed_time.dart';
import 'package:timestudyapp/models/study.dart';
import 'package:path_provider/path_provider.dart';
class StudyViewModel {
static List<Study> studies = [];
static List<ValueChanged<ElapsedTime>> timerListeners =
<ValueChanged<ElapsedTime>>[];
static Stopwatch stopwatch = new Stopwatch();
/// load from file...
static Future load() async {
try {
File file = await getFile();
String studiesJson = await file.readAsString();
if (studiesJson.isNotEmpty) {
List studiesParsed = json.decode(studiesJson);
studies = studiesParsed.map((i) => Study.fromJson(i)).toList();
}
} catch (e) {
print(e);
}
}
static Future<File> getFile() async {
final directory = await getApplicationDocumentsDirectory();
final path = directory.path;
return File('$path/studies.json');
}
static Future saveFile() async {
File file = await getFile();
file.writeAsString(json.encode(studies));
}
static bool checkName(String name) {
bool match = false;
for (int i = 0; i < studies.length; i++) {
if (studies[i].name == name) {
match = true;
break;
}
}
return match;
}
static String milliToElapsedString(int milliseconds) {
final int hundreds = (milliseconds / 10).truncate();
final int seconds = (hundreds / 100).truncate();
final int minutes = (seconds / 60).truncate();
String hundredsStr = (hundreds % 100).toString().padLeft(2, '0');
String minutesStr = (minutes % 60).toString().padLeft(2, '0');
String secondsStr = (seconds % 60).toString().padLeft(2, '0');
return minutesStr + ':' + secondsStr + ':' + hundredsStr;
}
}
timer_text.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:timestudyapp/models/elapsed_time.dart';
import 'package:timestudyapp/viewmodels/study_viewmodel.dart';
import 'package:timestudyapp/widgets/hundreds.dart';
import 'package:timestudyapp/widgets/minutes_seconds.dart';
class TimerText extends StatefulWidget {
final double fontSize;
TimerText({required this.fontSize});
TimerTextState createState() => new TimerTextState();
}
class TimerTextState extends State<TimerText> {
late Timer timer;
late int milliseconds;
#override
void initState() {
timer = new Timer.periodic(Duration(milliseconds: 30), callback);
super.initState();
}
#override
void dispose() {
timer.cancel();
super.dispose();
}
void callback(Timer timer) {
if (milliseconds != StudyViewModel.stopwatch.elapsedMilliseconds) {
milliseconds = StudyViewModel.stopwatch.elapsedMilliseconds;
final int hundreds = (milliseconds / 10).truncate();
final int seconds = (hundreds / 100).truncate();
final int minutes = (seconds / 60).truncate();
final ElapsedTime elapsedTime = new ElapsedTime(
hundreds: hundreds,
seconds: seconds,
minutes: minutes,
);
for (final listener in StudyViewModel.timerListeners) {
listener(elapsedTime);
}
}
}
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
MinutesAndSeconds(fontSize: widget.fontSize),
Hundreds(fontSize: widget.fontSize),
],
);
}
}
timer_text.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:timestudyapp/models/elapsed_time.dart';
import 'package:timestudyapp/viewmodels/study_viewmodel.dart';
import 'package:timestudyapp/widgets/hundreds.dart';
import 'package:timestudyapp/widgets/minutes_seconds.dart';
class TimerText extends StatefulWidget {
final double fontSize;
TimerText({required this.fontSize});
TimerTextState createState() => new TimerTextState();
}
class TimerTextState extends State<TimerText> {
late Timer timer;
late int milliseconds;
#override
void initState() {
timer = new Timer.periodic(Duration(milliseconds: 30), callback);
super.initState();
}
#override
void dispose() {
timer.cancel();
super.dispose();
}
void callback(Timer timer) {
if (milliseconds != StudyViewModel.stopwatch.elapsedMilliseconds) {
milliseconds = StudyViewModel.stopwatch.elapsedMilliseconds;
final int hundreds = (milliseconds / 10).truncate();
final int seconds = (hundreds / 100).truncate();
final int minutes = (seconds / 60).truncate();
final ElapsedTime elapsedTime = new ElapsedTime(
hundreds: hundreds,
seconds: seconds,
minutes: minutes,
);
for (final listener in StudyViewModel.timerListeners) {
listener(elapsedTime);
}
}
}
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
MinutesAndSeconds(fontSize: widget.fontSize),
Hundreds(fontSize: widget.fontSize),
],
);
}
}
Maybe the problem is in the function onTap, you should change if (isActive) to if (!isActive)
GestureDetector(
onTap: () {
// set active task or toggle is active
if (!isActive) {
setState(() {
activeTask = null;
StudyViewModel.stopwatch.start();
disable = true;
});
} else {
setState(() {
activeTask = element;
StudyViewModel.stopwatch.stop();
disable = false;
});
}
},
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),
),
);
});
I would like to be able to detect a triple tap (or even more) in a Flutter widget, although GestureDetector only has detection for double-tap built in.
What is the easiest way for me to detect a triple tap on a widget?
(I want continually clicking on a part of the screen to unlock some developer options)
Was a bit lazy with this one, in reality it's not that hard
// init
int lastTap = DateTime.now().millisecondsSinceEpoch;
int consecutiveTaps = 0;
GestureDetector(
onTap: () {
int now = DateTime.now().millisecondsSinceEpoch;
if (now - lastTap < 1000) {
print("Consecutive tap");
consecutiveTaps ++;
print("taps = " + consecutiveTaps.toString());
if (consecutiveTaps > 4){
// Do something
}
} else {
consecutiveTaps = 0;
}
lastTap = now;
},
child: ...
)
I tried the method mentioned here, but it didn't work for me. GestureDetector onTap is called only once,
regardless of the number of taps. Probably something has changed in flutter (I'm on the beta channel).
However, I dug into the source code of flutter and come to the solution (https://api.flutter.dev/flutter/gestures/SerialTapGestureRecognizer-class.html):
import "package:flutter/gestures.dart";
RawGestureDetector(gestures: {
SerialTapGestureRecognizer:
GestureRecognizerFactoryWithHandlers<SerialTapGestureRecognizer>(
() =>SerialTapGestureRecognizer(), (SerialTapGestureRecognizer instance) {
instance.onSerialTapDown = (SerialTapDownDetails details) {
if (details.count == 3) print("Consecutive tap 3");
};
})
I took a little different approach. Instead of having to compare timestamps, I set a Timer, which will reset the tapped state. But each time there is a tap, the old timer is canceled.
Timer? devPageClickTimer;
num devPageTapped = 0;
final devPageTapGoal = 5;
GestureDetector(
onTap: () {
devPageTapped++;
if (devPageTapped >= devPageTapGoal) {
router.push(const DeveloperRoute());
}
if (devPageClickTimer != null) {
devPageClickTimer!.cancel();
}
devPageClickTimer = Timer(const Duration(milliseconds: 200), () => devPageTapped = 0);
},
I have tried this method with reduced timeout and with both double and triple tap
int lastTap = DateTime.now().millisecondsSinceEpoch;
int consecutiveTaps = 1;
GestureDetector(
onTap: () {
int now = DateTime.now().millisecondsSinceEpoch;
if (consecutiveTaps == 1) {
print("taps = " + consecutiveTaps.toString());
lastTap = now;
}
if (now - lastTap < 300) {
print("Consecutive tap");
consecutiveTaps++;
print("taps = " + consecutiveTaps.toString());
if (consecutiveTaps == 3) {
print("Consecutive tap 3");
} else if (consecutiveTaps == 2) {
print("Consecutive tap 2");
}
} else {
consecutiveTaps = 1;
}
lastTap = now;
},
child: \\child);
Relevant solution.
Here is flexible reusable multiple tap widget based on Listener widget that reports raw pointer events:
class AppMultipleTap extends StatefulWidget {
final Widget child;
final VoidCallback onMultipleTap;
final int taps;
final Duration duration;
const AppMultipleTap({
super.key,
required this.child,
required this.onMultipleTap,
/// feel free to override these values
this.taps = 3,
this.duration = const Duration(milliseconds: 600),
});
#override
State<AppMultipleTap> createState() => _AppMultipleTapState();
}
class _AppMultipleTapState extends State<AppMultipleTap> {
/// in _count we store current number of taps
int _count = 0;
Timer? _timer;
#override
Widget build(BuildContext context) {
return Listener(
onPointerDown: (_) {
if (_timer == null) _startTimer();
_count++;
},
child: widget.child,
);
}
void _startTimer() {
_timer = Timer(widget.duration, () {
/// you can change this condition to ==, if you need 100% match
if (_count >= widget.taps) {
widget.onMultipleTap.call();
}
_timer = null;
_count = 0;
});
}
}
Then you can use it like that:
#override
Widget build(BuildContext context) {
return AppMultipleTap(
onMultipleTap: /// Do some action
I like this simple approach, without so many nested if blocks.
// Variables in the state class
var startTap = timeNow;
var consecutiveTaps = 0;
static const int serialTaps = 4;
static const int tapDurationInMs = 1000;
static int get timeNow => DateTime.now().millisecondsSinceEpoch;
// Build method
GestureDetector(
onTap: () {
final now = timeNow;
final userExceededTapDuration = now - startTap > tapDurationInMs;
if (userExceededTapDuration) {
consecutiveTaps = 0;
startTap = now;
}
consecutiveTaps++;
if (consecutiveTaps == serialTaps) {
// widget.onTap();
}
},
);
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();
});