How to activate / deactivate floating button with timer in flutter? - flutter

I need to show floating button for users and i want them to use it only one time per hour in flutter. So how can i do it? can anyone guide me to add countdown or timer method for floating button?
This is my floating button code
Widget _floating(BuildContext context,bool isVerified){
if (isVerified)
return FloatingActionButton(
//TODO: customise according to your needs
onPressed:() async{
},
tooltip: 'Increment',
child: Icon(Icons.add),
);
else
return Container();
}
}

Try this,
Set Defaults,
bool _buttonVisibility = false;
DateTime lastClicked;
FAB,
if (lastClicked == null) {
setState(() => _buttonVisibility = true);
}
floatingActionButton: Visibility(
visible: _buttonVisibility,
child: FloatingActionButton(
child: Icon(Icons.plus_one),
onPressed: () {
setState(() {
lastClicked = DateTime.now();
_buttonVisibility = false;
// change this seconds with `hours:1`
new Timer(Duration(seconds: 5),
() => setState(() => _buttonVisibility = true));
});
},
),
),

You can try to use the widget AnimatedPositioned Widget with Timer() to show and hide the button with something like:
import
import 'dart:async';
then in the StatefulWidget:
// Inside your StatefulWidget
[...]
Timer t;
double position = -300.0; // or any of your value
#override
void initState() {
super.initState();
t = Timer(const Duration(hours: 1), () {
_position = 0;
}
}
[...]
Widget myAnimatedFloatingActionButton(){
AnimatedPositioned(
duration: const Duration(milliseconds: 300),
curve: Curves.ease,
left: _position,
child: FloatingActionButton(),
);
}
[...]
And in your onPressed() reset the Timer.
Of course, adapt the _position and Timer initial value to your needs

Related

How to play lottie animation half?

Help me!
I want mute and unmute button with Lottie animation. But this mute.json json animation have both.
So I need one click play Lottie half animation like this.
When clicked and
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this)
..value = 0.5
..addListener(() {
setState(() {
// Rebuild the widget at each frame to update the "progress" label.
});
});
}
Column(
children[
Lottie.asset(
controller: _controller,
'assets/mute.json',
animate: true,
onLoaded: (composition) {
setState(() {
_controller.duration = composition.duration;
});
},
),
],
),
bool mute = false;
#override
void initState() {
super.initState();
// add duration
_controller = AnimationController(vsync: this, duration: Duration(milliseconds: 300));
}
use animateTo method on controller.
InkWell(
onTap: () {
mute = !mute;
log(mute.toString());
if (mute) {
_controller.animateTo(0.5);
} else {
_controller.animateTo(0);
}
},
child: LottieBuilder.network(
"https://maxst.icons8.com/vue-static/landings/animated-icons/icons/no-sound/no-sound.json",
controller: _controller,
height: 200,
),
),

how to add loader when on the toggle button

I want to display a circular loader when user is going to on the toggle button, then after few secs toggle button will active.
here is my code
InkWell(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ProductProfile(),
));
},
child: Container(
decoration: BoxDecoration(
color: _selectedProducts.contains(book.id) ? Colors.grey[200] :Colors.white,
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
child: ListTile(
dense: true,
trailing: Switch(
value: _selectedProducts.contains(book.id),
onChanged: (bool? selected) {
if (selected != null) {
setState(() {
_onProductSelected(selected, book.id);
});
}
},
activeTrackColor: HexColor("#b8c2cc"),
activeColor: HexColor("#7367f0"),
),
title: Text(
book.title,),
Divider()
),
),
),
SizedBox10(),
],
);
please help how to do this
To achieve that, you need bool _isLoading and a timer. Steps I would do:
Declare _isLoading: bool _isLoading = false;
Change _isLoading value using a timer:
void timer() {
int _time = 10;
Timer timer = new Timer.periodic(
Duration(seconds: 1),
(Timer timer) async {
if (_time == 0) {
_isLoading = true;
timer.cancel();
} else {
setState(() {
_time--;
});
}
},
);
}
Use _isLoading on your build method (for example):
#override
Widget build(BuildContext context) {
return _isLoading ? CircularProgressIndicator() : Container();
}
Or to hide your button:
#override
Widget build(BuildContext context) {
return _isLoading ? Container() : YourToggleButton;
}
Also remember to dispose your timer!
#override
void dispose() {
timer.cancel();
}
So, If you are on Flutter web, there is a widget called MouseRegion which has onHover, onEnter & onExit.
You can assign a new bool for instance bool showLoader=false which you will toggle to true with setState (inside the onHover, where you could also start the Timer and when finished reset the showLoader to false).
Now you can show your button with a ternary operator : showLoader ? CircularProgressIndicator() : YourButton()

