grouped multi select dropdown in flutter - flutter

after long search for create multi select dropdown in flutter i found tow solutions
first one with custom class :
Is there an equivalent widget in flutter to the "select multiple" element in HTML
scound one with the package :
multi_select_flutter
But what I want is how to make a grouped dropdown in either of these two ways Because giving a title to each option group is very important in my case like this:

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 :)

Related

The argument type 'List<Contact>?' can't be assigned to the parameter type 'List<MultiSelectItem<dynamic>>'

I am trying to test flutter_contacts library.
I am willing to build a view where the user can select several contacts and display each of them in a chip when selected. My problem is that I have List Contact. But for the multiselect, I must provide a List<MultiSelectItem>.
Please, do you know if I can convert my List Contact into List<MultiSelectItem>.
Many thanks.
import 'package:flutter/material.dart';
import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:multi_select_flutter/bottom_sheet/multi_select_bottom_sheet_field.dart';
import 'package:multi_select_flutter/chip_display/multi_select_chip_display.dart';
void TEST() => runApp(const FlutterContactsExample());
final _multiSelectKeyContext = GlobalKey<FormFieldState>();
class FlutterContactsExample extends StatefulWidget {
const FlutterContactsExample({Key? key}) : super(key: key);
#override
State<FlutterContactsExample> createState() => _FlutterContactsExampleState();
}
class _FlutterContactsExampleState extends State<FlutterContactsExample> {
List<Contact>? _contacts;
bool _permissionDenied = false;
late List<bool> isChecked;
#override
void initState() {
super.initState();
_fetchContacts();
}
Future _fetchContacts() async {
if (!await FlutterContacts.requestPermission(readonly: true)) {
setState(() => _permissionDenied = true);
} else {
final contacts = await FlutterContacts.getContacts();
setState(() => _contacts = contacts);
}
}
#override
Widget build(BuildContext context) => MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('flutter_contacts_example')),
body: Column(
children: [
_body(),
///MultiSelect for Context
MultiSelectBottomSheetField(
key: _multiSelectKeyContext,
initialChildSize: 0.7,
maxChildSize: 0.95,
title: const Text("Context", style: TextStyle(fontSize: 19),),
buttonText: const Text(
"Context", style: TextStyle(fontSize: 19),),
searchTextStyle: const TextStyle(fontSize: 19),
searchHintStyle: const TextStyle(fontSize: 19),
itemsTextStyle: const TextStyle(fontSize: 19),
items: _contacts,
searchable: true,
onConfirm: (valueContext) {
setState(() {
_contextSelected = valueContext;
print('mon test ligne 152');
print(_contextSelected);
});
_multiSelectKeyContext.currentState!.validate();
},
chipDisplay: MultiSelectChipDisplay(
textStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold,fontSize: 19),
onTap: (dynamic item) {
setState(() {
});
_multiSelectKeyContext.currentState!.validate();
},
),
),
const SizedBox(height: 40),
],
)));
Widget _body() {
if (_permissionDenied) return const Center(child: Text('Permission denied'));
if (_contacts == null) return const Center(child: CircularProgressIndicator());
return ListView.builder(
itemCount: _contacts!.length,
itemBuilder: (context, i) => ListTile(
title: (Text(_contacts![i].displayName)),
onTap: () async {
final fullContact =
await FlutterContacts.getContact(_contacts![i].id);
await Navigator.of(context).push(
MaterialPageRoute(builder: (_) => ContactPage(fullContact!)));
}
)
);
}
}
class ContactPage extends StatelessWidget {
final Contact contact;
const ContactPage(this.contact, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(contact.displayName)),
body: Column(children: [
Text('First name: ${contact.name.first}'),
Text('Last name: ${contact.name.last}'),
Text(
'Phone number: ${contact.phones.isNotEmpty ? contact.phones.first.number : '(none)'}'),
Text(
'Email address: ${contact.emails.isNotEmpty ? contact.emails.first.address : '(none)'}'),
]));
}
In MultiSelectBottomSheetField instead of
items: _contacts,
you should write it like this:
items: _contacts.map((e) => MultiSelectItem(e, e.name)).toList(),
as shown here: link to example.
Because as the error you got and documentation both says, it needs List<MultiSelectItem<V>> here and you gave it List<Contact>?

