How can I select/check only one checkbox to be checked at time?
And below is my code
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Checkbox(
checkColor: Colors.white,
value: isChecked,
onChanged: (bool value) {
setState(() {
isChecked = value;
// ignore: unnecessary_statements
passData(certId);
});
},
),
],
)),
Option1 - Using a map to maintain the state
Create a map:
final Map<int, bool> _state = {};
then, check if the value for that index is true/false:
return ListView.builder(itemBuilder: (context, index) {
return CheckboxListTile(
value: _state[index] ?? false,
onChanged: (value) {
setState(() {
_state[index] = value!;
});
},
title: Text(_data[index].text),
);
});
Option 2 - using a model:
class CheckBoxModel {
bool isChecked = false;
String text = "";
CheckBoxModel({required this.isChecked, required this.text});
}
and then, generate a List of 30 widgets:
final _data = List.generate(
30, (index) => CheckBoxModel(isChecked: false, text: "Item $index"));
Now, use a ListView.builder and based on the index, to update the corresponding value:
class Testing extends StatefulWidget {
const Testing({Key? key}) : super(key: key);
#override
State<Testing> createState() => _TestingState();
}
class _TestingState extends State<Testing> {
#override
Widget build(BuildContext context) {
return ListView.builder(itemBuilder: (context, index) {
return CheckboxListTile(
value: _data[index].isChecked,
onChanged: (value) {
setState(() {
_data[index].isChecked = value!;
});
},
title: Text(_data[index].text),
);
});
}
}
See also
Expansion tile trailing icon updates all in list on interaction with one tile. How can I only change the icon for the expanded tile?
Related
I have created a RadioListTile based on a list of string items in an initStat function. But i could not get the radio button to change when it is being selected. If it were in a build function i could just call setState and it would have marked it as selected.
How can i mark it as selected when i have created it at the beginning of the code. below here is the code i have tried, it actually print the selected radioTile value but i could not get it to change the selected state or the radio button selection.
List<String> list = ['Satisfied', 'Not Satisfied', 'Very Satisfied','Neutral'];
String _radioGroupValue = '';
int selectedRadioTile = 0;
void initState() {
super.initState();
selectedRadioTile = 0;
setState(() {
for (int n = 0; n < list.length; n++) {
answersRadio.add(RadioListTile(
value: n,
groupValue: _radioGroupValue,
onChanged: (val) {
print('selected Radio index $val');
setSelectedRadioTile(val);
setState(() {
});
},
title: Text(list[n]),
selected: _radioGroupValue == list[n] ? true : false,
));
}
});
}
setSelectedRadioTile(int val){
setState(() {
selectedRadioTile = val;
});
}
child: Column(children: answersRadio,)
class Example extends StatefulWidget {
const Example({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() {
return _Examplestate();
}
}
class _Examplestate extends State<Example> {
List<String> list = [
'Satisfied',
'Not Satisfied',
'Very Satisfied',
'Neutral'
];
String? _radioGroupValue;
List<RadioListTile> answersRadio = [];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Stateful Widget'),
),
body: Column(
children: [
for (var n in list)
RadioListTile<String>(
value: n,
groupValue: _radioGroupValue,
onChanged: (val) {
_radioGroupValue = val;
setState(() {});
},
title: Text(n),
toggleable: true,
selected: _radioGroupValue == n,
)
],
),
);
}
}
set int _groupValue = -1. define value for value : parameter according your need
Radio(
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.comfortable,
activeColor: AppTheme.primaryColor,
value: 1,
groupValue: _groupValue,
onChanged: (value) {
setState(() {
_groupValue = value as int;
});
},
),
I want to create a DropdownButton that unfolds when I hover over the Button. So basically I don't have to click to unfold the DropdownButton. Does anyone has a code sample or could help me with that?
By using GlobalKey we can open DropdownButton. To open on Hover, I'm using Inkwell.
Result
FullWidget
import 'package:flutter/material.dart';
class StraggedExample extends StatefulWidget {
const StraggedExample({Key? key}) : super(key: key);
#override
_StraggedExampleState createState() => _StraggedExampleState();
}
class _StraggedExampleState extends State<StraggedExample> {
final fromAPi = ["a", "e", "f", "a"];
late final dropitems;
late String initValue;
#override
void initState() {
super.initState();
final values = fromAPi.toSet().toList();
dropitems = List.generate(
values.length,
(index) => DropdownMenuItem(
child: Text("item $index"),
value: values[index],
),
);
initValue = values[0];
}
GlobalKey _dropdownButtonKey = GlobalKey();
openDropdown() {
GestureDetector? detector;
searchForGestureDetector(BuildContext element) {
element.visitChildElements((element) {
if (element.widget != null && element.widget is GestureDetector) {
detector = element.widget as GestureDetector;
} else {
searchForGestureDetector(element);
}
});
}
searchForGestureDetector(_dropdownButtonKey.currentContext!);
detector!.onTap!();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: InkWell(
onHover: (value) {
if (value) openDropdown();
},
onTap: () {},
child: DropdownButton(
key: _dropdownButtonKey,
value: initValue,
items: dropitems,
onChanged: (value) {
setState(() {
initValue = value as String;
});
},
),
),
),
);
}
}
ref: more details
I have taken the below code from How to create a checkbox using listview which checks all the items when one item is checked. how do i fix the code to not to check all the items?
class CheckBoxInListView extends StatefulWidget {
#override
_CheckBoxInListViewState createState() => _CheckBoxInListViewState();
}
class _CheckBoxInListViewState extends State<CheckBoxInListView> {
bool _isChecked = false;
List<String> _texts = [
"InduceSmile.com",
"Flutter.io",
"google.com",
"youtube.com",
"yahoo.com",
"gmail.com"
];
#override
Widget build(BuildContext context) {
return ListView(
padding: EdgeInsets.all(8.0),
children: _texts
.map((text) => CheckboxListTile(
title: Text(text),
value: _isChecked,
onChanged: (val) {
setState(() {
_isChecked = val;
});
},
))
.toList(),
);
}
}
Just make a List of '_isChecked' variable and use that.
class CheckBoxInListView extends StatefulWidget {
#override
_CheckBoxInListViewState createState() => _CheckBoxInListViewState();
}
class _CheckBoxInListViewState extends State<CheckBoxInListView> {
List<String> _texts = [
"InduceSmile.com",
"Flutter.io",
"google.com",
"youtube.com",
"yahoo.com",
"gmail.com"
];
List<bool> _isChecked;
#override
void initState() {
super.initState();
_isChecked = List<bool>.filled(_texts.length, false);
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: _texts.length,
itemBuilder: (context, index) {
return CheckboxListTile(
title: Text(_texts[index]),
value: _isChecked[index],
onChanged: (val) {
setState(
() {
_isChecked[index] = val;
},
);
},
);
},
);
}
}
you should have a list for is checked, and assign them individually to each item.
class CheckBoxInListView extends StatefulWidget {
#override
_CheckBoxInListViewState createState() => _CheckBoxInListViewState();
}
class _CheckBoxInListViewState extends State<CheckBoxInListView> {
final List<SimpleModel> _items = <SimpleModel>[
SimpleModel('InduceSmile.com', false),
SimpleModel('Flutter.io', false),
SimpleModel('google.com', false),
SimpleModel('youtube.com', false),
SimpleModel('yahoo.com', false),
SimpleModel('gmail.com', false),
];
#override
Widget build(BuildContext context) => ListView(
padding: const EdgeInsets.all(8),
children: _items
.map(
(SimpleModel item) => CheckboxListTile(
title: Text(item.title),
value: item.isChecked,
onChanged: (bool val) {
setState(() => item.isChecked = val);
},
),
)
.toList(),
);
}
class SimpleModel {
String title;
bool isChecked;
SimpleModel(this.title, this.isChecked);
}
The answer with the most votes works with correction, reports two errors, errors and lines that have been corrected:
A value of type 'bool?' can't be assigned to a variable of type 'bool'.
correction: _isChecked[index] = val!;
Non-nullable instance field '_isChecked' must be initialized.
correction: late List _isChecked;
I have a ListView.builder that builds a certain amount of widgets depending on user input. Each widget has their own specific name and has a DropDownMenu. I save this value with the corresponding name of the widget. It saves it correctly. However, when I try and read the data and create a new list from it, this error appears: [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The method '[]' was called on null.
'course' is a list. I am using the shared preferences import. When you tap the flat button, it should build the new list, but it is not. Could you explain to me why this is not working please?
This is code in the main app.
void main() {
runApp(Hemis());
}
class Hemis extends StatefulWidget {
#override
_HemisState createState() => _HemisState();
}
class _HemisState extends State<Hemis> {
_read() async {
final prefs = await SharedPreferences.getInstance();
for(int i = 0; i < course.length; i++) {
listMarks[i].name = course[i].name;
listMarks[i].mark = prefs.getInt(course[i].name) ?? 0;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
ListView.builder(
itemCount: course.length,
itemBuilder: (context, index) {
return ModuleListItem(
name: '${course[index].name}',
credits: course[index].credits,
);
},
),
FlatButton(
onPressed: () {
_read();
for(int i = 0; i < course.length; i++) {
print('${listMarks[i].name}: ${listMarks[i].mark}');
}
},
),
],
),
)
)
);
}
}
The widget that is being built.
final percentage = List<String>.generate(100, (i) => "$i");
class ModuleListItem extends StatefulWidget {
const ModuleListItem ({ Key key, this.name, this.credits }): super(key: key);
final String name;
final int credits;
#override
_ModuleListItemState createState() => _ModuleListItemState();
}
class _ModuleListItemState extends State<ModuleListItem> {
String dropdownValue;
bool isSwitched = false;
_save() async {
final prefs = await SharedPreferences.getInstance();
final key = '${widget.name}';
final value = int.parse(dropdownValue);
prefs.setInt(key, value);
print('saved $value');
}
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
DropdownButton<String>(
value: dropdownValue,
icon: Icon(Icons.keyboard_arrow_down),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
items: percentage.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
Switch(
value: isSwitched,
onChanged: (value) {
setState(() {
isSwitched = value;
if(isSwitched == true) {
_save();
}
print(isSwitched);
});
},
),
],
),
);
}
}
I am trying to ask the user to select the type of item in the first dropdown and then select from its corresponding available colours in the second dropdown. However, when after I have selected a colour (i.e. white) and now want to switch to another item that does not have this colour, an error is thrown:
"There should be exactly one item with [DropdownButton]'s value: white. \nEither zero or 2 or more
[DropdownMenuItem]s were detected with the same value"
Please help, I have already tried to setState at various places to update the values but this error still occurs.
The following is my code snippet:
StreamBuilder<QuerySnapshot>(
stream: mainItemsSnapshots,
builder: (context, snapshot) {
if (snapshot.hasError) return Text("Error");
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
{
List<DropdownMenuItem> dropdownMenuItems =
snapshot.data.documents
.map((DocumentSnapshot mainItem) {
return new DropdownMenuItem<String>(
value: mainItem.documentID,
child: Text(mainItem.documentID),
);
}).toList();
return DropdownButtonFormField<String>(
items: dropdownMenuItems,
onChanged: (String value) {
if (value != tempMainItemType) {
setState(() {
tempMainItemType = value;
tempItemColorsList.clear();
tempItemColorsList = [];
tempMainItemColor = null;
});
}
if (tempItemColorsList.isEmpty && value != null) {
tempItemColorsList = snapshot.data.documents
.where((element) => element.documentID == value)
.first
.data["colors"]
.keys
.map((color) => color.toString())
.toList()
.cast<String>();
}
setState((){});
},
onSaved: (String value) {
_order.mainItemType = value;
},
value: tempMainItemType,
);
}
}
},
),
// Main color
if (tempItemColorsList?.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: spacingGeneral),
child: textFieldLabel(context, "Main color"),
),
if (tempItemColorsList?.isNotEmpty)
DropdownButtonFormField(
items: tempItemColorsList.map((String color) {
return new DropdownMenuItem<String>(
value: color,
child: Text(color),
);
}).toList(),
onSaved: (String value) {
_order.mainColor = value;
},
value: tempMainItemColor,
onChanged: (String value) {
setState(() {
tempMainItemColor = value;
});
},
),
This maybe too late, but you can create a Map<String, List<String>> where the keys are the items of the first dropdown list, and the value will be the items of second dropdown list.
Here, I created a state that stores the selected item of the first dropdown list. Then I used it to map the items of the second dropdown list.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SampleDD(),
);
}
}
class SampleDD extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: DoubleDropdown(
items: <String, List<String>>{
'dark colors': ['black', 'gray', 'brown'],
'light colors': ['white', 'yellow', 'green'],
},
onChangedFirst: (val) => print('Selected first: $val'),
onChangedSecond: (val) => print('Selected second: $val'),
),
),
);
}
}
class DoubleDropdown extends StatefulWidget {
DoubleDropdown({
#required this.items,
#required this.onChangedFirst,
#required this.onChangedSecond,
});
final Map<String, List<String>> items;
final ValueChanged<String> onChangedFirst;
final ValueChanged<String> onChangedSecond;
#override
_DoubleDropdownState createState() => _DoubleDropdownState();
}
class _DoubleDropdownState extends State<DoubleDropdown> {
String selectedKey;
#override
void initState() {
super.initState();
selectedKey = widget.items.keys.first;
}
#override
Widget build(BuildContext context) {
return Column(
children: [
_buildFirstDropdown(),
_buildSecondDowndown(),
],
);
}
Widget _buildFirstDropdown() => DropdownButtonFormField<String>(
items: widget.items.keys.map((e) {
return DropdownMenuItem<String>(
child: Text(e),
value: e,
);
}).toList(),
onChanged: (val) {
setState(() => selectedKey = val);
widget.onChangedFirst(val);
},
value: selectedKey,
);
Widget _buildSecondDowndown() => DropdownButtonFormField<String>(
items: widget.items[selectedKey].map((e) {
return DropdownMenuItem<String>(
child: Text(e),
value: e,
);
}).toList(),
onChanged: widget.onChangedSecond,
value: widget.items[selectedKey].first,
);
}