Flutter Checkbox not changing/updating/working - flutter

I am trying to learn checkboxes in Flutter.
The problem is, when I want to use checkboxes in Scaffold(body:) it is working. But I want to use it in different places like an item in ListView.
return Center(
child: Checkbox(
value: testValue,
onChanged: (bool value) {
setState() {
testValue = value;
}
},
));
But it is not working, updating and changing anything.
Edit: I solved my problem with putting checkbox in a StatefulBuilder. Thanks to #cristianbregant
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Center(
child: CheckboxListTile(
title: const Text('Animate Slowly'),
value: _valueCheck,
onChanged: (bool value) {
setState(() {
_valueCheck = value;
});
},
secondary: const Icon(Icons.hourglass_empty),
),
);
});

Try these maybe:
return Center(
child: CheckboxListTile(
title: const Text('Animate Slowly'),
value: _valueCheck,
onChanged: (bool value) {
setState(() {
_valueCheck = value;
});
},
secondary: const Icon(Icons.hourglass_empty),
),
);
and remember that if you are using it in a dialog or bottomsheet you need to wrap the Checkbox Widget in a Stateful builder because the state does not update.

Checkboxes require you have a Scaffold or Material as their parent. Without either of these, you get this helpful error message:
The following assertion was thrown building Checkbox(dirty, state: _CheckboxState#1163b):
No Material widget found.
Checkbox widgets require a Material widget ancestor.
In material design, most widgets are conceptually "printed" on a sheet of material.
In Flutter's material library, that material is represented by the Material widget. It is the Material widget that renders ink splashes, for instance. Because of this, many material library widgets require that there be a Material widget in the tree above them.
Once you have a material ancestor, you can place the ListView as it's child and it should show fine:
class SettingsPage extends StatefulWidget {
#override
_SettingsPageState createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
var _foo = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Toggle Foo'),
Checkbox(
value: _foo,
onChanged: (bool value) {
setState(() => _foo = value);
},
),
],
),
],
),
);
}
}

Seems like you will have to use both initState and dispose.
See my code example below:
class SettingsOrder extends StatefulWidget {
#override
_SettingsOrderState createState() => _SettingsOrderState();
}
class _SettingsOrderState extends State<SettingsOrder> {
List options = [];
List<bool> newoptions = [];
int selectedoption;
bool checkedstatus;
bool initialcall;
Future getproductlist(selectedoption, checkedstatus, initialcall) async{
List updatedlist = [];
final arguments = ModalRoute.of(context).settings.arguments as Map;
int itempos = 0;
options.clear();
if(initialcall == false){
for(var item in arguments['options']){
updatedlist.add({
'checkbox' : newoptions[itempos]
});
itempos++;
}
} else {
for(var item in arguments['options']){
updatedlist.add({
'checkbox' : checkedstatus
});
newoptions.add(false);
itempos++;
}
}
setState(() {
options = updatedlist;
});
}
#override
void initState(){
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
getproductlist(0, false, true);
return Scaffold(
body: SingleChildScrollView(
child: Container(
width: double.infinity,
child: ListView.builder(
primary: false,
shrinkWrap: true,
itemCount: options.length,
itemBuilder: (BuildContext context, int index){
return Container(
child: Theme(
data: ThemeData(
unselectedWidgetColor: Colors.grey
),
child: CheckboxListTile(
controlAffinity: ListTileControlAffinity.trailing,
title: Text(options[index]['name']),
value: options[index]['checkbox'],
onChanged: (newvalue){
int indexposition = index;
newoptions.removeAt(indexposition);
newoptions.insert(indexposition, newvalue);
getproductlist(indexposition, newvalue, false);
},
activeColor: Color.fromRGBO(0, 130, 214, 1),
checkColor: Colors.white,
),
),
);
}
),
),
),
);
}

Related

Avoid Updating Parent Variable when Updating Child

I have problem with updating variable since it also affect its parent. I am trying to remove List item from child but it also removing the parent data.
Here is my code
Future<void> ChangeSubCategory({required String value}) async {
if (this.mounted) {
setState(() {
if (!_subCategory.contains(value)) {
if (value == 'all') {
_subCategory = _categoryOutput[_category]; => set _subCategory from parent List
} else {
_subCategory.add(value);
}
} else if (_subCategory.contains(value)) {
_subCategory.remove(value); => When doing this one, the parent _categoryOutput also affected
}
UpdateFilter();
});
}
}
What is the safest way if we want to replicate variable from parent? since in flutter it also update the parent when we update child variable. Thank you.
Your problem is updating the whole state of parent which updates all the sub children widgets, to avoid that, you can use StatefulBuilder to only update the child you want!
As the below example, even all the three Checkboxes change the variable isChecked, but only the first Checkbox can refreshes the whole state which refreshes all the three Checkboxes childs, but the the second and the third Checkboxes can only refresh its state as they are using StatefulBuilder:
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool isChecked = false;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Refresh all the checkboxes'),
Checkbox(
value: isChecked,
onChanged: (value) {
setState(() {
isChecked = !isChecked;
});
},
),
Divider(),
Text('Refresh only this checkbox'),
StatefulBuilder(
builder: (context, setState) => Checkbox(
value: isChecked,
onChanged: (value) {
setState(() {
isChecked = !isChecked;
});
},
),
),
Divider(),
Text('Refresh only this checkbox'),
StatefulBuilder(
builder: (context, setState) => Checkbox(
value: isChecked,
onChanged: (value) {
setState(() {
isChecked = !isChecked;
});
},
),
),
],
),
)),
);
}
}