How to show selected checkbox on prev screen?

I need to display checkboxes selected by the user on the previous page using pop()
I have a function that displays the user's message on the previous page and I need to pass the selected checkboxes in the same way. How to pass them as arguments to pop()?
Screen with checkboxes:
const TextScreen({Key? key}) : super(key: key);
#override
State<TextScreen> createState() => _TextScreenState();
}
class _TextScreenState extends State<TextScreen> {
// initial values for checkboxes
bool _privacy = false;
bool _termsOfUse = false;
// text controller for message input
TextEditingController textController = TextEditingController();
#override
void dispose() {
textController.dispose();
super.dispose();
}
// go to result screen
void getResult(BuildContext context) {
String valueResult = textController.text;
Navigator.pop(context, valueResult);
}
#override
Widget build(BuildContext context) {
//change state for privacy checkbox
_onPrivacyChange(value) {
setState(() {
_privacy = value!;
});
}
//change state for terms of use checkbox
_onTermsOfUSeChange(value) {
setState(() {
_termsOfUse = value!;
});
}
return Scaffold(
appBar: AppBar(
title: const Text('Enter data'),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: textController,
decoration: const InputDecoration(labelText: 'Message')),
const SizedBox(height: 20),
CheckboxListTile(
title: const Text('Privacy'),
controlAffinity: ListTileControlAffinity.leading,
value: _privacy,
onChanged: _onPrivacyChange,
contentPadding: EdgeInsets.zero,
),
CheckboxListTile(
title: const Text('Terms of use'),
controlAffinity: ListTileControlAffinity.leading,
value: _termsOfUse,
onChanged: _onTermsOfUSeChange,
contentPadding: EdgeInsets.zero,
),
ElevatedButton(
onPressed: () {
getResult(context);
},
child: const Text('Display result'))
],
)),
);
}
}
Screen with results display:
class ResultScreen extends StatefulWidget {
const ResultScreen({Key? key}) : super(key: key);
#override
State<ResultScreen> createState() => _ResultScreenState();
}
class _ResultScreenState extends State<ResultScreen> {
String? _valueText = '';
#override
Widget build(BuildContext context) {
// navigation to next screen
void _navToNextScreen(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const TextScreen()),
);
// update widget after result comes back
setState(() {
_valueText = result;
});
}
return Scaffold(
appBar: AppBar(
title: const Text('Results'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
_navToNextScreen(context);
},
child: const Text('Enter data'),
),
const SizedBox(height: 50),
Text('Message: $_valueText'),
const SizedBox(height: 20),
Text('Checkboxes: '),
],
)),
);
}
}
I think this should be the job of a simple state management strategy; for communication between separate widgets (in this case, two page widgets), that's the cleanest approach. You should create a common service to which both page widgets are subscribed: one to trigger the changes, the other to capture them and display them, using a ChangeNotifier service along with Consumer widgets, as shown below:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => SelectedData(),
child: MyApp()
)
);
}
class SelectedData extends ChangeNotifier {
bool _privacy = false;
bool _termsOfUse = false;
String _valueResult = '';
bool get privacy => _privacy;
bool get termsOfUse => _termsOfUse;
String get valueResult => _valueResult;
set privacy(bool value) {
_privacy = value;
notifyListeners();
}
set termsOfUse(bool value) {
_termsOfUse = value;
notifyListeners();
}
set valueResult(String value) {
_valueResult = value;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: ResultScreen(),
),
),
);
}
}
class TextScreen extends StatefulWidget {
const TextScreen({Key? key}) : super(key: key);
#override
State<TextScreen> createState() => _TextScreenState();
}
class _TextScreenState extends State<TextScreen> {
// text controller for message input
TextEditingController textController = TextEditingController();
#override
void initState() {
super.initState();
}
#override
void dispose() {
textController.dispose();
super.dispose();
}
// go to result screen
void getResult(BuildContext context) {
Navigator.pop(context);
}
#override
Widget build(BuildContext context) {
SelectedData data = Provider.of<SelectedData>(context, listen: false);
textController.text = data.valueResult;
//change state for privacy checkbox
_onPrivacyChange(value) {
data.privacy = value;
}
//change state for terms of use checkbox
_onTermsOfUSeChange(value) {
data.termsOfUse = value;
}
return Scaffold(
appBar: AppBar(
title: const Text('Enter data'),
),
body: Consumer<SelectedData>(
builder: (context, selectedData, child) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: textController,
onChanged: (value) {
data.valueResult = value;
},
decoration: const InputDecoration(labelText: 'Message')),
const SizedBox(height: 20),
CheckboxListTile(
title: const Text('Privacy'),
controlAffinity: ListTileControlAffinity.leading,
value: selectedData.privacy,
onChanged: _onPrivacyChange,
contentPadding: EdgeInsets.zero,
),
CheckboxListTile(
title: const Text('Terms of use'),
controlAffinity: ListTileControlAffinity.leading,
value: selectedData.termsOfUse,
onChanged: _onTermsOfUSeChange,
contentPadding: EdgeInsets.zero,
),
ElevatedButton(
onPressed: () {
getResult(context);
},
child: const Text('Display result'))
],
));
}
),
);
}
}
class ResultScreen extends StatefulWidget {
const ResultScreen({Key? key}) : super(key: key);
#override
State<ResultScreen> createState() => _ResultScreenState();
}
class _ResultScreenState extends State<ResultScreen> {
#override
Widget build(BuildContext context) {
// navigation to next screen
void _navToNextScreen(BuildContext context) async {
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const TextScreen()),
);
}
return Scaffold(
appBar: AppBar(
title: const Text('Results'),
),
body: Consumer<SelectedData>(
builder: (context, selectedData, child) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
_navToNextScreen(context);
},
child: const Text('Enter data'),
),
const SizedBox(height: 50),
Text('Message: ${selectedData.valueResult}'),
const SizedBox(height: 20),
const Text('Checkboxes: '),
Text('Privacy: ${selectedData.privacy}'),
Text('Terms of Use: ${selectedData.termsOfUse}')
],
));
}
),
);
}
}
Here's the output when you implement it this way:
So from what i see is you are only passing one value that is message and you what many values to pass at a time so here the map can be used and as pop() function takes dynamic returns you can pass any thing.
From your example i have created a sample example that will be a working proof which will demostrate the using map for passing data to previous screen.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: ResultScreen(),
);
}
}
class TextScreen extends StatefulWidget {
const TextScreen({Key? key}) : super(key: key);
#override
_TextScreenState createState() => _TextScreenState();
}
class _TextScreenState extends State<TextScreen> {
// initial values for checkboxes
bool _privacy = false;
bool _termsOfUse = false;
// text controller for message input
TextEditingController textController = TextEditingController();
#override
void dispose() {
textController.dispose();
super.dispose();
}
// go to result screen
void getResult(BuildContext context) {
String valueResult = textController.text;
final data = {
"message":valueResult,
"privacy": _privacy,
'terms':_termsOfUse,
};
Navigator.pop(context, data);
}
#override
Widget build(BuildContext context) {
//change state for privacy checkbox
_onPrivacyChange(value) {
setState(() {
_privacy = value!;
});
}
//change state for terms of use checkbox
_onTermsOfUSeChange(value) {
setState(() {
_termsOfUse = value!;
});
}
return Scaffold(
appBar: AppBar(
title: const Text('Enter data'),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: textController,
decoration: const InputDecoration(labelText: 'Message')),
const SizedBox(height: 20),
CheckboxListTile(
title: const Text('Privacy'),
controlAffinity: ListTileControlAffinity.leading,
value: _privacy,
onChanged: _onPrivacyChange,
contentPadding: EdgeInsets.zero,
),
CheckboxListTile(
title: const Text('Terms of use'),
controlAffinity: ListTileControlAffinity.leading,
value: _termsOfUse,
onChanged: _onTermsOfUSeChange,
contentPadding: EdgeInsets.zero,
),
ElevatedButton(
onPressed: () {
getResult(context);
},
child: const Text('Display result'))
],
)),
);
}
}
class ResultScreen extends StatefulWidget {
const ResultScreen({Key? key}) : super(key: key);
#override
State<ResultScreen> createState() => _ResultScreenState();
}
class _ResultScreenState extends State<ResultScreen> {
String? _valueText = '';
bool _privacyValue =false;
bool _termsOfUse = false;
#override
Widget build(BuildContext context) {
// navigation to next screen
void _navToNextScreen(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const TextScreen()),
);
if(result !=null)
{
setState(() {
if(result['message']!=null )_valueText = result['message'];
if(result['privacy']!=null) _privacyValue = result['privacy'];
if(result['terms']!=null) _termsOfUse = result['terms'];
});
}
}
return Scaffold(
appBar: AppBar(
title: const Text('Results'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
_navToNextScreen(context);
},
child: const Text('Enter data'),
),
const SizedBox(height: 50),
Text('Message: $_valueText'),
const SizedBox(height: 20),
Text('Privacy Value: $_privacyValue '),
const SizedBox(height: 20),
Text('Terms Value: $_termsOfUse '),
],
)),
);
}
}
You can make changes as per your needs, So let me know if it works.

