A similar question has been asked before for Flutter see question. However no valid answer was given, so it may be worth reopening.
Here is a complete code example.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
const cities = <String>{
'Atlanta',
'Baltimore',
'Boston',
'Chicago',
'Denver',
'Houston',
'Los Angeles',
'Philadelphia',
'San Francisco',
'Washington, DC',
};
enum SelectionState {
all('(All)'),
none('(None)'),
some('(Some)');
const SelectionState(this._value);
final String _value;
@override
String toString() => _value;
}
class SelectionModel {
SelectionModel({required this.selection, required this.choices});
late final Set<String> selection;
final Set<String> choices;
SelectionState get selectionState {
if (selection.isEmpty) return SelectionState.none;
if (choices.difference(selection).isNotEmpty) {
return SelectionState.some;
} else {
return SelectionState.all;
}
}
SelectionModel add(String value) {
if (value == '(All)') {
return SelectionModel(selection: {...choices}, choices: choices);
} else {
return SelectionModel(selection: selection..add(value), choices: choices);
}
}
SelectionModel remove(String value) {
if (value == '(All)') {
return SelectionModel(selection: <String>{}, choices: choices);
} else {
selection.remove(value);
return SelectionModel(selection: selection, choices: choices);
}
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Dropdown with Select (All)',
theme: ThemeData(
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
SelectionModel model =
SelectionModel(selection: {...cities}, choices: {...cities});
List<PopupMenuItem<String>> getCheckboxList() {
var out = <PopupMenuItem<String>>[];
out.add(PopupMenuItem<String>(
padding: EdgeInsets.zero,
value: '(All)',
child: StatefulBuilder(builder: (context, setState) {
return CheckboxListTile(
value: model.selectionState == SelectionState.all,
controlAffinity: ListTileControlAffinity.leading,
title: const Text('(All)'),
onChanged: (bool? checked) {
setState(() {
if (checked!) {
model = model.add('(All)');
} else {
model = model.remove('(All)');
}
});
},
);
})));
for (final value in model.choices) {
out.add(PopupMenuItem<String>(
padding: EdgeInsets.zero,
value: value,
child: StatefulBuilder(builder: (context, setState) {
return CheckboxListTile(
value: model.selection.contains(value),
controlAffinity: ListTileControlAffinity.leading,
title: Text(value),
onChanged: (bool? checked) {
setState(() {
if (checked!) {
model = model.add(value);
} else {
model = model.remove(value);
}
});
},
);
})));
}
return out;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
width: 250,
color: Colors.orangeAccent,
child: PopupMenuButton<String>(
constraints: const BoxConstraints(maxHeight: 400),
position: PopupMenuPosition.under,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text(model.selectionState.toString()),
const Spacer(),
const Icon(Icons.keyboard_arrow_down_outlined),
],
),
),
itemBuilder: (context) {
return getCheckboxList();
},
onCanceled: () {
setState(() {
model = SelectionModel(
selection: model.selection, choices: model.choices);
});
},
),
),
),
const Spacer(),
Text('Selected cities: ${model.selection.join(', ')}'),
],
),
),
);
}
}
If I click individual cities, everything is fine. If I click the (All) checkbox, I would like all the checkboxes to turn false (which does not happen unless I close the menu.)
How can I do this? If I just have a list of CheckboxListTiles in the main app, the logic works fine, and the checkboxes update as I want. However, once they are part of the menu, it doesn't work properly anymore.
Thank you for any help with this! Tony
Aucun commentaire:
Enregistrer un commentaire