Is there a way to remove a widget from the tree after button press?

Flutter newbie here. I'm trying to make a simple to-do list that I'm using to build up my skills. The idea is there is a to-do list that you can move the properties up and down and once you complete a task you check it off and it should display a checkmark and maybe eventually play an animation and remove it. For now, I'm stuck on just removing it, I had a few implementation ideas, but a lot of them will require me to restart my code. Is there a way to make it so that once I am done with a specific widget I can delete it or replace it with another, noting the fact that these widgets are in a list? code for reference:
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
var checkboxes = [
Checkboxstate(title: "test"),
Checkboxstate(title: "test2"),
Checkboxstate(title: "test3"),
Checkboxstate(title: "tes4t"),
];
bool value = true;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("To-Do"),
backgroundColor: Colors.purple,
),
backgroundColor: Colors.blue,
body: Container(
padding: EdgeInsets.all(10),
child: ReorderableListView(
shrinkWrap: true,
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
var item = checkboxes.removeAt(oldIndex);
checkboxes.insert(newIndex, item);
});
},
children: [
...checkboxes.map(buildBox).toList(),
],
),
),
);
}
Card buildBox(Checkboxstate checkbox) {
return Card(
key: UniqueKey(),
child: CheckboxListTile(
title: Text(checkbox.title),
key: UniqueKey(),
controlAffinity: ListTileControlAffinity.leading,
value: checkbox.value,
onChanged: (value) {
setState(() {
checkboxes.removeAt();
checkbox.value = value;
});
},
),
);
}
}
class Checkboxstate {
String title;
bool value;
Checkboxstate({this.title, this.value = false});
}
EDIT:
As suggested by Prabhanshu I followed his steps and instead used the item builder; however, there is a new issue: the reorderablelistview now doesn't work. My idea was that the reason was that the index was different for the reorderable list than the checkbox widget I create, but I am still unable to find a solution.
new code:
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
var checkboxes = [
Checkboxstate(title: "test"),
Checkboxstate(title: "test2"),
Checkboxstate(title: "test3"),
Checkboxstate(title: "tes4t"),
];
bool value = true;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("To-Do"),
backgroundColor: Colors.purple,
),
backgroundColor: Colors.blue,
body: Container(
padding: EdgeInsets.all(10),
child: ReorderableListView.builder(
shrinkWrap: true,
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
var item = checkboxes.removeAt(oldIndex);
checkboxes.insert(newIndex, item);
});
},
itemCount: checkboxes.length,
itemBuilder: (context, index) {
Checkboxstate box = checkboxes.elementAt(index);
return buildBox(box, index);
},
),
),
);
}
Card buildBox(Checkboxstate checkbox, int index) {
return Card(
key: UniqueKey(),
child: CheckboxListTile(
title: Text(checkbox.title),
key: UniqueKey(),
controlAffinity: ListTileControlAffinity.leading,
value: checkbox.value,
onChanged: (value) {
setState(() {
checkbox.value = value;
checkboxes.removeAt(index);
});
},
),
);
}
}
class Checkboxstate {
String title;
bool value;
Checkboxstate({this.title, this.value = false});
}
If drag to dismiss is an option:
What you're looking for here is the Dismissible Widget, which as the docs describe is a widget that can be dismissed by dragging in the indicated direction.
Check out the flutter cookbook tutorial for a detailed explanation
on how to use this widget.
This will definitely help you.
replace the build method body with this
Scaffold(
appBar: AppBar(
title: Text("To-Do"),
backgroundColor: Colors.purple,
),
backgroundColor: Colors.blue,
body: Container(
padding: EdgeInsets.all(10),
child: ReorderableListView.builder(
shrinkWrap: true,
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
var item = checkboxes.removeAt(oldIndex);
checkboxes.insert(newIndex, item);
});
},
itemCount: checkboxes.length,
itemBuilder: (context, index) {
Checkboxstate box = checkboxes.elementAt(index);
return buildBox(box, index);
},
),
),
);
and
Replace this method also
Card buildBox(Checkboxstate checkbox, int index) {
return Card(
key: UniqueKey(),
child: CheckboxListTile(
title: Text(checkbox.title),
key: UniqueKey(),
controlAffinity: ListTileControlAffinity.leading,
value: checkbox.value,
onChanged: (value) {
setState(() {
checkboxes.removeAt(index);
checkbox.value = value;
});
},
),
);
}