Open / close filter menu

I have a code that is responsible for building a menu filter. It allows you to filter data by category and then by subcategory.
Initially, subcategories are in a closed state, but when you click on the arrow, they can be opened. Take a look
But my problem is that if I click on the arrow for any category (Country in my case), then all subcategories open at once. Take a look
It's my code
class _FilterDialogUserState extends State<FilterDialogUser> {
Map<String, List<String>?> filters = {};
bool needRefresh = false;
bool isClickedCountry = false;
#override
void initState() {
super.initState();
filters = widget.initialState;
}
List<FilterItem> children = [
FilterItem('Georgia', subitems: [
FilterItem('Tbilisi'),
FilterItem('Batumi'),
]),
FilterItem('Poland', subitems: [
FilterItem('Warsaw'),
FilterItem('Krakow'),
FilterItem('Wroclaw'),
]),
FilterItem('Armenia', subitems: [
FilterItem('Erevan'),
FilterItem('Gyumri'),
]),
];
// Building a dialog box with filters.
#override
Widget build(BuildContext context) {
return SimpleDialog(
title: const Text('Filters',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontFamily: 'SuisseIntl',
)),
contentPadding: const EdgeInsets.all(16),
// Defining parameters for filtering.
children: [
Column(
children: children.map(
(e) {
return Column(
children: [
InkWell(
onTap: () async {
setState(() {
isClickedCountry = !isClickedCountry;
});
},
child: Row(
children: [
Checkbox(
value: e.selected,
onChanged: (value) => setState(() {
e.subitems.forEach((element) =>
element.selected = value as bool);
e.selected = value as bool;
}),
),
Text(e.text),
const Spacer(),
isClickedCountry
? const Icon(Icons.arrow_circle_up)
: const Icon(Icons.arrow_circle_down)
],
),
),
if (e.subitems.isNotEmpty)
!isClickedCountry
? Container()
: Padding(
padding: const EdgeInsets.fromLTRB(30, 0, 0, 0),
child: Column(
children: e.subitems.map((e) {
return Row(children: [
Checkbox(
value: e.selected,
onChanged: (value) => setState(() {
e.selected = value as bool;
}),
),
Text(e.text),
]);
}).toList(),
),
)
],
);
},
).toList(),
),
]);
}
}
class FilterItem {
final String text;
bool selected;
List<FilterItem> subitems;
FilterItem(
this.text, {
this.selected = false,
this.subitems = const [],
});
}
Tell me, is it possible to change my code so that not all subcategories are opened, but only the one that the user clicks on?
The each main filter item must be controlled one by one.
Define List isClickedCountry variable
Save and load state from List isClickedCountry variable
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _buildBody(),
floatingActionButton: FloatingActionButton(
onPressed: () {},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
Widget _buildBody() {
return FilterDialogUser();
}
}
class FilterDialogUser extends StatefulWidget {
FilterDialogUser({Key key}) : super(key: key);
#override
State<FilterDialogUser> createState() => _FilterDialogUserState();
}
class _FilterDialogUserState extends State<FilterDialogUser> {
Map<String, List<String>> filters = {};
bool needRefresh = false;
List<bool> isClickedCountry = List.filled(3, false);
#override
void initState() {
super.initState();
// filters = widget.initialState;
}
List<FilterItem> children = [
FilterItem('Georgia', subitems: [
FilterItem('Tbilisi'),
FilterItem('Batumi'),
]),
FilterItem('Poland', subitems: [
FilterItem('Warsaw'),
FilterItem('Krakow'),
FilterItem('Wroclaw'),
]),
FilterItem('Armenia', subitems: [
FilterItem('Erevan'),
FilterItem('Gyumri'),
]),
];
// Building a dialog box with filters.
#override
Widget build(BuildContext context) {
return SimpleDialog(
title: const Text('Filters',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25,
fontFamily: 'SuisseIntl',
)),
contentPadding: const EdgeInsets.all(16),
// Defining parameters for filtering.
children: [
Column(
children: children.map(
(e) {
final int index = children.indexOf(e);
return Column(
children: [
InkWell(
onTap: () async {
setState(() {
isClickedCountry[index] = !isClickedCountry[index];
});
},
child: Row(
children: [
Checkbox(
value: e.selected,
onChanged: (value) => setState(() {
e.subitems.forEach((element) =>
element.selected = value as bool);
e.selected = value as bool;
}),
),
Text(e.text),
const Spacer(),
isClickedCountry[index]
? const Icon(Icons.arrow_circle_up)
: const Icon(Icons.arrow_circle_down)
],
),
),
if (e.subitems.isNotEmpty)
!isClickedCountry[index]
? Container()
: Padding(
padding: const EdgeInsets.fromLTRB(30, 0, 0, 0),
child: Column(
children: e.subitems.map((e) {
return Row(children: [
Checkbox(
value: e.selected,
onChanged: (value) => setState(() {
e.selected = value as bool;
}),
),
Text(e.text),
]);
}).toList(),
),
)
],
);
},
).toList(),
),
]);
}
}
class FilterItem {
final String text;
bool selected;
List<FilterItem> subitems;
FilterItem(
this.text, {
this.selected = false,
this.subitems = const [],
});
}

