I have a question, how do I completely remove the button from the screen? I know that it is possible to disable it using Null, but this does not suit me, because it will still be displayed, albeit in the off state. I would like the button to be completely removed after a few clicks on it, how can I do this?
ElevatedButton(
onPressed: () {
setState(() {
_clickBloc.add(ClickUpdate());
});
},
),
You can wrap ElevatedButton with Visible Widget. And make Visible widget property to false after few clicks.
bool visibleVar = true;
Visibility(
child: ElevatedButton(
onPressed: () {
setState(() {
_clickBloc.add(ClickUpdate());
visibleVar();
});
},
),
visible: visibleVar,
),
void changeVisibility(){
visibleVar = ! visibleVar;
}
You can also use conditional if with a bool like bool showThisWidget = true
if (showThisWidget) ElevatedButton(....)
If it is on child , most child accept null.
child: showThisWidget? ElevatedButton(....) :null
You can use the Visibility Widget to remove the button from the screen like:
bool visible = true;
void makeItUnvisible() {
setState(() {
visible = false;
});
}
Visibility(
visible: visible,
child: ElevatedButton(
onPressed: makeItUnvisible,
child: const Text('Button Text'),
),
),
Related
I was trying to make a form that shows one field at the time that acomplishes the next things:
When pressing a "next" button, it validates the field and let the user go to the next field if everything is ok.
When the user presses the next button in the keyboard it should do the same.
If possible, a sliding animation between fields.
What i came up with was something like this:
List<Widget> questions = [List of textFieldForm]
int pageCount = 0;
GlobalKey _formKey = GlobalKey<FormState>();
pickSection(pageCount) {
return questions[pageCount];
}
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
pickSection(widget.pageCount),
Container(
child: Row(children: [
Container(
width: screenWidth * 0.4,
child: TextButton(
onPressed: widget.pageCount == 0
? null
: () {
setState(() {
widget.pageCount--;
}),
child: Text("BACK"),
),
),
Container(
width: screenWidth * 0.4,
child: TextButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
if (widget.pageCount == questions.length - 1) {
print("FORM ENDS");
} else {
setState(() {
widget.pageCount++;
});
}
}
},
child: Text("NEXT"),
),
),
])
),
]),
),
}
This way i validate every field when it's index is called since i treat every widget as it's own form, but i couldn't think of a way to implement an animation, or make the field advance using the next button on the keyboard(again, because this treats every field as it's own form).
Please help me figure out how to implement this. Animation is not important but using the next button to advance fields is crucial.
Thanks in advance.
You can achieve this using Visibility widget(which is used to show a widget on condition) and maintaining flags.
bool goToNextField = false;
TextButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
goToNextField = true;
if (widget.pageCount == questions.length - 1) {
print("FORM ENDS");
} else {
setState(() {
widget.pageCount++;
});
}
}
},
And in the next widget use a Visibility widget
Widget nextField () {
return Visibility(
visible: goToNextField,
child :
// what you want to show when next button is pressed
);
}
im trying to submit form on Dialog and i have a DateTimePicker button and need to make a validation on it also before submitting , what i want to do is showing a text error in case no date picked by changing my own variable "isValid" to false but the UI is not updating , i have to close the dialog and reopen it to see the error text even though i wrapped my column with a StatefulBuilder
my dialog photo here
here is my code
StatefulBuilder(builder: (context, StateSetter setState) {
return isValid == false
? Column(
children: [
ElevatedButton(
onPressed: () {
DateTimePicker(context)
.then((value) => setState(() {
_appointmentDateTime = value;
}));
},
child: Text(getTimeDate())),
Text(
'error',
style: TextStyle(
color: Colors.red, fontSize: 10),
),
],
)
: Column(
children: [
ElevatedButton(
onPressed: () {
DateTimePicker(context)
.then((value) => setState(() {
_appointmentDateTime = value;
}));
},
child: Text(getTimeDate())),
],
);
})
Validating form + toggling the isValid Value is working fine
OutlinedButton(
onPressed: () async {
if (_formKey.currentState.validate() &&
_appointmentDateTime != null) {
String date = DateFormat('yyyy-MM-dd hh:mm')
.format(_appointmentDateTime);
var appointment = Appointment(
patientName: widget.patient.name,
date: date,
hospital: _hospitalController.text,
await FirebaseApi.addPatientAppointment(
widget.patient.id, appointment);
print('Appointment Created ');
_formKey.currentState.reset();
setState(() {
translator = null;
_appointmentDateTime = null;
});
Navigator.pop(context);
}
else {
setState(() {
isValid = !isValid;
});
}
},
child: Text('Add Appointment')),
It can get confusing when writing the code like this when dealing with Dialogs. The setState you are using in the OutlinedButton is not the same as the setState used in the StatefulBuilder. You need to enclose your OutlinedButton inside the StatefulBuilder too. If you ever use a StatefulBuilder inside a stateful widget, it is better to use a different name like e.g setDialogState.
It is even better to create a separate stateful widget class just for your Dialog contents and pass the formKey and anything else than using a StatefulBuilder in this case to avoid confusion.
I have an ExpansionTile that checks if the user is allowed to enable it:
ExpansionTile(
tilePadding: widget.rent ? EdgeInsets.all(0) : null,
onExpansionChanged: (bool value) {
if (_canCreateBill) {
setState(() {
_billable = value;
});
} else {
return Navigator.of(context)
.pushNamed('/invoice-details')
.then((val) async {
await _getBillingInfo();
setState(() {
_billable =
_canCreateBill;
});
});
}
widget.onSwitchChange(value);
},
initiallyExpanded: _billable,
title: Text(translate('expense.create_invoice')),
subtitle: Text(translate('expense.create_invoice_info')),
trailing: IgnorePointer(
child: Switch(
value: _billable,
onChanged: (_) {},
),
),
children: [
Visibility(
visible: _canCreateBill,
child: _billingInfo(),
),
],
);
Happy path:
Here we have _canCreateBill which controls if the user can enable the switch. If the user clicks and he's not allowed, I moved him to a page to add some info. After adding that info, he comes back and the switch is enable.
Problem:
The user can go back without adding any info. The switch will be disable but the ExpansionTile will be active because we already clicked on it:
How can check for this also? Can we disable the ExpansionTile or force a click somehow?
This option is not a solution:
IgnorePointer(
ignoring: true,
child: ExpansionTile()
)
My case is I have a widget with TextField used for search. When I type something in TextField the cross icon become visible in suffixIcon (to clear). I do not do search and just click the cross icon to clear the entered input but onSubmitted is called and search executed!!! But I don't need it! I do not submit the text input, I cancel it!!
final searchClear = ValueNotifier(false);
final searchController = TextEditingController();
// in initState method:
searchController.addListener(() {
searchClear.value = searchController.text.isNotEmpty;
});
// in build method:
TextField(
...
controller: searchController,
suffixIcon: ValueListenableBuilder<bool>(
valueListenable: searchClear,
builder: (_,visible,child) {
return Visibility(
visible: visible,
child:child,
);
},
child: InkWell(
child: Icon(Icons.close),
onTap: () {
searchController.clear();
searchFocus.unfocus();
}
),
),
onSubmitted: (value) {
if(value.isEmpty) {
FocusScope.of(context).requestFocus(searchFocus);
} else {
widget.search(value);
}
}
),
P.S. Any ideas to work around this?
This is my code:
showArrow? IconButton(icon: Icon(FontAwesomeIcons.arrowCircleRight,color: Colors.white,), onPressed: (){
setState(() {
showArrow = !showArrow;
});
}):IconButton(icon: Icon(Icons.arrow_right),onPressed: (){},),
At the very beginning showArrow value is true, so when I press the icon it updates the icon but I want it to update icon after 3 second and meanwhile show a loading gif at the same place in some other widget(like AssetImage). So is it possible to do so?
Before updating the Icon I want it to show loading gif at the same place for 3 seconds.
FIRST SOLUTION:
It's better if you have a state management for you widget. So first you will show the IconButton (first icon) and then when click on it you can set a timer for three seconds or wait for a future value and replace the IconButton (first icon) with CircularProgressIndicator.
When the timer has finished replace back the CircularProgressIndicator with IconButton (second icon)
SECOND SOLUTION:
var showArrow = false;
var showLoading = false;
Widget myIconButton() {
if (!showLoading)
return showArrow
? IconButton(
icon: Icon(Icons.clear, color: Colors.white),
onPressed: () {
setState(() {
showArrow = !showArrow;
showLoading = !showLoading;
});
Future.delayed(Duration(seconds: 3), () {
if (mounted)
setState(() {
showLoading = !showLoading;
});
});
})
: IconButton(
icon: Icon(Icons.arrow_right),
onPressed: () {},
);
else
return SizedBox(
width: 20, height: 20, child: CircularProgressIndicator());
}
Try this in the on pressed:
setState(() {
showArrow = false;
// Anything else you want
});
Future.delayed(Duration(seconds: 3)).then((_) {
setState(() {
showArrow = true; //goes back to arrow Icon
// Anything else you want
});
And in the widget itself something like:
showArrow ? ArrowIcon() : LoadingIcon();
This way once the arrow is pressed the state is false and after 3 seconds true again