RadioListTile is not working with setstate

What is working when i click on radio button it is not changing
class OnBoardingCheckBoxWithActionWidget extends StatefulWidget {
String label;
Function onClick;
OnBoardingCheckBoxWithActionWidget(this.label, {this.onClick});
#override
_OnBoardingCheckBoxWithActionWidgetState createState() =>
_OnBoardingCheckBoxWithActionWidgetState();
}
class _OnBoardingCheckBoxWithActionWidgetState
extends State<OnBoardingCheckBoxWithActionWidget> {
bool checked = true;
#override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: RadioListTile(
value: checked,
groupValue: checked,
toggleable: true,
title: Text("${widget.label}"),
onChanged: (val) {
if (widget.onClick != null) {
widget.onClick(val);
}
setState(() {
print("$checked");
checked = !checked;
print("$checked");
});
},
),
),
); // Card(child: Row(children: [Expanded(child: RadioListTile())],),);
}
}
You're trying to change some value from your Parent Stateful widget.
Try changing your current widget like
class OnBoardingCheckBoxWithActionWidget
extends StatelessWidget {
String label;
Function onClick;
var groupValue;
var buttonValue;
OnBoardingCheckBoxWithActionWidget(this.label, {this.onClick, this.groupValue,, this.buttonValue});
#override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: RadioListTile(
value: buttonValue,
groupValue: groupValue,
toggleable: true,
title: Text("${this.label}"),
onChanged: (val) {
if (this.onClick != null) {
this.onClick(val);
}
},
),
),
); // Card(child: Row(children: [Expanded(child: RadioListTile())],),);
}
}
And then call it like
OnBoardingCheckBoxWithActionWidget("Button1", onClick: (value){
setState((){
///Your Other Code
if(value){
groupValue = 'button1';
}
});
},
groupValue: someGroupValueVariableInState, buttonValue: 'button1')
Note that, the groupValue and buttonValue variables will always be some other variables other than booleans. When groupValue == buttonValue, the corresponding button will automatically marked as checked.

Related DropdownButtons and raising onChange event

