I have a stateful widget that utilizes flutter_multi_select_items package. The issue I'm having is the alternating state only changes once upon first click. I want the checkboxes to be unclicked by default, yet the selected input requires a value and it remains true after each state change event. How do I properly alternate adding/removing id values from the checkbox array and keeping the state in sync with the values being added and removed?
Logs
flutter: true
flutter: 1
flutter: removing add on
flutter: []
Performing hot reload...
Reloaded 1 of 1718 libraries in 426ms (compile: 21 ms, reload: 156 ms, reassemble: 174 ms).
flutter: false
flutter: adding add-on
flutter: [1]
flutter: true
flutter: removing add on
flutter: []
flutter: true
class _AddServiceState extends State<AddService> {
final MultiSelectController<dynamic> _multiSelectcontroller = MultiSelectController();
List<dynamic> checkAddons = [];
Widget build(BuildContext context) {
List<dynamic> _addOns = widget.service_profile['service_add_ons'];
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: 'Adjust your add-ons')
),
body: SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Column(children: <Widget>[
ElevatedButton(
onPressed: () {
_multiSelectcontroller.selectAll();
},
child: const Text('Select All')),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MultiSelectCheckList(
maxSelectableCount: 5,
textStyles: const MultiSelectTextStyles(
selectedTextStyle: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold)),
itemsDecoration: MultiSelectDecorations(
selectedDecoration: BoxDecoration(
color: Colors.indigo
.withOpacity(0.8))),
listViewSettings: ListViewSettings(
separatorBuilder: (context, index) =>
const Divider(
height: 0,
)),
controller: _multiSelectcontroller,
items: List.generate(
_addOns.length,
(index) => CheckListCard(
value: _addOns[index]['id'],
title:
Text(_addOns[index]['title']),
subtitle:
Text(_addOns[index]['price']),
selectedColor: Colors.white,
checkColor: Colors.indigo,
selected:
checkedAddons.indexOf(_addOns[index]['id'], index) == index,
enabled: true,
checkBoxBorderSide:
const BorderSide(
color: Colors.blue),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(
5)))),
onChange:
(allSelectedItems, selectedItem) {
if (allSelectedItems.isNotEmpty) {
checkedAddons = [];
checkedAddons
.addAll(allSelectedItems);
return;
}
print(checkedAddons
.contains(selectedItem));
if (checkedAddons
.contains(selectedItem)) {
print('removing add on');
setState(() {
checkedAddons.removeWhere((item) => item == selectedItem);
});
print(checkedAddons);
} else {
print('adding add-on');
setState(() {
checkedAddons.add(selectedItem);
});
print(checkedAddons);
}
},
onMaximumSelected:
(allSelectedItems, selectedItem) {
// CustomSnackBar.showInSnackBar(
// 'The limit has been reached', context);
},
)
]
)
)
}
});
If you put checkAddons
inside the build
method when you use setState(), the method build is recalled, then the checkAddons
will be reset to the default value of []
, which will make this error. The solution is to put checkAddons
outside the build
method.
There is an example for flutter_multi_select_items:
import 'package:flutter/material.dart';
import 'package:flutter_multi_select_items/flutter_multi_select_items.dart';
class MultiSelectExample extends StatefulWidget {
@override
State<MultiSelectExample> createState() => _MultiSelectExampleState();
}
class _MultiSelectExampleState extends State<MultiSelectExample> {
final MultiSelectController _controller = MultiSelectController();
final List<Map<String, String>> items = [
{'id': '1', 'title': 'Apple'},
{'id': '2', 'title': 'Banana'},
{'id': '3', 'title': 'Cherry'},
{'id': '4', 'title': 'Date'},
{'id': '5', 'title': 'Elderberry'},
];
List<dynamic> selectedItems = []; // Manage items selected
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Multi Select Example'),
),
body: Column(
children: [
const SizedBox(height: 16),
Row(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: ElevatedButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Selected items: $selectedItems')),
);
},
child: const Text('Show Selected Items'),
),
),
ElevatedButton(
onPressed: () {
_controller.selectAll();
setState(() {
selectedItems = items.map((e) => e['id']).toList();
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('All items selected')),
);
},
child: const Text('Select All'),
),
],
),
Expanded(
child: MultiSelectCheckList(
controller: _controller,
items: List.generate(
items.length,
(index) => CheckListCard(
value: items[index]['id'],
title: Text(items[index]['title']!),
checkColor: Colors.white,
selectedColor: Colors.indigo,
),
),
textStyles: const MultiSelectTextStyles(
selectedTextStyle: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
itemsDecoration: MultiSelectDecorations(
selectedDecoration: BoxDecoration(
color: Colors.indigo.withOpacity(0.8),
),
),
onChange: (allSelectedItems, selectedItem) {
setState(() {
selectedItems = allSelectedItems;
});
print('Selected items: $selectedItems');
},
),
),
],
),
);
}
}