I implemented a DropDown which contains a list of items you can delete.
The Dropdown can't be displayed correctly after deleting the item and that causes the error but i don't know how to fix this. Help is highly appriciated!
The DropDown:
The items are a collection of documents queried from firebase.
Deleting the item removes it from firebase but i get the following error:
This is my code:
var selectedStand;
void deleteStand() {
DocumentReference ref = Firestore.instance
.collection('Standnamen')
.document(selectedStand);
ref.delete();
}
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('Standnamen').snapshots(),
// ignore: missing_return
builder: (context, snapshot) {
if (!snapshot.hasData) {
Text("Loading");
} else {
List<DropdownMenuItem> standItems = [];
for (int i = 0; i < snapshot.data.documents.length; i++) {
DocumentSnapshot snap = snapshot.data.documents[i];
standItems.add(
DropdownMenuItem(
child: Text(
snap.documentID,
style: TextStyle(color: Colors.blue),
),
value: "${snap.documentID}",
)
);
}
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DropdownButton(
items: standItems,
onChanged: (standValue) {
setState(() {
selectedStand = standValue;
});
},
value: selectedStand,
isExpanded: false,
hint: new Text(
"Choose stand to delete",
),
),
)
],
);
}
},
),
],
),
);
}
}
Detailed Error:
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following assertion was thrown building StreamBuilder<QuerySnapshot>(dirty, state: _StreamBuilderBaseState<QuerySnapshot, AsyncSnapshot<QuerySnapshot>>#46c06):
There should be exactly one item with [DropdownButton]'s value: example3.
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
'package:flutter/src/material/dropdown.dart':
Failed assertion: line 827 pos 15: 'items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) {
return item.value == value;
}).length == 1'
The relevant error-causing widget was:
StreamBuilder<QuerySnapshot> file:///Users/darjusch/Developer/flutterProjects/sommerobst_app_beta/lib/screens/admin/admin_create_stand_screen.dart:67:13
When the exception was thrown, this was the stack:
#2 new DropdownButton (package:flutter/src/material/dropdown.dart:827:15)
#3 _AdminCreateStandScreenState.build.<anonymous closure> (package:sommerobst_app_beta/screens/admin/admin_create_stand_screen.dart:92:23)
#4 StreamBuilder.build (package:flutter/src/widgets/async.dart:509:81)
#5 _StreamBuilderBaseState.build (package:flutter/src/widgets/async.dart:127:48)
#6 StatefulElement.build (package:flutter/src/widgets/framework.dart:4619:28)
...
════════════════════════════════════════════════════════════════════════════════════════════════════
W/erobst_app_bet(20965): Reducing the number of considered missed Gc histogram windows from 119 to 100
════════ Exception caught by rendering library ═════════════════════════════════════════════════════
A RenderFlex overflowed by 99569 pixels on the bottom.
EDIT:
I tried your suggestion which sounds very logical but it did not work i still get the same error.
var selectedDoc;
DropdownButton(
items: standItems,
onChanged: (standValue) {
setState(() {
selectedStand = standValue;
selectedDoc = snapshot.data.documents.firstWhere(
(doc) => doc.documentID == selectedStand,
orElse: () => null,
);
});
},
value: selectedDoc?.documentID,
After deletion DropdownButton is given a value(selectedStand) that none of the DropdownMenuItems contain. So, first check if a document exists whose id is selectedStand otherwise set value to null.
// get the document with id as selectedStand. Will be null if it doesn't exist.
var selectedDoc = snapshot.data.documents.firstWhere(
(doc) => doc.documentID == selectedStand,
orElse: () => null,
);
DropdownButton(
// assign selectedDoc's id (same as selectedStand) if exists
// otherwise null
value = selectedDoc?.documentID,
// ...
),
The logic should not be in onChanged but outside of DropdownButton within the StreamBuilder.
selectedDoc = snapshot.data.documents.firstWhere(
(doc) => doc.documentID == selectedStand,
orElse: () => null,
);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DropdownButton(
items: standItems,
onChanged: (standValue) {
setState(() {
selectedStand = standValue;
});
},
value: selectedDoc?.documentID,
isExpanded: false,
hint: new Text(
"Choose stand to delete"
),
),
],
),
Alternatively you could set selectedStand = selectedDoc?.documentID right after finding selectedDoc, so that selectedStand will always have a valid value.
I guess the problem is that you are adding to the same variable without clearing it first which cause multiple element of items to be there with the same names. So I would suggest clear out the items in standItems right before you call the add function to add items outside the for loop.
Related
I want to get and display items name from list in a list of Category widgets
when user click on category name I should get items and display it under category name as in image.
So, I want to add items to List of String in a larger List
I tried alot in initiation the List and tried alot in adding or assigning value one by one but i faild to do it.
I am asking how to declare initiate and use variable List<List< String>>, thats all, or if there is another way to have List for each category.
this is a simple code:
List<List<String>> itemsList = [[]];
Container(
child: ListView.builder(
itemCount: itemsCategories.length ,
itemBuilder: (context, index) {
return Card(
child: Column(
children: [
InkWell(
onTap:() async{
List<String> tempList = await getItems();
// add to list of list
for (var element in temp) { itemsList [index].add(element); }
print( 'tempList[index] items: ${tempList[index]}');
}
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextitemsCategories[index].name),
),
),
],
),
);
}
),
)
I tried another way to assign data to list
// assign value one by one
for (var i = 0; i < tempList.length; i++) {
itemsList[index][i]= tempList [i];
print( itemsList[index][i]);
}
I got different errors like
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: RangeError (index): Invalid value: Valid value range is empty: 0
E/flutter ( 8581): #0 List.[] (dart:core-patch/growable_array.dart:264:36)
E/flutter ( 8581): #1 _HomeState.build..
I'm making favorite list which contains the user favorite journeys using provider then I will display these journeys in the favorite journeys screen.
Favorite.dart:
import 'package:flutter/material.dart';
class Favorite extends ChangeNotifier {
final Text date;
final Text time;
final Text source;
final Text destination;
final Text price;
Favorite(this.date, this.time, this.source, this.destination, this.price);
}
class Following extends ChangeNotifier {
List<Favorite> list = [];
add(favorite) {
list.add(favorite);
notifyListeners();
}
remove(favorite) {
list.remove(favorite);
notifyListeners();
}
}
journeys.dart (Which shows all journeys):
FirebaseAnimatedList(
shrinkWrap: true,
query: Consts.journeyRef.child("journeys"),
itemBuilder: (BuildContext context, DataSnapshot snapshot,
Animation animation, int index) {
try {
return Consumer<Following>(
builder:
(BuildContext context, value, child) {
return Dismissible(
key: UniqueKey(),
secondaryBackground: buildSwipeActionRight(),
background: buildSwipeActionLeft(),
child: ListView(
scrollDirection: Axis.vertical,
shrinkWrap: true,
children: <Widget>[
eachTile(
following,
Favorite(
Text(Map<String, dynamic>.from(
snapshot.value as Map)[Consts.pathDateJourney]),
Text(Map<String, dynamic>.from(
snapshot.value as Map)[Consts.pathTimeJourney]),
Text(Map<String, dynamic>.from(
snapshot.value as Map)[Consts.pathSourceCity]),
Text(Map<String, dynamic>.from(
snapshot.value as Map)[Consts.pathDestinationCity]),
Text(Map<String, dynamic>.from(
snapshot.value as Map)[Consts.pathPriceJourney]),),)
]),
onDismissed: (direction) =>
dismissItem(context, index, direction),
);
},
);
} catch (e) {
customSnackBar(context, e.toString(), 3, Colors.white24, Colors.brown, 17);
return const Text(
"We Can't show you information disabled by the Administrator");
}
}),
eachTile.dart:
ListTile eachTile(following, favorite) {
return ListTile(
leading: Column(
children: [
favorite.date,
const SizedBox(
height: 10,
),
favorite.time,
],
),
title: Row(
children: [
favorite.source,
const SizedBox(
width: 50,
),
favorite.price
],
),
subtitle: favorite.destination,
trailing: IconButton(
icon: following.list.contains(favorite)
? const Icon(Icons.favorite)
: const Icon(Icons.favorite_border_outlined),
onPressed: () {
print(following.list.contains(favorite));
// this print statement always false
if (following.list.contains(favorite)) {
following.remove(favorite);
} else {
following.add(favorite);
}
print(following.list.contains(favorite));
// this print statement always true
print(following.list);
// this print statement print the list and in each time the code execute new instance added to the list
},
),
);
}
This code is working fine as adding the journey to the list but the Problem is that when you click on the favorite icon again the condition
following.list.contains(favorite)
is returning false (Which means this object is not in the list but that's wrong I try to print the list and there is an instance) it seem that the following instance changed but I didn't create any new instance I think it is creating new different instance in each time.
What is the best way to add and remove items to the favorite list using provider?
the output:
I/flutter ( 578): false
I/flutter ( 578): true
I/flutter ( 578): [Instance of 'Favorite']
V/AutofillManager( 578): requestHideFillUi(null): anchor = null
I/flutter ( 578): false
I/flutter ( 578): true
I/flutter ( 578): [Instance of 'Favorite', Instance of 'Favorite']
V/AutofillManager( 578): requestHideFillUi(null): anchor = null
I/flutter ( 578): false
I/flutter ( 578): true
I/flutter ( 578): [Instance of 'Favorite', Instance of 'Favorite', Instance of 'Favorite']
first clean the code here in journey.dart you are using both the methods of provider you can archive this task by
1.you can use consumer widget or provider.of(context) but you are using both ways to call only Following provider
2.in journey.dart if you decided to use consumer then in (BuildContext context, Following value, Widget? child) you can use value and child directly no need to write data types in front eg.. (BuildContext context, value, child)
3.can you display your console output
The problem was that I'm making equal-to operator between two object and it's returning false that is because all objects in the dart language except the primitive data types like string, int, and double... are not equal to each other since no == operator is overridden and set to it, and this include also collections like lists, maps...
the solution is to override the == operator in Favorite class:
class Favorite{
final Text date;
final Text time;
final Text source;
final Text destination;
final Text price;
Favorite(this.date, this.time, this.source, this.destination, this.price);#override
bool operator == (Object other) {
return other is Favorite && date.toString() == other.date.toString() && time.toString() == other.time.toString() && source.toString() == other.source.toString() && destination.toString() == other.destination.toString() && price.toString() == other.price.toString();
}
}
now when I run following.list.contains(favorite) or following.list[0]==favorite in listTile Widget it will return true.
and that's it
Every time you make a change, you should call notifyListeners();. An example Implementation would be:
Inside your Provider Class:
void add(int n) {
myList.add(n);
notifyListeners();
}
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.
Hello I'm new to flutter and I'm trying to create multiple DropdownButtons based on different documents in my Firebase backend. Since there may be multiple DropdownButton (different documents have different number of dropdowns to be created), I wanted to maintain there values with List<String> formInput (formInput[0] maintains the first DropdownButton value etc).
But I keep encountering this error, and came to the conclusion that it is was the formInput[i] fault.
So my question is why can't I use formInput[i] as a value for a Dropdownbutton? Is formInput[i] not considered one item? And is there a better solution for maintaining unknown number of values?
Many Thanks
Code:
class _SubmissionFormState extends State<SubmissionForm> {
List<String> formInput;
var test;
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return FutureBuilder(
future: getStringFromPref('site'),
builder: (context, snapshot) {
if (snapshot.hasData) {
String site = snapshot.data;
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('form')
.where('name', isEqualTo: site)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
int inputSize = snapshot.data.documents.length;
List<Widget> listOfWidget = [];
formInput = new List<String>.filled(inputSize, '-');
print(formInput.length);
for (int i = 0; i < inputSize; i++) {
DocumentSnapshot snap = snapshot.data.documents[i];
List<DropdownMenuItem> options = [];
// adding drop down menu items
for (int j = 0; j < snap['formVal'].length; j++) {
String formVal = snap['formVal'][j];
options.add(DropdownMenuItem(
child: Text(
formVal,
),
value: formVal,
));
print('[inner loop] for loop $i ' + snap['formVal'][j]);
}
// the list of DropdownButtons
listOfWidget.add(DropdownButton(
value: formInput[i],
items: options,
hint: Text(
"-",
),
onChanged: (value) {
setState(() {
formInput[i] = value;
});
},
));
// listOfWidget.add(Text(snap['formTxt']));
}
return Column(
children: listOfWidget,
);
} else {
return CircularProgressIndicator();
}
});
} else {
return CircularProgressIndicator();
}
},
);
}
}
Error:
I/flutter (30860): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (30860): The following assertion was thrown building StreamBuilder<QuerySnapshot>(dirty, state:
I/flutter (30860): _StreamBuilderBaseState<QuerySnapshot, AsyncSnapshot<QuerySnapshot>>#60d57):
I/flutter (30860): There should be exactly one item with [DropdownButton]'s value: -.
I/flutter (30860): Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
I/flutter (30860): 'package:flutter/src/material/dropdown.dart':
I/flutter (30860): Failed assertion: line 839 pos 15: 'items == null || items.isEmpty || value == null ||
I/flutter (30860): items.where((DropdownMenuItem<T> item) {
I/flutter (30860): return item.value == value;
I/flutter (30860): }).length == 1'
I/flutter (30860):
I am trying to delete my data from a database, and proceed to navigate to my homepage if it succeeds.
Below are my code:
StatelessWidget that consist of deleteFromDatabase method which passed an Id(String), an a context:
Consumer<SettingsProvider>(
builder: (context, settingsProvider, child) {
final exerciseSettings = settingsProvider.findById(id);
if (exerciseSettings == null) {
return Center(
child: CircularProgressIndicator(),
);
}
return PreviewExerciseItem(
exerciseSettings: exerciseSettings,
id: id,
navigateToEdit: () =>
_navigateToEditPage(exerciseSettings, context),
deleteFromDatabase: () => _deleteFromDatabase(id, context),
navigateToCountDown: () =>
navigateToCountDownPage(exerciseSettings, context),
);
},
),
_deleteFromDatabase method called from StatelessWidget and shows an AlertDialog to confirm deletion:
void _deleteFromDatabase(String id, context) async {
await showDialog(
context: context,
builder: (context) => new AlertDialog(
title: new Text("Are you sure you want to delete?"),
actions: <Widget>[
new FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: new Text('No'),
),
new FlatButton(
onPressed: () async {
try {
Navigator.of(context).pop(true);
await Provider.of<SettingsProvider>(context, listen: false)
.deleteFromList(id);
Navigator
.pushNamedAndRemoveUntil(context,HomePage.routeName, (Route route) => route.isFirst);
} catch (e) {
print(e);
}
},
child: new Text('Yes'),
),
],
),
);
}
deleteFromList method From My Provider class:
Future<void> deleteFromList(String id) async{
try{
final _itemIndex = _items.indexWhere((item) => item.id == id);
await _databaseHelper.deleteExercises(id);
_items.removeAt(_itemIndex);
notifyListeners();
}catch(e){
print(e);
}
}
findById from Provider Class:
CustomExercise findById(String id) {
return _items.firstWhere((prod) => prod.id == id);
}
Note: I am able to delete my data successfully from my database, however right before it navigates to my HomePage, an error pops out for a split second as a form of Red Screen: Bad State: No Element
Below are the full error message from my Log:
The following StateError was thrown building Consumer(dirty, dependencies: [_InheritedProviderScope, _InheritedTheme, _LocalizationsScope-[GlobalKey#5ce12]]):
Bad state: No element
The relevant error-causing widget was:
Consumer<SettingsProvider>
When the exception was thrown, this was the stack:
#0 ListMixin.firstWhere (dart:collection/list.dart:150:5)
#1 SettingsProvider.findById (package:workoutapp/providers/settings_provider.dart:12:19)
#2 PreviewExercisePage.build.<anonymous closure> (package:workoutapp/pages/preview_exercise_page.dart:68:55)
#3 Consumer.buildWithChild (package:provider/src/consumer.dart:175:19)
#4 SingleChildStatelessWidget.build (package:nested/nested.dart:260:41)
This is happens when the list is empty or maybe the first element is empty, so you should check the list is not empty.
List list = [];
print(list[0])
is sure you'll receive like this message:
Unhandled exception:
Bad state: No element
#0 List.first (dart:core-patch/growable_array.dart:332:5)
#1 main (file:///C:/Users/onbody/AndroidStudioProjects/nmae/bin/name.dart:9:14)
#2 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:281:32)
#3 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
the solution is:
List list = [];
if (list.isNotEmpty) {
print(list[0]);
} else {
print('the list is empty'!);
}
I hope this is helpful for someone Thanks!
As previously mentioned in the comments, it's likely that checking the values of an empty List causes the error. A workaround for this is to have a checker if the List is empty on both CustomExercise findById(String) and deleteFromList(String).
i.e.
if(_items != null && _items.length > 0)
We were using Drift (formerly Moor) and its watchSingle() method. That will throw this error, if no matching database row is found.
It was very hard to track down since a stream was emitting the error and it had no stack trace attached to it.
The fix was to use watch() with limit(1) instead and skip processing if the result is empty.