I have two DropdownButton widget. Content of second one depends on first one selection. Second DropdownButton will initiate refresh of third widget. How can I initiate refresh of second DropdownButton when first one is populated? And then how can I refresh third widget when second DropdownButton populated also?
class ParentBloc {
Stream<List<Parent>> get items => _controller.asyncMap(...);
Future<List<Parent>> _callApi() {
// call endpoint /parents
}
}
class ChildBloc {
ChildBloc(this.parentId);
int parentId;
Stream<List<Child>> get items => _controller.asyncMap(...);
Future<List<Child>> _callApi() {
// call endpoint /parents/$parentId/children
}
}
// This bloc created at init state
ParentBloc parentBloc;
// This bloc will be created only after value will
// be selected in the Parent dropdownbutton because
// I need to know `parentId`.
ChildBloc childBloc;
#override
void initState() {
super.initState();
parentBloc = ParentBloc();
}
#override
Widget build(BuildContext context) {
return Row(
children: [
StreamBuilder<List<Parent>>(
stream: parentBloc.items,
builder: (context,snapshot) {
return DropdownButton(
items: snapshot.data.map((item) {
return DropdownButtonItem();
}),
);
}
),
// Content of this widget depends on above one
StreamBuilder<List<Child>>(
stream: childBloc.items,
builder: (context,snapshot) {
return DropdownButton(
items: snapshot.data.map((item) {
return DropdownButtonItem();
}),
);
}
),
// Content of this widget depends on above one
StreamBuilder<List<Grandchild>>(
stream: grandchildBloc.items,
builder: (context,snapshot) {
return ListView(),
);
}
),
]
);
}
Provided you're doing this inside a StatefulWidget, you can use setState inside one of your functions where you update the variables that in turn have to be used to control what is currently displayed in each of your widgets.
It should look something like this (inside your Dropdown):
onChanged: (newValue) {
setState(() {
_currentSelection = newValue;
});
},
Update: after discussion in the comments, here's a working example that I made of how something can be updated based on a value selected inside a dropbox, hope it helps:
import 'package:flutter/material.dart';
class ExampleWidget extends StatefulWidget {
#override
_ExampleWidgetState createState() => _ExampleWidgetState();
}
class _ExampleWidgetState extends State<ExampleWidget> {
List<String> someStringsToSelectFrom = [
'value1',
'value2',
'value3',
];
String selectedValue;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Select something:'),
DropdownButton<String>(
value: selectedValue,
icon: Icon(
Icons.arrow_downward,
color: Colors.deepPurpleAccent,
),
iconSize: 24,
elevation: 16,
style: TextStyle(
fontSize: 30.0,
color: Colors.green,
),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String newValue) {
setState(() {
selectedValue = newValue;
});
},
items: someStringsToSelectFrom.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
Text('This will update after you selected a value:'),
someStringsToSelectFrom.contains(selectedValue) ? Text(selectedValue + ' was selected') :
Text('Still waiting for user action.'),
],
),
),
);
}
}

Dynamic list of check box tile in alert dialog not working