How to receive data from another statefull widget on the same screen?

So i need to receive data from my RadioDialog stf widget to BoxScreen widget.
I want to show different text depends of random value from boxItems, what i do on this line _randomBoxItem == boxOptionValue ? Text('text1') : Text('text2'),
And i dont know how to get value from _boxOption to boxOptionValue like exmple.
Here is my code:
class BoxScreen extends StatefulWidget {
const BoxScreen({Key? key}) : super(key: key);
static const routeName = '/box';
#override
State<BoxScreen> createState() => _BoxScreenState();
}
class _BoxScreenState extends State<BoxScreen> {
String? boxOptionValue;
final List<String> boxItems = [
'Banana',
'Apple',
'Orange',
];
Future<void> whatIsInTheBox(BuildContext context) async {
return await showDialog(
context: context,
builder: (BuildContext context) => const RadioDialog(),
);
}
#override
Widget build(BuildContext context) {
final String _randomBoxItem = (boxItems..shuffle()).first;
return Scaffold(
appBar: AppBar( title: const Text('BOX Game'),),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(_randomBoxItem),
_randomBoxItem == boxOptionValue ? Text('text1') : Text('text2'),
ElevatedButton.icon(
icon: const Icon(Icons.play_arrow_rounded),
onPressed: () {
whatIsInTheBox(context);
},
label: const Text('What is in BOX?'),
),
],
),
);
}
}
class RadioDialog extends StatefulWidget {
const RadioDialog({Key? key}) : super(key: key);
#override
State createState() => RadioDialogState();
}
class RadioDialogState extends State<RadioDialog> {
// SingingCharacter? _character = SingingCharacter.banana;
String? _boxOption;
void _handleBoxItemChange(String? value) {
setState(() {
_boxOption = value;
});
}
#override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 10),
title: const Text('Select option'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
RadioListTile<String>(
title: const Text('Banana'),
value: 'Banana',
groupValue: _boxOption,
onChanged: _handleBoxItemChange,
),
RadioListTile<String>(
title: const Text('Apple'),
value: 'Apple',
groupValue: _boxOption,
onChanged: _handleBoxItemChange,
),
RadioListTile<String>(
title: const Text('Orange'),
value: 'Orange',
groupValue: _boxOption,
onChanged: _handleBoxItemChange,
),
],
),
actions: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('I choose this!'),
),
],
);
}
}
I am with Flutter 2 month. Thanks
In the widget calling the dialog:
someVariable = await showDialog(...);
In the dialog widget:
Navigator.of(context).pop(returnedValue);
Are you able to declare a string outside of your widgets? Then you should be able to use it like so..
String newValue = ''; //**define a string outside the widgets**
class BoxScreen extends StatefulWidget {
const BoxScreen({Key? key}) : super(key: key);
static const routeName = '/box';
#override
State<BoxScreen> createState() => _BoxScreenState();
}
class _BoxScreenState extends State<BoxScreen> {
String? boxOptionValue;
final List<String> boxItems = [
'Banana',
'Apple',
'Orange',
];
Future<void> whatIsInTheBox(BuildContext context) async {
return await showDialog(
context: context,
builder: (BuildContext context) => const RadioDialog(),
);
}
#override
Widget build(BuildContext context) {
final String _randomBoxItem = (boxItems..shuffle()).first;
return Scaffold(
appBar: AppBar( title: const Text('BOX Game'),),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(_randomBoxItem),
_randomBoxItem == boxOptionValue ? Text('text1') : Text('text2'),
ElevatedButton.icon(
icon: const Icon(Icons.play_arrow_rounded),
onPressed: () {
whatIsInTheBox(context);
},
label: const Text('What is in BOX?'),
),
],
),
);
}
}
class RadioDialog extends StatefulWidget {
const RadioDialog({Key? key}) : super(key: key);
#override
State createState() => RadioDialogState();
}
class RadioDialogState extends State<RadioDialog> {
// SingingCharacter? _character = SingingCharacter.banana;
String? _boxOption;
void _handleBoxItemChange(String? value) {
setState(() {
newValue = value; //**Make it equal the new value**
});
}
#override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 10),
title: Text(newValue), //**use it in any widget like this**
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
RadioListTile<String>(
title: const Text('Banana'),
value: 'Banana',
groupValue: _boxOption,
onChanged: _handleBoxItemChange,
),
RadioListTile<String>(
title: const Text('Apple'),
value: 'Apple',
groupValue: _boxOption,
onChanged: _handleBoxItemChange,
),
RadioListTile<String>(
title: const Text('Orange'),
value: 'Orange',
groupValue: _boxOption,
onChanged: _handleBoxItemChange,
),
],
),
actions: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('I choose this!'),
),
],
);
}
}

