I made a counter application, the user can determine the number of their own count. if the count reaches the count limit, I want it vibrate at the last count. For example, if I specify the number 5, the smartphone will vibrate on the 5th tap. However, I have a problem, because the vibration appears on the 6th tap.
How to make it vibrate on the 5th beat?
here is my code:
int _counter = 0;
int _five = 5;
onTap: () async {
setState(() {
if (_counter != _five) {
_counter++;
} else if (_counter == _five) {
Vibration.vibrate(duration: 500);
_counter = _counter + 0;
} else {}
});
},
Your code need a little change like:
onTap: () async {
setState(() {
if (_counter < _five) {
_counter++;
}
if (_counter == _five) {
Vibration.vibrate(duration: 500);
} else {}
});
}
Related
*** UPDATE ***
I have a problem. When I use a Timer in Flutter and cancel() it afterwards, but as soon the cancel method is triggered, nothing happens.
var counter = 0;
Timer.periodic(const Duration(milliseconds: 50), (timer) {
developer.log('Actual counter ' + counter.toString());
counter++;
if (counter == 5) {
developer.log('TIMER DONE');
timer.cancel();
}
});
developer.log('This comes afterwards');
Expected out put:
Actual counter 0
Actual counter 1
Actual counter 2
Actual counter 3
Actual counter 4
TIMER DONE
This comes afterwards
The Output:
Actual counter 0
Actual counter 1
Actual counter 2
Actual counter 3
Actual counter 4
TIMER DONE
normally I should see a message 'this is afterwards' in the console, but it seems that with the cancel the complete method will be canceled.
Many thanks in advance.
Update: perform operation end of timer.
You need to add logic inside if condition to perform operation.
if (counter == firmwareList.length) {
timer.cancel();
developer.log('This comes afterwards'); //here
}
});
Here is an widget example
class TimerF extends StatefulWidget {
const TimerF({super.key});
#override
State<TimerF> createState() => _TimerFState();
}
class _TimerFState extends State<TimerF> {
var counter = 0;
Timer? timer;
bool isTimerActive = true;
#override
void initState() {
super.initState();
timer = Timer.periodic(const Duration(seconds: 1), (timer) {
print('Still timer');
if (counter == 5) {
timer.cancel();
isTimerActive = false;
}
counter++;
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text("current value ${counter}"),
if (timer?.isActive == false) Text("Timer isnt active"),
if (isTimerActive == false) Text("Timer isnt active $isTimerActive"),
],
),
);
}
}
I can't see any increment on counter on current snippet. that's why counter remains 0 and doesnt meet the condition to cancel the timer.
Perhaps it will be
var counter = 0;
Timer.periodic(const Duration(milliseconds: 50), (timer) {
print('Still timer');
if (counter == 5) {
timer.cancel();
}
counter++; //increment the value
print('this is afterwards');
});
Here is a fully working example using a Timer
Timer? timer;
startTimer() {
var counter = 0;
timer = Timer.periodic(const Duration(milliseconds: 50), (timer) {
counter += 1;
print('Actual step $counter');
if(counter==5) {
cancelTimer();
}
});
}
cancelTimer() {
timer?.cancel();
print('this is afterwards');
//do something here
}
When you startTimer(), here is the output
Actual step 1
Actual step 2
Actual step 3
Actual step 4
Actual step 5
this is afterwards
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 building a Flutter Quiz app where the user gets a specific duration of time to answer the question. If the user fails to answer within the given time frame, the next question is retrieved.
The starttimer method acts as a counter and stops at 1.
void starttimer() {
_counter = 10;
if (_timer != null) {
_timer.cancel();
}
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
setState(() {
if (_counter > 0) {
print(_counter);
_counter--;
} else {
_timer.cancel();
}
if (_counter == 1) {
// TODO: nextQuestion();
}
});
});
}
The questions are in a list and a index is used to obtain a specific question within the Text widget ex: Text(questionlist[i]).
The nextQuestion() needs to increment the index by 1.
How can I achieve this? Or is there any other way to retrieve the next question?
Try this logic:
void startTimer() {
_counter = 10;
_timer?.cancel();
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
setState(() {
if (_counter == 0) {
nextQuestion();
timer.cancel();
}
_counter--;
});
});
}
I have a class called Counter,
int value1;
bool isValueValid;
class Counter {
void init(Data data) {
if(data == null) {
value1 = 0;
isValueValid = true;
}
}
}
The method init() of Counter is called in the initState() of my app,
void initState() {
Counter().init(widget.data);
super.initState();
}
The value of value1 or isValueValid gets set to 0 and false respectively on the onTap() of the MaterialButton, however when I navigate to another page and then come back to the same page using Navigator.pop(), the value of isValueValid becomes true again even though it should be false. My method Counter().init is not called anywhere else my app.
MaterialButton(
height: 40,
onPressed: () {
if(value1 >= 1 || isValueValid) {
print('loop entered');
value1 = 0;
isValueValid = false;
setState(() {}
// I need to call `setState` to change the color of a `Container` which is dependent on the bool `isValueValid`
}
});
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();
}
},
);