How to set the duration of onLongPress

I know onLongPress would trigger after a certain period of time (like 500 ms or so). But what I want to do is to trigger some action when user presses the button for like 3 seconds. Actually I want to set the duration for onLongPress.
ElevatedButton(
onPressed: () => print('ok I\'m just fine'),
onLongPress: () => print('Trigger me when user presses me for like 3 seconds'),
style: ElevatedButton.styleFrom(
primary: Colors.red,
elevation: 4,
),
How I did it:
onLongPress: () {
Timer(Duration(milliseconds: (longPressIncrementDuration > 500) ? longPressIncrementDuration - 500 : 0), //enter function here//);
// I subtract 500 ms from required time limit as longpress on flutter activates after 500ms
},
You can solve your problem this way, use onPanCancel and onPanDown of GestureDetector with timer.
class _MyHomePageState extends State<MyHomePage> {
Timer _timer;
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: GestureDetector(
onPanCancel: () => _timer?.cancel(),
onPanDown: (_) => {
_timer = Timer(Duration(seconds: 3), () { // time duration
// your function here
})
},
),
);
}
}
let me know if it work for you.
I made a package today where you can set the duration on GestureDetector. if you wan you can try it out https://pub.dev/packages/custom_long_tap
GestureDetector(
onTapDown: (_) { //Detect when you click the element
_timer = Timer(
const Duration(seconds: 5),
() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ListOrder(),
),
);
},
);
print('tapping');
},
onTapUp: (_) { // Detect and cancel when you lift the click
_timer!.cancel();
print('cancel');
},
child: const Icon(Icons.person_search),
),

How to Check if Timer is Active Before Creating a New One

I came across this code for a timer on another thread. When you press the RaisedButton multiple times concurrently it adds a -1 second for every click thus increasing the rate of decrease.
Any ideas for the easiest way to check if the timer is already active and if it is to not let the RaisedButton create a new one. Thanks!
import 'dart:async';
[...]
Timer _timer;
int _start = 10;
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
void dispose() {
_timer.cancel();
super.dispose();
}
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(title: Text("Timer test")),
body: Column(
children: <Widget>[
RaisedButton(
onPressed: () {
startTimer();
},
child: Text("start"),
),
Text("$_start")
],
),
);
}
Add a check to see if _timer is already active using the isActive property of Timers. If it's already active, it won't create a new one.
Example:
void startTimer() {
const oneSec = const Duration(seconds: 1);
if(!_timer?.isActive) {
_timer = new Timer.periodic(
oneSec,
(Timer timer) => setState(
() {
if (_start < 1) {
timer.cancel();
} else {
_start = _start - 1;
}
},
),
);
}
}
The receiver can't be null, so the null-aware operator '?.' is unnecessary.
Change this:
if(!_timer?.isActive)
to
if(!_timer.isActive)

Generating widgets in Pageview based on conditional logic

