In the items, list set the type to data to add checkbox or to sep to add a title. The output from the dialog will be a dictionary in the form of {2,3} where value 2 = Cordoba.
Full Code
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: HomeScreen(),
);
}
}
class MultiSelectDialogItem<V> {
V value;
String name;
String type;
MultiSelectDialogItem(
{required this.name, required this.type, required this.value});
}
class MultiSelectDialog<V> extends StatefulWidget {
const MultiSelectDialog({
Key? key,
required this.items,
required this.initialSelectedValues,
}) : super(key: key);
final List<MultiSelectDialogItem<V>> items;
final Set<V> initialSelectedValues;
@override
State<StatefulWidget> createState() => _MultiSelectDialogState<V>();
}
class _MultiSelectDialogState<V> extends State<MultiSelectDialog<V>> {
final _selectedValues = <V>{};
@override
void initState() {
super.initState();
_selectedValues.addAll(widget.initialSelectedValues);
}
void _onItemCheckedChange(V itemValue, bool checked) {
setState(() {
if (checked) {
_selectedValues.add(itemValue);
} else {
_selectedValues.remove(itemValue);
}
});
}
void _onCancelTap() {
Navigator.pop(context);
}
void _onSubmitTap() {
Navigator.pop(context, _selectedValues);
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Select place'),
contentPadding: const EdgeInsets.all(20.0),
content: SingleChildScrollView(
child: ListTileTheme(
contentPadding: const EdgeInsets.fromLTRB(14.0, 0.0, 24.0, 0.0),
child: ListBody(
children: widget.items.map(_buildItem).toList(),
),
),
),
actions: <Widget>[
ElevatedButton(
onPressed: _onCancelTap,
child: const Text('CANCEL'),
),
ElevatedButton(
onPressed: _onSubmitTap,
child: const Text('OK'),
)
],
);
}
Widget _buildItem(MultiSelectDialogItem<V> item) {
final checked = _selectedValues.contains(item.value);
return item.type == "data"
? CheckboxListTile(
value: checked,
title: Text(item.name),
controlAffinity: ListTileControlAffinity.leading,
onChanged: (checked) => _onItemCheckedChange(item.value, checked!),
)
: Container(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
item.name,
style: TextStyle(color: Color.fromARGB(255, 91, 91, 91)),
),
),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
HomeScreenState createState() => HomeScreenState();
}
class HomeScreenState extends State<HomeScreen> {
void _showMultiSelect(BuildContext context) async {
final items = <MultiSelectDialogItem<int>>[
MultiSelectDialogItem(name: 'Argentina', type: 'sep', value: 1),
MultiSelectDialogItem(name: 'Cordoba', type: 'data', value: 2),
MultiSelectDialogItem(name: 'Chaco', type: 'data', value: 3),
MultiSelectDialogItem(name: 'Buenos Aires', type: 'data', value: 4),
MultiSelectDialogItem(name: 'USA', type: 'sep', value: 5),
MultiSelectDialogItem(name: 'California', type: 'data', value: 6),
MultiSelectDialogItem(name: 'Florida', type: 'data', value: 7),
];
final selectedValues = await showDialog<Set>(
context: context,
builder: (BuildContext context) {
return MultiSelectDialog(
items: items,
initialSelectedValues: [].toSet(),
);
},
);
print(selectedValues);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
child: Text("show dialog"),
onPressed: () {
_showMultiSelect(context);
},
),
),
);
}
}
Output

Hope this helps. Happy Coding :)