Flutter: disable ExpansionTile - flutter

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

Related

How to remove ElevatedButton?

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'),
),
),

Listview with radio buttons in flutter slows performance

I would like to update when the value of radio button changes in flutter But its slowing down the app.
I have created the following listview with the radio buttons yes and no
final List<ItemModel> _questions =
List.generate(100, (index) => ItemModel(name: 'nm_$index', id: index + 1, checked: ''));
ListTile(
title: Text((index + 1).toString() + ". " + question.name),
subtitle: Column(
children: [
ListTile(
onTap: () {
setState(() {
_questions[index].checked = 'yes';
});
},
title: const Text("Yes"),
leading: Radio(
value: 'yes',
groupValue: question.checked,
onChanged: (val) {
setState(() {
_questions[index].checked = 'yes';
});
}),
),
ListTile(
title: const Text("No"),
onTap: () {
setState(() {
_questions[index].checked = 'no';
});
},
leading: Radio(
value: 'no',
groupValue: question.checked,
onChanged: (val) {
setState(() {
_questions[index].checked = 'no';
});
}),
)
],
));
The above listview has two radio buttons where each row is either marked as yes or no. It works well for a small list but when the list gets larger like the above with 100 items the app starts to slow down. I believe its caused by the calling of setState on each radio button click which then causes the ui to be rebuilt.
Is there a way i can update the status of the selected radio without causing the ui to rebuild or is there a way i can improve the above to make the app stop lagging and slowing down when there is alot of items.

Flutter TextField calls onSubmitted unexpectedly

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?

How to get Radio to work on flutter when using dynamic object?

I'm new to flutter and I have issues with Radio.
I got it to work perfectly when using the "regular" method:
int _groupValue=-1;
...
...
child: Align(
alignment: Alignment.center,
child: Column(
children: <Widget>[
RadioListTile(activeColor: Colors.black,
groupValue: _groupValue,
value: 1,
onChanged: (value) { setState(() {
_groupValue=value;
}); },
title: Text("a"),
),
RadioListTile(activeColor: Colors.black,
groupValue: _groupValue,
value: 2,
onChanged: (value) { setState(() {
_groupValue=value;
}); },
title: Text("b"),
),
],
),
),
Since I'm using data from API to create the radio buttons I've changed this code to this:
child: Align(
alignment: Alignment.center,
child: children: radioListView
),
),
and on click of a button i call async method to download the data from the API and like this :
void getApiData() async {
...
...
...
setState() {
var radioListView = List<RadioListTile>();
for (Map<String, dynamic> c in apidata)) {
radioListView.add(new RadioListTile(
groupValue: _groupValue,
value: c['option_value'],
title: c['title'],
onChanged: (value) { setState(() {
_groupValue=value;
});
),
));
}
}
}
using the first code it works but using the second code I just get to see the items but nothing happens when I click on the radio buttons (although the onchanged does trigger because I tried to print the value and it's fine)
what am I doing wrong?
I found the solution here:
https://www.didierboelens.com/2018/05/hint-5-how-to-refresh-the-content-of-a-dialog-via-setstate/
The problem was that I had to seperate the widgets and create another stateful widget for the radio

control & disable a dropdown button in flutter?

I wanted to control a drop-down button and make it unclickable using a button.
Is there any way to make it disable. Basically not allowing it able to change.
new DropdownButton(
value: animalName,
items: animals.map(
(String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text('$value'),
);
},
).toList(),
onChanged: (value) {
setState(() {
animalName = value;
});
},
),
So this is the code I currently use on the drop-down button, but i cant disabled it.
Found this in the DropdownButton docs:
If items or onChanged is null, the button will be disabled, the down arrow will be grayed out, and the disabledHint will be shown (if provided)
DropdownButton(
onChanged: null,
items: [...],
)
This isn't what you want to hear, but I don't think there's currently an easy way. I experimented with simply removing all the items and that causes a nice little crash. Maybe worth raising an issue with the flutter people on github...
There is an alternative that may be good enough for you for now. If you wrap your DropdownButton in an IgnorePointer, when you want it to be disabled you can change IgnorePointer's ignoring property to true.
That way if the user taps on it, it won't do anything.
But you'll probably want to indicate to the user somehow that it's disabled as well, something like setting the hint text (as it's grey).
child: new IgnorePointer(
ignoring: true,
child: new DropdownButton(
hint: new Text("disabled"),
items: ["asdf", "wehee", "asdf2", "qwer"].map(
(String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text('$value'),
);
},
).toList(),
onChanged: (value) {},
),
You can make DropdownButtonFormField or DropdownButton disabled if set onChanged to null, and if you want that dropdown still shows selected value you must set disabledHint. For example:
DropdownButtonFormField<String>(
disabledHint: Text(_selectedItem),
value: _selectedItem,
onChanged: enabled ? (value) => setState(() => _selectedItem = value) : null,
items: items.map<DropdownMenuItem<String>>((item) {
return DropdownMenuItem(
value: item,
child: Text(item),
);
}).toList(),
)
Just wrap it with IgnorePointer widget to make DropdownButton disable
IgnorePointer(
ignoring: enabled,
child: new DropdownButton(
value: animalName,
items: animals.map(
(String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text('$value'),
);
},
).toList(),
onChanged: (value) {
setState(() {
animalName = value;
});
},
),
);
If items or onChanged is null, the button will be disabled, the down
arrow will be grayed out, and the disabledHint will be shown (if
provided)
So something like this should work:
DropdownButton<String>(
...
onChanged: this.enabled ? (id) => setState(() => this.id = id) : null,
)
okay, i found a trick that satisfied me
i wanted it hide/show the DropdownButton depending on CheckboxListTile
in StatefulWidget Class
first create a function ex:
_buildDropDown(bool enable) {
if (enable) {
return DropdownButton<String>(
hint: Text("Hint"),
items: <String>[
'item 1',
'item 2',
'item 3',
].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
onChanged: (value) {},
);
} else { // Just Divider with zero Height xD
return Divider(color: Colors.white, height: 0.0);
}
}
and now in build
bool enable = true;
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
CheckboxListTile(
title: const Text('Switcher'),
selected: true,
value: enable,
onChanged: (bool value) {
setState(() {
enable = value;
});
},
),
_buildDropDown(enable),
],
);
}
now every time you change enable it will display and hide the DropdownButton
DropdownButtonFormField(
onChange: isDisable ? null : (str){
},
disabledHint: isDisable ? null : Text('Your hint text'),
...
)
For disable
onChange: null
For disable Caption
disabledHint: Text('Your hint text')
//add widget'AbsorbPointer' true-disable,false-enable
// isEditable = ture
AbsorbPointer(
absorbing: isEditable
DropdownButton(
onChanged: null,
items: [...],
)
)
Simple:
decoration:InputDecoration(enabled: false),