There is no clear answer on how to implement a checkbox tile in a dialog and set the state to work.
A print statement is working in setting the state of the checkbox is not changing, but other statements are working. Where can I find the answer?
I am using a dialog with multiple check boxes for multi select. Is there another of implementing multiselect in Flutter?
child: TextFormField(
decoration: InputDecoration(
labelText: 'Team Leader',
labelStyle: TextStyle(color: Colors.black)),
controller: teamLeaderController,
enabled: false,
style: TextStyle(color: Colors.black),
),
onTap: () {
showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return CheckBoxDialog(context, teamLeader,
"Choose Team Leader", teamLeaderController, onSubmit);
});
}),
class CheckBoxState extends State<CheckBoxDialog> {
BuildContext context;
List<String> places;
String title;
TextEditingController con;
bool state;
CheckBoxState(this.context, this.places, this.title, this.con);
#override
void initState() {
super.initState();
state = false;
}
#override
Widget build(BuildContext context) {
return new AlertDialog(
title: new Text(title),
content:
Column(children: getMultiSelectOption(context, places, con, state)),
actions: <Widget>[
FlatButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
}),
FlatButton(
child: Text('Ok'),
onPressed: () {
widget.onSubmit("");
Navigator.of(context).pop();
})
],
);
}
List<Widget> getMultiSelectOption(BuildContext context, List<String> places,
TextEditingController con, bool state) {
List<Widget> options = [];
List<String> selectedList = [];
for (int i = 0; i < places.length; i++) {
options.add(CheckboxListTile(
title: Text(places[i]),
value: selectedList.contains(places[i]),
onChanged: (bool value) {
print("on change: $value title: ${places[i]}");
setState(() {
if (value) {
selectedList.add(places[i]);
} else {
selectedList.remove(places[i]);
}
print("contains: ${selectedList.contains(places[i])}");
print("status: $value");
});
}));
}
return options;
}
}
Suppose you have a Dialog with some Widgets such as RadioListTile, DropdowButton… or anything that might need to be updated WHILE the dialog remains visible, how to do it?
Look at this example here.
https://www.didierboelens.com/2018/05/hint-5-how-to-refresh-the-content-of-a-dialog-via-setstate/
Suppose you have a Dialog with some Widgets such as RadioListTile, DropdowButton… or anything that might need to be updated WHILE the dialog remains visible, how to do it?
Difficulty: Beginner
Background
Lately I had to display a Dialog to let the user select an item from a list and I wanted to display a list of RadioListTile.
I had no problem to show the Dialog and display the list, via the following source code:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
class Sample extends StatefulWidget {
#override
_SampleState createState() => new _SampleState();
}
class _SampleState extends State<Sample> {
List<String> countries = <String>['Belgium','France','Italy','Germany','Spain','Portugal'];
int _selectedCountryIndex = 0;
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){_showDialog();});
}
_buildList(){
if (countries.length == 0){
return new Container();
}
return new Column(
children: new List<RadioListTile<int>>.generate(
countries.length,
(int index){
return new RadioListTile<int>(
value: index,
groupValue: _selectedCountryIndex,
title: new Text(countries[index]),
onChanged: (int value) {
setState((){
_selectedCountryIndex = value;
});
},
);
}
)
);
}
_showDialog() async{
await showDialog<String>(
context: context,
builder: (BuildContext context){
return new CupertinoAlertDialog(
title: new Text('Please select'),
actions: <Widget>[
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Cancel');},
child: new Text('Cancel'),
),
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Accept');},
child: new Text('Accept'),
),
],
content: new SingleChildScrollView(
child: new Material(
child: _buildList(),
),
),
);
},
barrierDismissible: false,
);
}
#override
Widget build(BuildContext context) {
return new Container();
}
}
I was surprised to see that despite the setState in lines #34-36, the selected RadioListTile was not refreshed when the user tapped one of the items.
Explanation
After some investigation, I realized that the setState() refers to the stateful widget in which the setState is invoked. In this example, any call to the setState() rebuilds the view of the Sample Widget, and not the one of the content of the dialog. Therefore, how to do?
Solution
A very simple solution is to create another stateful widget that renders the content of the dialog. Then, any invocation of the setState will rebuild the content of the dialog.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
class Sample extends StatefulWidget {
#override
_SampleState createState() => new _SampleState();
}
class _SampleState extends State<Sample> {
List<String> countries = <String>['Belgium','France','Italy','Germany','Spain','Portugal'];
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){_showDialog();});
}
_showDialog() async{
await showDialog<String>(
context: context,
builder: (BuildContext context){
return new CupertinoAlertDialog(
title: new Text('Please select'),
actions: <Widget>[
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Cancel');},
child: new Text('Cancel'),
),
new CupertinoDialogAction(
isDestructiveAction: true,
onPressed: (){Navigator.of(context).pop('Accept');},
child: new Text('Accept'),
),
],
content: new SingleChildScrollView(
child: new Material(
child: new MyDialogContent(countries: countries),
),
),
);
},
barrierDismissible: false,
);
}
#override
Widget build(BuildContext context) {
return new Container();
}
}
class MyDialogContent extends StatefulWidget {
MyDialogContent({
Key key,
this.countries,
}): super(key: key);
final List<String> countries;
#override
_MyDialogContentState createState() => new _MyDialogContentState();
}
class _MyDialogContentState extends State<MyDialogContent> {
int _selectedIndex = 0;
#override
void initState(){
super.initState();
}
_getContent(){
if (widget.countries.length == 0){
return new Container();
}
return new Column(
children: new List<RadioListTile<int>>.generate(
widget.countries.length,
(int index){
return new RadioListTile<int>(
value: index,
groupValue: _selectedIndex,
title: new Text(widget.countries[index]),
onChanged: (int value) {
setState((){
_selectedIndex = value;
});
},
);
}
)
);
}
#override
Widget build(BuildContext context) {
return _getContent();
}
}