Currently facing a problem with conditional logic and PageView widget. Let's say that the PageView will dynamically generate 3 pages. In those 3 pages different widgets will be generated. One of the widgets is a button (called "Next"), which is a PageController, but that widget has to be replaced by a button widget that is supposed to submit (called "Submit") the whole form (the PageView is wrapped in a form).
It seems obvious, just write conditional logic that compares the current page of the PageView to the length of the PageView (PageView is populated with a List, so it is easy to get the length). Then switch the widgets when the right conditions meet: when current page equals to 3, change the widget. Unfortunately, the PageView renders the "Next" button on every page. So only when I get to the last page and then click "Next" again will it change to "Submit". It is supposed to be "Submit", when the user gets on the last page.
const int TRIVIA_STARTING_TIME = 10;
class TriviaOneForm extends StatefulWidget {
final UserRepository _userRepository;
TriviaOneForm({Key key, #required UserRepository userRepository})
: assert(userRepository != null),
_userRepository = userRepository,
super(key: key);
State<TriviaOneForm> createState() => _TriviaOneFormState();
}
class _TriviaOneFormState extends State<TriviaOneForm> {
final TextEditingController _answerController = TextEditingController();
UserRepository get _userRepository => widget._userRepository;
TriviaOneBloc _triviaOneBloc;
PageController _pageController;
Timer _timer;
bool _isLoadingScreen;
bool _isNextOrSubmitButton;
int _start;
int _indexOfCarouselItem;
List<int> _selectedValList;
List _triviaDataList;
#override
void initState() {
super.initState();
_isLoadingScreen = true;
_getTriviaData();
_pageController = PageController();
_indexOfCarouselItem = 0;
_isNextOrSubmitButton = true;
_selectedValList = [0, 0, 0, 0, 0];
_triviaDataList = [];
_start = TRIVIA_STARTING_TIME;
_triviaOneBloc = BlocProvider.of<TriviaOneBloc>(context);
_answerController.addListener(_onAnswerChanged);
}
#override
void dispose() {
if (_timer != null) {
_timer.cancel();
}
_pageController.dispose();
super.dispose();
}
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 BlocListener<TriviaOneBloc, TriviaOneState>(
listener: (context, state) {
if (state.isFailure) {
Scaffold.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Submition Failure'),
Icon(Icons.error)
],
),
backgroundColor: Colors.red,
),
);
}
if (state.isSubmitting) {
Scaffold.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Submitting Answers...'),
],
),
),
);
}
if (state.isSuccess) {
BlocProvider.of<TriviaOneBloc>(context).add(Submitted());
}
},
child: BlocBuilder<TriviaOneBloc, TriviaOneState>(
builder: (context, state) {
return _isLoadingScreen
? _displayLoadScreen()
: Padding(
padding: EdgeInsets.all(20.0),
child: Form(
child: PageView(
physics: NeverScrollableScrollPhysics(),
controller: _pageController,
reverse: false,
scrollDirection: Axis.horizontal,
children: _triviaDataList.map<Widget>((triviaData) {
return ListView(
shrinkWrap: true,
children: <Widget>[
Text(triviaData.getQuestion),
ListView(
shrinkWrap: true,
children: triviaData.getAnswers
.map<Widget>((triviaAnswer) {
int index =
triviaData.getAnswers.indexOf(triviaAnswer);
return ListTile(
title: Text(triviaAnswer.getAnswer),
leading: Radio(
value: index,
groupValue:
_selectedValList[_indexOfCarouselItem],
onChanged: (int value) {
setState(() {
print(value);
_selectedValList[_indexOfCarouselItem] =
value;
});
},
),
);
}).toList(),
),
_isNextOrSubmitButton ? _nextButton() : _submitButton(),
RaisedButton(
onPressed: () {
startTimer();
},
child: Text('Start'),
),
Text('$_start'),
],
);
}).toList(),
),
),
);
},
),
);
}
Widget _triviaControlButton(PageController pageController) {
if (0 < _triviaDataList.length) {
return RaisedButton(
child: Text('Next'),
onPressed: () {
pageController.nextPage(
duration: Duration(seconds: 1), curve: Curves.easeInOut);
print('Next');
},
);
} else if (pageController.page.toInt() == _triviaDataList.length) {
return RaisedButton(
child: Text('Submit'),
onPressed: () {
print('Submit');
},
);
} else {
return RaisedButton(
child: Text('Error'),
onPressed: () {
print('Error');
},
);
}
}
Widget _displayLoadScreen() {
return Container(
alignment: Alignment(0.0, 0.0),
child: CircularProgressIndicator(),
);
}
void _onAnswerChanged() {
_triviaOneBloc.add(AnswerChanged(answer: _answerController.text));
}
void _getTriviaData() async {
var data = _userRepository.retrieveTriviaData();
// Await trivia data to be retrieved from firebase
await data.getDocuments().then((collection) {
collection.documents.forEach((document) {
TriviaData triviaData = TriviaData();
List<TriviaAnswer> triviaAnswerList = List<TriviaAnswer>();
// Iterate through all of the answers for a question
// Create a list of TriviaAnswer objects to hold key and value
document.data['answers'].forEach((key, value) {
TriviaAnswer triviaAnswer = TriviaAnswer();
triviaAnswer.setAnswer = key;
triviaAnswer.setAnswerValue = value;
triviaAnswerList.add(triviaAnswer);
});
// Assign question String and answer List to TriviaData
// Add all data to data list
triviaData.setAnswers = triviaAnswerList;
triviaData.setQuestion = document.data['question'];
_triviaDataList.add(triviaData);
});
});
setState(() {
_isLoadingScreen = false;
});
}
Widget _nextButton() {
return RaisedButton(
child: Text('Next'),
onPressed: () {
if (_indexOfCarouselItem < _triviaDataList.length) {
_pageController.nextPage(
duration: const Duration(milliseconds: 100),
curve: Curves.easeInOut);
setState(() {
_start = TRIVIA_STARTING_TIME;
_indexOfCarouselItem += 1;
});
}
if (_indexOfCarouselItem == _triviaDataList.length) {
Future.delayed(const Duration(seconds: 0), () {
setState(() {
_isNextOrSubmitButton = false;
});
});
}
try {
if (_timer != null || !_timer.isActive) {
startTimer();
}
} catch (_) {
print('Error: Timer is already disabled');
}
},
);
}
Widget _submitButton() {
return RaisedButton(
child: Text('Submit'),
onPressed: () {
print(_selectedValList);
_userRepository.storeTriviaToFirebase();
setState(() {
if (_timer != null || _timer.isActive) {
_timer.cancel();
}
});
},
);
}
}
EDIT 1:
This is the updated code I use for the button to populate in the PageView. I am setting a String to initial value "Next" then updating it when _indexOfCarouselItem + 2 == _triviaDataList.length is true. The updated value will be "Submit", when the condition is met.
Widget _triviaControlButton() {
return RaisedButton(
child: Text(buttonText),
onPressed: () {
_pageController.nextPage(
duration: const Duration(milliseconds: 100),
curve: Curves.easeInOut);
if (_indexOfCarouselItem + 2 == _triviaDataList.length) {
setState(() {
buttonText = "Submit";
});
}
if (_indexOfCarouselItem < _triviaDataList.length) {
setState(() {
_start = TRIVIA_STARTING_TIME;
_indexOfCarouselItem += 1;
});
}
print(_indexOfCarouselItem);
print(_triviaDataList.length);
},
);
}
I’m on phone now so I can’t guarantee the code I’ll post is ok, but you’ll get the idea.
First: I don’t think you need 2 buttons if they are equals on size etc. so you can implement something like this:
child: Text( _indexOfCarouselItem += 1 != _triviaDataList.length
? 'Next' : 'Submit')
And then use the same logic in the onPressed:
onPressed() {
_indexOfCarouselItem += 1 != _triviaDataList.length ? doSomethibg : doSomethingDifferent;
}
Edit:
Ok if I understand correctly the problem right now is that because of the transition the button says "Submit" but there are no question yet? If this is the case you can like you said, add delay, but I think a better approach will be wire the text of the button with the question. I mean you can keep the actual logic (because it works) and add something like this:
child: Text( _indexOfCarouselItem += 1 != _triviaDataList.length && questionText != ""
? 'Next' : 'Submit')
This logic can be applied in if ... else ... block too.
Edit 2: try this one:
Widget _triviaControlButton() {
return RaisedButton(
child: Text(buttonText),
onPressed: () {
_pageController.nextPage(
duration: const Duration(milliseconds: 100),
curve: Curves.easeInOut);
if (_indexOfCarouselItem < _triviaDataList.length) {
setState(() {
_start = TRIVIA_STARTING_TIME;
_indexOfCarouselItem += 1;
});
if (_indexOfCarouselItem == _triviaDataList.length) {
setState(() {
buttonText = "Submit";
});
}
},
);
}