Failed assertion:'items == null || items.isEmpty || value == null || items.where((DropdownMenuItem<T> item) return item.value == value;}).length == 1'

class DropDown extends StatefulWidget {
const DropDown({
this.data,
this.hint,
Key key,
}) : super(key: key);
final List<String> data;
final String hint;
#override
_DropDownState createState() => _DropDownState();
}
String _chosenValue1;
class _DropDownState extends State<DropDown> {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
width: 250,
padding: const EdgeInsets.all(0.0),
child: DropdownButton<String>(
iconSize: 30,
isExpanded: true,
value: _chosenValue1,
//elevation: 5,
items: widget.data.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
hint: Text(
widget.hint,
style: TextStyle(
color: Colors.black,
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
onChanged: (String value) {
setState(() {
_chosenValue1 = value;
});
},
),
),
);
}
}
DropDown(
data: [
'Non-Blanchable',
'Partial thickness skin',
'Full thickness skin loss involving damage or necrosis',
'Obscured by necrosis'
],
hint: 'Assessment',
),
DropDown(
data: [
'Indistinct, diffuse,none ',
'Distinct,outline clearly'
],
hint: 'Assessment',
),
i have been stuck on this problem for a while now, When i have the same data inside the data it works however all the dropdown would become the same, I want to be able to have different data for different dropdown , but when i do so the error is caused and i cant figure out whats wrong with it
import 'package:flutter/material.dart';
class DropDown extends StatefulWidget {
DropDown({
this.data,
this.hint,
this.initialValue,
Key? key,
}) : super(key: key);
final List<String>? data;
final String? hint;
final String? initialValue;
String chosenValue1 = "";
#override
_DropDownState createState() => _DropDownState();
}
class _DropDownState extends State<DropDown> {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
width: 250,
padding: const EdgeInsets.all(0.0),
child: DropdownButton<String>(
iconSize: 30,
isExpanded: true,
value: widget.initialValue!.isEmpty ? null : widget.initialValue!,
//elevation: 5,
items: widget.data!.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
hint: Text(
widget.hint!,
style: const TextStyle(
color: Colors.black,
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
onChanged: (value) {
setState(() {
widget.chosenValue1 = value!;
});
},
),
),
);
}
}
import 'package:flutter/material.dart';
import 'dropdown.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DropDown(
data: const [
'Non-Blanchable',
'Partial thickness skin',
'Full thickness skin loss involving damage or necrosis',
'Obscured by necrosis'
],
hint: 'Assessment',
initialValue: "Non-Blanchable",
),
DropDown(
data: const [
'Indistinct, diffuse,none',
'Distinct,outline clearly'
],
hint: 'Assessment',
initialValue: "",
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Use the above code it will fix ur error
I tried running your code and, after making your data and hint required params and moving the chosenValue variable inside your _DropDownState, it works perfectly fine. Can you maybe share some steps with how to reproduce the error that you're seeing, because I see two different dropdowns with values I can select independently of each other.
As per your description of how to reproduce the error, I've tried adding navigation between two screens, but it still all works as intended.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Dropdowns(),
);
}
}
class Dropdowns extends StatelessWidget {
const Dropdowns();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(40),
child: Column(
children: [
Text('This is the first screen'),
DropDown(
data: [
'Non-Blanchable',
'Partial thickness skin',
'Full thickness skin loss involving damage or necrosis',
'Obscured by necrosis'
],
hint: 'Assessment',
),
DropDown(
data: ['Indistinct, diffuse,none ', 'Distinct,outline clearly'],
hint: 'Assessment',
),
ElevatedButton(
child: Text('Go to second screen'),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SecondScreen(),
),
);
},
),
],
),
),
);
}
}
class DropDown extends StatefulWidget {
const DropDown({
required this.data,
required this.hint,
Key? key,
}) : super(key: key);
final List<String> data;
final String hint;
#override
_DropDownState createState() => _DropDownState();
}
class _DropDownState extends State<DropDown> {
String? _chosenValue1;
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
width: 250,
padding: const EdgeInsets.all(0.0),
child: DropdownButton<String>(
iconSize: 30,
isExpanded: true,
value: _chosenValue1,
//elevation: 5,
items: widget.data.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
hint: Text(
widget.hint,
style: TextStyle(
color: Colors.black,
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
onChanged: (String? value) {
setState(() {
_chosenValue1 = value;
});
},
),
),
);
}
}
class SecondScreen extends StatelessWidget {
const SecondScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('SECOND SCREEN'),
),
body: Padding(
padding: EdgeInsets.all(40),
child: Column(
children: [
Text('This is the second screen'),
DropDown(
data: [
'Non-Blanchable',
'Partial thickness skin',
'Full thickness skin loss involving damage or necrosis',
'Obscured by necrosis'
],
hint: 'Assessment',
),
DropDown(
data: ['Indistinct, diffuse,none ', 'Distinct,outline clearly'],
hint: 'Assessment',
),
],
),
),
);
}
}
onChanged: (String value) {
setState(() {
_chosenValue = value;
selcat = null; use dropdown as category
_chosenValue == null
? Container()
: _chosenValue == "hi"
? _hi()
: _chosenValue == "hello"
? _hello()
: Container(),