Future<DropDownButton> items empty/null/same values | Flutter - flutter

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.

Related

Possible to update/rebuild open DropdownButtonFormField?

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.

ValueListenableBuilder is not rebuilding the screen, when hotreloading, it is working

I'm trying to build a note app, all data and other things is working perfectly, cos the data is displaying to the screen when the code file is saving, its weird , first time facing this problem
in short, the valuelistanble is not listening when the data adding from app, but when just hot reloading the data is displaying
how can i fix this,
here is the code
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
WidgetsBinding.instance!.addPostFrameCallback((_) async {
final value = await NoteDB.instance.getAllNotes();
});
____________________________________________
____________________________________________
//code line for aligment
Expanded(
child: ValueListenableBuilder(
valueListenable: NoteDB.instance.noteListNotifier,
builder: (context, List<NoteModel> newNotes, _) {
return GridView.count(
childAspectRatio: 3 / 4,
crossAxisCount: 2,
mainAxisSpacing: 34,
crossAxisSpacing: 30,
padding: const EdgeInsets.all(20),
//generating list for all note
children: List.generate(
newNotes.length,
(index) {
//setting the notelist to a variable called [note]
final note = newNotes[index];
if (note.id == null) {
//if the note's id is null set to sizedbox
//the note id never be null
const SizedBox();
}
return NoteItem(
id: note.id!,
//the ?? is the statement (if null)
content: note.content ?? 'No Content',
title: note.title ?? 'No Title',
);
},
),
);
},
)),
here is the NoteDB.instance.getAllNotes(); function
#override
Future<List<NoteModel>> getAllNotes() async {
final _result = await dio.get(url.baseUrl+url.getAllNotes);
if (_result.data != null) {
final noteResponse = GetAllNotes.fromJson(_result.data);
noteListNotifier.value.clear();
noteListNotifier.value.addAll(noteResponse.data.reversed);
noteListNotifier.notifyListeners();
return noteResponse.data;
} else {
noteListNotifier.value.clear();
return [];
}
}
and also there is a page to create note , and when create note button pressed there is only one function calling here is function
Future<void> saveNote() async {
final title = titleController.text;
final content = contentController.text;
final _newNote = NoteModel.create(
id: DateTime.now().millisecondsSinceEpoch.toString(),
title: title,
content: content,
);
final newNote = await NoteDB().createNote(_newNote);
if (newNote != null) {
print('Data Added to the DataBase Succesfully!');
Navigator.of(scaffoldKey.currentContext!).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => HomePage()),
(Route<dynamic> route) => false);
} else {
print('Error caught while data adding to the DataBase');
}
}
everything work fine, but while add the data the UI isn't refreshing even tho notifier is active
and if you need full code please have a look at this github link : https://github.com/Mishalhaneef/Note-app
Since this ValueNotifier has a type of List<NoteModel>, the value will not change when you add new items to the list or delete from it or clear all. The value here is a reference to the list which does not change.
You have to assign a new value to it, like:
noteListNotifier.value = List<NoteModel>[<add your current items here>];
You can manipulate your current list with List.from, removeWhere, add etc., and then re-assign the complete list.
Besides you don't need to call notifyListeners in case of a ValueNotifier, the framework handles it, see here.
Another approach would be to use a custom ChangeNotifierProvider where you can call notifyListeners when the contents of your list are changed.
Some further suggestions:
In your homescreen.dart file, instead of NoteDB.instance.noteListNotifier.value[index] you can use newNotes[index].
In data.dart, within getAllNotes, you have to set a new value for noteListNotifier in order to get the changes propagated. Currently you are just modifying items in this list and that is not considered to be a change. Try this code:
#override
Future<List<NoteModel>> getAllNotes() async {
//patching all data from local server using the url from [Post Man]
final _result = await dio.get(url.baseUrl+url.getAllNotes);
if (_result.data != null) {
//if the result data is not null the rest operation will be operate
//recived data's data decoding to json map
final _resultAsJsonMap = jsonDecode(_result.data);
//and that map converting to dart class and storing to another variable
final getNoteResponse = GetAllNotes.fromJson(_resultAsJsonMap);
noteListNotifier.value = getNoteResponse.data.reversed;
//and returning the class
return getNoteResponse.data;
} else {
noteListNotifier.value = <NoteModel>[];
return [];
}
}

Why my drop down widget occurs an error when include value

Currently I'm trying to implement dropdown using DropdownButton, but there is one problem.
In DropdownButton(), if value: is empty, error is not occurred but nothing change when choose.
if I assign an arbitrary String variable to value:, the following error will occur.
════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building StatefulBuilder(dirty, state: _StatefulBuilderState#65c7c):
Assertion failed:
..\…\material\dropdown.dart:915
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: . \nEither zero or 2 or more [DropdownMenuItem]s were detected with the same value"
My code;
List<String> strings = <String>["Aa", "Bb", "Cc"];
String selectedString = "";
#override
Widget build(BuildContext c) { ....
showDialog( builder: (context) {
return Dialog(builder: (context, setState) {
return SingleChildScrollView(
child: Column( ....
DropdownButton<String>(
value: selectedString, // IF THIS IS NOT NULL, THE ERROR NOT APPEARD
hint: Text("Choose"),
items: strings.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (value) {
setState(() {
selectedString = value!;
});
},
),
........
You have to follow these three steps;
1. List<String> strings = <String>[" ","Aa", "Bb", "Cc"];
2. String selectedString = " ";
3. value: selectedString ,
This was easy to solve.
Flutter: There should be exactly one item with [DropdownButton]'s value
This answer says that selectedString value should be string that exists in strings. Like this
selectedString = "Aa";
You must have at least one value that is contained in the list of items
List strings = [" ","Aa", "Bb", "Cc"];
String selectedString = " "; or "Aa" or "Bb" <******** !!
value: selectedString ,

How can I solve "There should be exactly one item with [DropdownButton]'s value: null." error in Flutter?

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

DropdownMenuItem from SQFLite using Flutter

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?