I am trying to build multiple dependent dropdown on flutter. The second one depend on 1st one. here is the code to the dropdown I have implemented.
Container(
child: new DropdownButton<String>(
underline: SizedBox(
height: 1,
),
hint: new Text("Select Faculty"),
value: facultyId,
items: faculty.map((item) {
return new DropdownMenuItem(
child: new Text(item['name']),
value: item['id'].toString(),
);
}).toList(),
onChanged: (faculty == null)
? null
: (String newValue) {
setState(() {
filteredbatch.clear();
facultyId = newValue;
for (var item in allbatch) {
if (item['facultyId'].toString() == newValue){
filteredbatch.add(item);
disableBatch = false;
}
}
});
print(facultyId);
},
),
),
Container(
child: new DropdownButton<String>(
underline: SizedBox(
height: 1,
),
disabledHint: new Text("Select Faculty First"),
hint: Text("Select Batch"),
value: batchId,
onChanged: disableBatch
? null
: (String newValue) {
setState(() {
batchId = newValue;
disableSection = false;
});
print(batchId);
},
items: filteredbatch.map((item) {
return DropdownMenuItem(
child: Text(item['name']),
value: item['id'].toString(),
);
}).toList()),
),
Whenever I select one dropdown item from first one, it enables 2nd dropdown and lets me to select an item from that dropdown. And when I select an item from 2nd dropdown and go back to change first dropdown, it throws error that dropdown requires one item with respective value. 0 or 2 found. I am lost here. How do I resolve this error?
What is going on here is quite simple. Let's say "allbatch" has these values:
faculty: foo , which has batchids: foo1, foo2, foo3
faculty: bar , which has batchids: bar1, bar2, bar3.
When you select foo in the 1st dropdown a new "filteredbatch" is created and it only contains foo1,foo2,foo3. You then select foo3 in your 2nd dropdown and everything is still working fine...
BUT when you change your 1st dropdown to bar, then "filteredbatch" only contains:bar1, bar2, bar3 and your second dropdown value is still set to foo3 which can not be found in the newly generated "filteredbatch", and then you get that error you are seeing.
To fix this simply set batchId to null before changing the "filteredbatch" in your 1st dropdown onChanged method:
setState(() {
//fix
batchId = null;
//end of fix
filteredbatch.clear();
facultyId = newValue;
for (var item in allbatch) {
if (item['facultyId'].toString() == newValue){
filteredbatch.add(item);
disableBatch = false;
}
}
});
Your 2nd dropdown will revert back to hint text and the app user will have to select a new batchId.
If you have any more questions feel free to ask.
Flutter Dropdown Button FormField Dependent
List<String> dataList = ["A", "B", "C"];
List<String> dataListA = ["A1", "A2", "A3", "A4", "A5"];
List<String> dataListB = ["B1", "B2", "B3", "B4", "B5"];
List<String> dataListC = ["C1", "C2", "C3", "C4", "C5"];
String? valueItem;
List<String> listItem = [];
// DropdownButtonFormField
Container(
margin: const EdgeInsets.only(bottom: p20),
child: DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Data list',
labelStyle: const TextStyle(),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0)),
contentPadding: const EdgeInsets.only(left: 5.0),),
value: valueItem,
isExpanded: true,
items: dataList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),);
}).toSet().toList(),
onChanged: (value) {
setState(() {
valueItem= value!;
if (valueItem=="A") {
listItem= dataListA;
} else if (valueItem== "B") {
listItem= dataListB;
} else if (valueItem== "C") {
listItem= dataListC;
}
});
},
),
),
// Second DropdownButtonFormField
Container(
margin: const EdgeInsets.only(bottom: p20),
child: DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'List dependent',
labelStyle: const TextStyle(),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0)),
contentPadding: const EdgeInsets.only(left: 5.0),),
value: ListItem,
isExpanded: true,
items: listItem.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);}).toSet().toList(),
onChanged: (value) {
setState(() {
your_value = value!;
});
},
),
),
Related
In my flutter app I've created a dropdown. initially there is only one dropdown but the user can add more. the items of these dropdowns are the same. but the selected results has to be different. but my code is returning the last selected value. so how can I add it on list?
Model
class DropModel {
String? selected;
DropModel({this.selected});
setData(list) {
for (int i = 0; i < list.length; i++) {
selected = list;
}
}
}
initialize
List<String>? selCat;
DropModel selDrop = DropModel();
dropdown widget
DropdownButtonFormField2 _generatedDropDown(
List<String> category, String? selected, int index) {
final group = _GroupControllers();
return DropdownButtonFormField2(
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.zero,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5),
),
),
isExpanded: true,
hint: const Text(
'Select Category',
style: TextStyle(fontSize: 14),
),
icon: const Icon(
Icons.arrow_drop_down,
color: Colors.black45,
),
iconSize: 30,
buttonHeight: 60,
buttonPadding: const EdgeInsets.only(left: 20, right: 10),
items: category
.map((item) => DropdownMenuItem<String>(
value: item,
child: Text(
item,
style: const TextStyle(
fontSize: 14,
),
),
))
.toList(),
validator: (value) {
if (value == null) {
return 'Please select Catagory.';
}
},
onChanged: (value) {
//Do something when changing the item if you want.
setState(() {
// selDrop.setData(value.toString());
selected = value.toString();
selDrop.selected = selected;
selCat!.insert(index, selected.toString());
});
},
onSaved: (value) {
//selDrop.setData(value.toString());
selected = value.toString();
selDrop.selected = selected;
selCat!.insert(index, selected.toString());
},
);
}
result looped
print(".................${selDrop.selected}"); // returns the last added/ selected value
print(".................${selCat}"); //returns null
the traditional List returns null and the model returns the last added/selected value. how can i add multiple dropdown selected values to a List? each has different values. in a textfield I could create dynamic TextEditingController but how can I manage dropdowns
I was using normal dropdown menu in my project with small amount of data fetched from my api, but now i have menu which could reach hundred of values and make it hard to select an item.
That's why i wanted to use DropDownSearch but instead i get an error
Normal dropdown code which works very fine
DropdownButton(
showSearchBox: true,
showSelectedItem: true,
items: data3.map((item) {
return new DropdownMenuItem(
child: Text(item['first_name']+" "+ item['last_name']),
value: item['emp_code'].toString(),
);
}).toList(),
onChanged: (newVal) {
setState(() {
_mySelection3 = newVal.toString();
});
},
value: _mySelection3,
),
data3 = [{emp_code: 111, first_name: adnen, last_name: hamouda}, {emp_code: 666, first_name: ahmed, last_name: ahmed 99}....
this is the result: normal dropdown
But when i tried to convert it to dropDownSearch i got this result: search dropdown
I want to show the first_name and last_name like the normal dropdown but save the value of their 'emp_code' which i will be using in another api later. How can i fix it ?
DropdownSearch(
mode: Mode.DIALOG,
showSearchBox: true,
items: data3.map((item) {
return new DropdownMenuItem(
child: Text(item['first_name']+" "+ item['last_name']),
value: item['emp_code'].toString(),
);
}).toList(),
onChanged: (newVal) {
setState(() {
print(data3);
_mySelection3 = newVal.toString();
});
},
selectedItem: _mySelection3,
),
Here is the way I found to use searchable dropdown lists.
I tried dropdown_search package at first, but it seems the documentation and examples related with the latest version (5.0.2) are depreciated.
Later on, I moved to dropdown_button2 and I am happy the way the DropdownButtonFormField2 was implemented, because it is very similar which I have done to the flutter DropdownButtonFormField implementation, so far.
Have a look at the Flutter bundled DropdownButtonFormField example:
return DropdownButtonFormField<SetorTemp>(
decoration: const InputDecoration(
labelText: 'Setor institucional'),
isExpanded: true,
icon: const Icon(Icons.arrow_downward),
items: snapshot.data!
.map((SetorTemp rtItem) =>
DropdownMenuItem<SetorTemp>(
value: rtItem,
child: Text(
'${rtItem.nome} (${rtItem.sigla})',
softWrap: true,
),
))
.toList(),
hint: Text(
'${selectedSetorTemp.nome} (${selectedSetorTemp.sigla})'),
onChanged: (SetorTemp? newValue) {
setState(() {
// do your logic here!
selectedSetorTemp = newValue;
});
},
);
And the DropdownButtonFormField2 using dropdown_button2 package
return DropdownButtonFormField2<SetorTemp>(
decoration: const InputDecoration(
labelText: 'Setor institucional'),
isExpanded: true,
icon: const Icon(Icons.arrow_downward),
items: snapshot.data!
.map((SetorTemp rtItem) =>
DropdownMenuItem<SetorTemp>(
value: rtItem,
child: Text(
'${rtItem.nome} (${rtItem.sigla})',
softWrap: true,
),
))
.toList(),
hint: Text(
'${selectedSetorTemp.nome} (${selectedSetorTemp.sigla})'),
onChanged: (SetorTemp? newValue) {
setState(() {
// Do your logic here!
selectedSetorTemp = newValue;
});
},
// Search implementation using dropdown_button2 package
searchController: searchContentSetor,
searchInnerWidget: Padding(
padding: const EdgeInsets.only(
top: 8,
bottom: 4,
right: 8,
left: 8,
),
child: TextFormField(
controller: searchContentSetor,
decoration: InputDecoration(
isDense: false,
contentPadding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 8,
),
labelText: 'Setor institucional',
hintText: 'Parte do nome do setor...',
counterText: '',
hintStyle: const TextStyle(fontSize: 16),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
),
searchMatchFn: (rtItem, searchValue) {
return (rtItem.value.nome
.toLowerCase()
.contains(searchValue.toLowerCase()));
},
//This to clear the search value when you close the menu
onMenuStateChange: (isOpen) {
if (!isOpen) {
searchContentSetor.clear();
}
},
);
They are similar at first:
However, after clicking they will show their differences from the initial implementation:
To the dropdown_button2 package:
The selectedSetorTemp variable is of the type SetorTemp and the model I have used is:
class SetorTemp {
final int id;
final String? nome;
final String? sigla;
SetorTemp({required this.id, this.nome, this.sigla});
factory SetorTemp.fromJson(Map<String, dynamic> json) {
return SetorTemp(
id: json['id'] as int,
nome: json['nome'] as String,
sigla: json['sigla'] as String,
);
}
Map<String, dynamic> toJson() => {
'nome': nome,
'sigla': sigla,
};
}
I m new to flutter, need help to set the value of the DropdownButton programmatically.
The value is from textfield. Once i click it, it will set the value at the dropdownbutton automatically.
Widget _districtListContainer() {
return Container(
width: 360.0,
child: new InputDecorator(
decoration: InputDecoration(
suffixIcon: new Icon(
Icons.search,
color: Colors.blue[700],
),
labelText: 'Select District',
labelStyle: TextStyle(fontSize: 12.0)),
isEmpty: _selectedDistrict == null,
child: new DropdownButtonHideUnderline(
child: new DropdownButton<District>(
value: _selectedDistrict,
isDense: true,
isExpanded: false,
onChanged: (District newValue) {
setState(() {
_selectedDistrict = newValue;
});
},
items: _listDistrict?.map((District value) {
return new DropdownMenuItem<District>(
value: value,
child: new Text(
value.district != null ? value.district : '',
style: new TextStyle(fontSize: 11.0),
),
);
})?.toList() ??
[],
),
),
),
margin: EdgeInsets.only(bottom: 10.0));
}
thanks
First Of All, Add the data into the list[] From the TextFormfield then retrieve the list into DropDownButton item.
Also, Make Sure, DropDown Button List Display Textformfield data insert activity could not be able to update simultaneously.
I have layout issues because the text from the dropdown menu that you choose can't fit inside the TextFormField and I can't seem to fix this issue. I have tried adding the overflow: TextOverflow.ellipsis property but that only changes for the dropdown labels, still when I choose one, they can't fit inside the TextFormField.
When you press the dropdown button, it shows like this:
and with the TextOverflow.elipsis property:
which is fine, but I still have the layout issue because of the chosen text that is now displayed inside the textformfield:
How can I add the same property to the TextFormField, or any sort of solution to this issue?
The code:
Row(
children: [
Expanded(
child: DropdownButtonFormField<String>(
decoration:
kTextFieldDecoration.copyWith(
hintText: 'Legal status',
labelText: 'Legal status',
),
value: _legalStatus,
items: [
'Sole proprietorship',
'Partnerships',
'Limited Liability Company (LLC)',
'Corporation',
'Small Business Corporation (S-Corporation)'
]
.map((label) => DropdownMenuItem(
child: Text(
label.toString(),
),
value: label,
))
.toList(),
onChanged: (value) {
setState(() {
_legalStatus = value;
print(value);
});
},
validator: (thisValue) {
if (thisValue == null) {
return 'Please choose your legal status';
}
return null;
},
),
),
SizedBox(
width: 16.0,
),
Container(
width: 120.0,
child: DropdownButtonFormField<String>(
decoration:
kTextFieldDecoration.copyWith(
hintText: 'Year established',
labelText: 'Year established',
),
value: _yearEstablished,
items: items // a list of numbers that are Strings
.map((label) => DropdownMenuItem(
child:
Text(label.toString()),
value: label,
))
.toList(),
onChanged: (value) {
setState(() {
_yearEstablished = value;
print(value);
});
},
validator: (thisValue) {
if (thisValue == null) {
return 'Please choose your company year of establishment';
}
return null;
},
),
),
],
),
Thanks in advance for your help!
You need to use isExpanded property of DropDownFormField to solve this error.
Row(
children: [
Expanded(
child: DropdownButtonFormField<String>(
isExpanded: true,
decoration:
kTextFieldDecoration.copyWith(
hintText: 'Legal status',
labelText: 'Legal status',
),
value: _legalStatus,
items: [
'Sole proprietorship',
'Partnerships',
'Limited Liability Company (LLC)',
'Corporation',
'Small Business Corporation (S-Corporation)'
]
.map((label) => DropdownMenuItem(
child: Text(
label.toString(),
),
value: label,
))
.toList(),
onChanged: (value) {
setState(() {
_legalStatus = value;
print(value);
});
},
validator: (thisValue) {
if (thisValue == null) {
return 'Please choose your legal status';
}
return null;
},
),
),
SizedBox(
width: 16.0,
),
Container(
width: 120.0,
child: DropdownButtonFormField<String>(
decoration:
kTextFieldDecoration.copyWith(
hintText: 'Year established',
labelText: 'Year established',
),
value: _yearEstablished,
items: items // a list of numbers that are Strings
.map((label) => DropdownMenuItem(
child:
Text(label.toString()),
value: label,
))
.toList(),
onChanged: (value) {
setState(() {
_yearEstablished = value;
print(value);
});
},
validator: (thisValue) {
if (thisValue == null) {
return 'Please choose your company year of establishment';
}
return null;
},
),
),
],
),
You need to use selectedItemBuilder parameter which will control how the selected item will be displayed on the button. Then, TextOverflow.ellipsis will work with you as expected. Here's how to use it:
selectedItemBuilder: (BuildContext context) {
return items.map<Widget>((String item) {
return Text(item, overflow: TextOverflow.ellipsis,);
}).toList();
},
I am new to Flutter and I am creating a form with TextFormfield and DropdownButtonFormField. I am trying to display the error message of the DropdownButtonFormField(Site Crop) to below the text box like how the site name field display its error message.
Should I continue using DropdownButtonFormField or is there a better way to do this?
FormSample
code for dropdown
child: DropdownButtonHideUnderline(
child: DropdownButtonFormField(
decoration: InputDecoration(
errorBorder: InputBorder.none,
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
),
hint: Text(
'Choose Country',
style: TextStyle(
fontSize: 16,
color: NewSite.black.withOpacity(0.6),
),
),
validator: (value) => value == null
? 'Please fill in' : null,
value: _selectedLocation,
onChanged: (newValue) {
setState(() {
_selectedLocation = newValue;
});
},
items: _locations.map((location) {
return DropdownMenuItem(
child: new Text(location),
value: location,
);
}).toList(),
icon: Icon(Icons.arrow_drop_down, color: ColorTheme.main,),
style: TextStyle(color: NewSite.black),
isExpanded: true,
elevation: 26,
dropdownColor: NewSite.white,
),
),
Try Form Widget,
Wrap this your filed inside Form,
like this
import 'package:flutter/material.dart';
class DemoDropDownCreate extends StatefulWidget {
#override
_DemoDropDownCreate createState() =>
_DemoDropDownCreate();
}
class _DemoDropDownCreate
extends State<DemoDropDownCreate> {
final _formKey = GlobalKey<FormState>();
bool _autovalidate = false;
String selectedCountry;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Form(
key: _formKey,
autovalidate: _autovalidate,
child:
Column(
children: <Widget>[
DropdownButtonFormField<String>(
value: selectedCountry,
hint: Text(
'Select Country',
),
onChanged: (salutation) =>
setState(() => selectedCountry = salutation),
validator: (value) => value.isEmpty ? 'field required' : null,
items:
['India', 'Japan'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
FlatButton(
child: Text('PROCEED'),
color: Colors.green,
onPressed: () {
if (_formKey.currentState.validate()) {
//form is valid, proceed further
_formKey.currentState.save();//save once fields are valid, onSaved method invoked for every form fields
print("success");
} else {
setState(() {
_autovalidate = true; //enable realtime validation
});
print("Please select all field");
}
},
)
],
),
),
);
}
}
You can indeed use validators for DropdownButtonFormField
DropdownButtonFormField(
validator: (dynamic value) => value.isEmpty ? "An error happened or please pick something" : null,
decoration: InputDecoration(....
you can change value.isEmpty to any condition you want, same as the "An error happened" message.