I've seen other similar topics but it didn't help me. I'm not a programmer or have a strong background on this field, just a mechanical engineer trying to develop some apps ;)
So, here's the thing, when I use dropdownmenu, i usually define a list with the items beforehand, like the below:
List<String> varfluid = ["CO2", "O2"];
String _varfluid = "CO2";
void metFluid(String value){
setState((){
_varfluid=value;
});
}
This is defined before, and the below is how I create my DropdownButton
new DropdownButton(
onChanged: (String value){
metFluid(value);
},
value: _varfluid,
items: varfluid.map((String value){
return new DropdownMenuItem(
value: value,
child: new Text(value),
);
}).toList(),
)
It is working fine for me so far, the challenge I'm facing now is to populate another DropDown with items from a sqflite db. I tried to do it in a similar way to the above but I'm getting an error. Here's what I have:
This is the method for returning a list with all the items from the DB
Future<List> pegarTanques() async {
var bdCliente = await db;
var res = await bdCliente.rawQuery("SELECT * FROM $tabelaTanque");
return res.toList();
}
Then I run this piece of code to get only the desired property of the object to my List.
List todosTanques;
List<String> nomestanques = [""];
String _nomestanques = "";
void listartanques() async {
var db = new BD();
todosTanques = await db.pegarTanques();
for (int i = 0; i < todosTanques.length; i++) {
Tanque tanque = Tanque.map(todosTanques[i]);
nomestanques.add(tanque.nome);
}
}
And then I'm getting the list just fine. However when I try to implement it the same way as I did with the dropdown with predefined items, everytime I click on an item from the dropdown list I get this error:
'package:flutter/src/material/dropdown.dart': Failed assertion: line 620 pos 15: 'items == null || items.isEmpty || value == null || items.where((DropdownMenuItem<T> item) => item.value == value).length == 1': is not true.
User-created ancestor of the error-causing widget was:
MaterialApp file:///C:/Users/Victor/AndroidStudioProjects/formv3/lib/main.dart:9:14
Any help on this?
Related
Is it possible to uppdate or rebuild open DropdownButtonFormField, e.g. with GetIt or Provider - or perhaps by using a stream - when new data becomes available?
At the moment the options in the dropdown gett added as data arrives while the dropdown is un-expanded, but once the user taps on it only those items that were available at the time they tapped are available and new items are only available if and when the user closes and reopens the dropdown.
My user interface currently has
DropdownButtonFormField(
value: dropdownValue,
icon: const Icon(Icons.keyboard_arrow_down),
items: dropdownItems,
validator: (value) =>
(value == null || value == "0") ? 'Please select a recipient.' : null,
onChanged: (String? newValue) {
if (newValue != null) {
dropdownValue = newValue;
} else {
dropdownValue = "0";
}
setState(() {});
},
)
the dropdownItems comes from
List<DropdownMenuItem<String>>? get dropdownItems {
if (clientListicle.items.isEmpty) {
developer.log('go fetch', name: '_MessagePageState get dropdownItems');
fetchClients();
}
for (var element in clientListicle.items) {
DropdownMenuItem<String> potentialItem =
DropdownMenuItem(value: element.id.toString(), child: Text(element.title));
bool isHave = false;
for (var exstElement in menuItems) {
if (exstElement.value == element.id.toString()) {
isHave = true;
}
}
if (!isHave) {
menuItems.add(potentialItem);
setState(() {});
}
}
if (menuItems.length == 1) {
return null;
} else {
return menuItems;
}
}
clientListicle is a singleton that inherits from a class that extends ChangeNotifier, and is registered in GetIt.
I've had a quick look at the implementation of DropdownButtonFormField and FormField that it extends, thinking maybe one could add functionality to or override a build method or some such, but think maybe I'm missing something simpler/easier and am probably just a bit out of my depth here... :-)
Update, I've tried adding a final _formFieldKey = GlobalKey<FormFieldState>(); key to the dropdown widget thinking I might be able to use that from the getter to trigger a rebuild, but no luck yet.
I am trying to integrate a search and filter textfield in my app, but I am not completely sure of the errors my compiler is throwing. I think I am having type issues, or that I have not set up my inheritances correctly?
Ok, so basically I am sending a http request to an api and receiving this data where I have set up my class to display it. In a futurebuilder I have some conditional logic trying to sort the right data I need into separate lists(Im not sure if this the right thing to do but I digress) I then create a map (resultMap) and within that I loop through the arrays and nest a map (innerMap).
class _HomePageState extends State<HomePage> {
final controller = TextEditingController();
String? searchString = '';
late final Future<ProjectModel> futureProjects;
List<ProjectSearch> searchList = []; //I think this causing this issues.....
List<String?> jobNames = [];
List<String?> jobNumbers = [];
List<String?> techs = [];
List<String?> pms = [];
List<String?> address = [];
List<String?> majors = [];
List<String?> budget = [];
Map resultMap = {};
#override
void initState() {
super.initState();
futureProjects = fetchProjects();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
'Upcoming / Live Projects',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
backgroundColor: ColorConstants.darkScaffoldBackgroundColor,
),
drawer: const CustomDrawer(),
backgroundColor: ColorConstants.lightScaffoldBackgroundColor,
// ignore: avoid_unnecessary_containers
body: Center(
child: FutureBuilder<ProjectModel>(
future: futureProjects,
builder: (context, snapshot) {
if (snapshot.hasData) {
var data = snapshot.data!;
var columns = data.columns;
var rows = data.rows;
for (var item in rows!) {
var cells = item.cells;
for (var elements in cells!)
if (elements.columnId != null) {
if (elements.columnId == 2057691532158852) {
var displayValues = elements.displayValue;
if (displayValues != null) {
jobNames.add(displayValues);
}
..... continuing conditional logic
//Here is where I nest my arrays to the resultMap map:
int index = 0;
for (int i = 0; i < jobNames.length; i++) {
Map innerMap = {};
innerMap['name'] = jobNames[index];
innerMap['address'] = address[index];
innerMap['jobNumber'] = jobNumbers[index];
innerMap['major'] = majors[index];
innerMap['tech'] = techs[index];
innerMap['pM'] = pms[index];
innerMap['budget'] = budget[index];
index++;
resultMap[i] = innerMap;
}
I have been looking trough some tutorials and it looks like it is easier to apply string search functions using .contains() but these only work on lists. So I convert my resultMap using a forEach to create my searchList list.
resultMap.forEach((key, value) => searchList.add(value));
Here is my TextFormField:
TextFormField(
textAlign: TextAlign.center,
controller: controller,
onChanged: search,
.....
And then my ListView.builder:
return MaterialButton(
onPressed: () => showModalBottomSheet<void>(
backgroundColor: Colors.transparent,
context: context,
builder: (BuildContext context) {
final projectData = searchList[index];
return
Text(
projectData['name'],
And finally the function I am using to search:
void search(String query) {
List finder = searchList;
final suggestions = searchList.where((check) {
final projectName = check['name'].toLowerCase();
final input = query.toLowerCase();
return projectName.contains(input);
}).toList();
setState(() {
finder = suggestions;
});
}
I hope this makes sense...
When I get rid of the model it doesn't break down but the search function doesnt filter thru the array. However here is the compile error:
type 'List<dynamic>' is not a subtype of type 'List<ProjectModel>' of 'function
result'
Try this showSearch() method
It is an inbuilt functionality which is simple to use.
I think the problem arises when you convert map to list. please try below procedure to convert map to list when data comes from api...
obviously do anything(search) with list data in your app.
just change api url from below
Future<List<ProjectModel>> fetchProjectData() async {
final response =
await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
final parsed = jsonDecode(response.body);
return parsed.map<ProjectModel>((json) => ProjectModel.fromJson(json)).toList();
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load data');
}
}
I want to get some details from database and put that values to dropdown menu. I have successfully got the information that I want to put on dropdown from database and put it into dropdown list.
But when I tried to select one item from dropdown list, it shows this error.
There should be exactly one item with [DropdownButton]'s value: null.
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
Here is the code I wrote so far.
Variables
var supplier;
var _supplierDetails = [];
Init Method
#override
void initState() {
super.initState();
getSupliers();
}
Get Suppliers
void getSupliers() async {
final responseSup =
await http.get(Uri.parse('http://170.14.0.0:8020/supplier/'));
final jsonDataSup = jsonDecode(responseSup.body) as List;
print(jsonDataSup);
setState(() {
_supplierDetails = jsonDataSup;
});
}
DropDown menu implemented
DropdownButton(
hint: Text('Select Supplier'),
items: _supplierDetails.map((list) {
return DropdownMenuItem(
child: Text(list['Name']),
value: list['id'].toString(),
);
}).toList(),
onChanged: (value) {
setState(() {
supplier = value;
});
},
value: supplier,
)
Can someone help me to fix this issue please?
Thank you
It seems there are more than one item where item['id'] is null. DropdownButton need all it's items to have a unique value
During the process of my web-application I want the user to allow to make changes and save them. For that process I'm using SharedPreferences in order to store the changes. I have a list of titles called konzernDataTitle. In general this list is used to display my titles.
In order to change something I edit this list and "upload" it to my SharedPreferences. However, everything works fine but I cant get the new list prefsKonzernTitle into my DropDownButton. For that I'm using a FutureBuilder. The error is quite simple:
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
[...]
The following assertion was thrown building FutureBuilder<List<DropdownMenuItem<String>>>(dirty,
state: _FutureBuilderState<List<DropdownMenuItem<String>>>#dcca3):
Assertion failed:
items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) {
return item.value == value;
}).length == 1
"There should be exactly one item with [DropdownButton]'s value: MyTitle. \nEither zero or 2 or more
[DropdownMenuItem]s were detected with the same value"
The relevant error-causing widget was:
FutureBuilder<List<DropdownMenuItem<String>>>
FutureBuilder Function:
Future<List<DropdownMenuItem<String>>> getDropDownItems() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool prefsTitleExist;
var newList = List<String>.empty();
if (prefs.containsKey("prefsKonzernTitle")) {
var getPrefList = prefs.getStringList("prefsKonzernTitle");
newList = getPrefList!;
prefsTitleExist = true;
} else {
prefsTitleExist = false;
}
final actualList = prefsTitleExist ? newList : konzernDataTitle;
return actualList.map((data) {
return DropdownMenuItem<String>(
value: data,
child: Text(
data,
),
);
}).toList();
}
FutureBuilder Widget
FutureBuilder<List<DropdownMenuItem<String>>>(
future: getDropDownItems(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
}
return DropdownButton<String>(
value: dropdownValue,
items: snapshot.data,
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
i = konzernDataTitle.indexOf(newValue);
titleController.text = konzernDataTitle[i];
linkController.text = konzernDataLink[i];
imageController.text = konzernDataImage[i];
colorController.text =
konzernDataColor[i].toString();
});
},
);
}),
I searched the problems inside the lists but all lists are exactly how they have to be.
So maybe you can help me out. Thanks for any kind of help. All questions will be answered in a few minutes.
^Tim
There should be exactly one item with [DropdownButton]'s value: MyTitle.
Either zero or 2 or more
[DropdownMenuItem]s were detected with the same value
The above error denotes dropdownValue does not match with any value available in dropdown menu item list or more than one value present in dropMenuItemList.
For the first case set dropdownValue = null on initialization and for the second case check menu item doesn't have duplication.
I have a page for item insert and inside it a DropdownButton contains list of categories to select from. Then there is a button beside it you use to add a new category into the dropdown items without reload the page "image below"
dropdown with IconButton beside
The problem that when I add a new category am facing an error as the following "RangeError (index): Invalid value: Not in range 0..10, inclusive: 11".
The error appears for a seconds in the UI "this red error box" before it disappear. My guess is that it happen because the count of the items inside the DropdownButton is changes before the rebuild happen.
I searched for such thing but I can't find any solution plus there is no "count" property for the DropdownButton like the "listView Builder" to help me, all the available solutions is for the listView and not related to the DropdownButton.
code in the dorpdown:
class AndroidDropdown extends StatefulWidget {
final List dropDownData;
final Function getSelectedValue;
//valueFromDB: used for the edit case, when there is already saved value
final int valueFromDB;
AndroidDropdown(
{#required this.dropDownData,
#required this.getSelectedValue,
this.valueFromDB});
#override
_AndroidDropdownState createState() => _AndroidDropdownState();
}
class _AndroidDropdownState extends State<AndroidDropdown> {
String _selectedValue;
int prevValueFromDB;
#override
void initState() {
super.initState();
//check if there a data from the DB (for update reasons)
if (widget.valueFromDB != null) {
int listIndex = widget.valueFromDB - 1;
_selectedValue = widget.dropDownData[listIndex];
widget.getSelectedValue(widget.valueFromDB);
prevValueFromDB = widget.valueFromDB; //to use in checkIfNewValueAdded()
} else {
//make the default selected is the first value
_selectedValue = widget.dropDownData[0];
//assign a default value for the function in case the user didn't use onChange
widget.getSelectedValue(1);
}
}
#override
Widget build(BuildContext context) {
checkIfNewValueAdded();
List<DropdownMenuItem<dynamic>> dropdownItems = [];
//the items loop function
for (var item in widget.dropDownData) {
var newItem = DropdownMenuItem(
child: Text(item),
value: item,
);
dropdownItems.add(newItem);
}
return DropdownButton<dynamic>(
value: _selectedValue, //for initial value
items: dropdownItems,
onChanged: (value) {
setState(() {
_selectedValue = value;
widget.getSelectedValue(widget.dropDownData.indexOf(value) + 1);
//I used this trick to get the ID of the item type that saved in the DB
});
},
);
}
//this to check if there is a new value added in the list
void checkIfNewValueAdded() {
if (widget.valueFromDB != prevValueFromDB) {
setState(() {
int listIndex = widget.valueFromDB - 1;
_selectedValue = widget.dropDownData[listIndex];
prevValueFromDB = widget.valueFromDB;
});
}
}
}