Map getter not pulling items by index properly - flutter

I have a Map of <String, CartItem> which is a custom class, and I am trying to return specific values from that Map, but every time I do return them in the format item[I], I get a "Null check operator used on a null value." Is there a different way I should be calling this?
Details
I have a map of _items with a public getter that returns those items:
class Cart with ChangeNotifier {
Map<String, CartItem> _items = {};
Map<String, CartItem> get items {
return {..._items};
}
Followed by a function that adds new items to the Map:
void addItem(
String productId,
double price,
String title,
) {
if (_items.containsKey(productId)) {
_items.update(
productId,
(existingCartItem) => CartItem(
id: existingCartItem.id,
title: existingCartItem.title,
price: existingCartItem.price,
quantity: (existingCartItem.quantity + 1),
),
);
} else {
_items.putIfAbsent(
productId,
() => CartItem(
id: DateTime.now().toString(),
title: title,
price: price,
quantity: 1,
),
);
}
print(items.length);
print(items[0]);
print(items);
notifyListeners();
}
When I call addItem, this is the output I get from those three print statements - it appears calling as items[0] returns null, even though the length and the full items list print properly:
Performing hot restart...
Restarted application in 1,483ms.
flutter: 1
flutter: null
flutter: {p1: Instance of 'CartItem'}
Then whenever I call that Cart Provider looking for a specific item in the Map:
child: ListView.builder(
itemCount: cart.itemCount,
itemBuilder: (ctx, i) => CartItem(cart.items[i]!)),
)
I get a "Null check operator used on a null value" error. Why is this way of calling the items in the map returning null?

You seem to assume that items[0] works like an array and returns the first element. It does not. A Map has an indexer by key. It returns the item, with the given key. That is the whole point of a map, being able to look up items by key. You set your productId as a key, so to find it you would need to use items[productId].
Some thoughts: does your cart items needs to be a map? If all you want is enumerating through it, it would work if you made it a simple list instead. Then the numeric indexer would work again.
If it needs to be a map, you can use items.values to iterate through all the values. But please note that a map is not ordered. Again, if you need an ordered list, it would be better to use a list in the first place.

Related

Flutter firebase: Bad state: field does not exist within the DocumentSnapshotPlatform

I'm getting this error:
Bad state: field does not exist within the DocumentSnapshotPlatform
with the following code:
static List<Report?> reportListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map<Report?>((report) {
return Report(
type: report['type'],
reason: report['reason'],
reportId: report['id'],
chat:
(report['chat'] == null) ? null : Chat.chatFromMap(report['chat']),
stingray: Stingray.stingrayFromDynamic(report['stingray']),
reporterUser: User.fromDynamic(report['reporterUser']),
reportTime: report['reportTime'].toDate(),
);
}).toList();
}
Its failing on the first map,
type: report['type'],
and when i look at it in debug mode, it shows the data i am looking for:
As you can see from the screenshot, 'type' exists with a value of 'chat report'. Any idea why this is breaking?
Thanks!
You are supposed to call .data() on report
static List<Report> reportListFromSnapshot(QuerySnapshot snapshot) {
final List<Report> reports = [];
for (final doc in snapshot.docs) {
final report = doc.data() as Map<String, dynamic>?; // you missed this line.
if (report == null) continue;
reports.push(
Report(
type: report['type'] as String,
reason: report['reason'] as String,
reportId: report['id'] as String,
chat: (report['chat'] == null)
? null
: Chat.chatFromMap(report['chat']),
stingray: Stingray.stingrayFromDynamic(report['stingray']),
reporterUser: User.fromDynamic(report['reporterUser']),
reportTime: (report['reportTime'] as Timestamp).toDate(),
),
);
}
return reports;
}
// I returned List<Report> not List<Report?>
Check out this link on how to use withConverter so you do not have to manually convert models.
The problem turned out to be larger in scope than i thought. I was uploading a map object to firebase rather than just a document, and that map was attempting to be read, which is where the error occurred due to it only having one value in the document snapshot.
Maybe you have typo in your field which does not match with your model fields. So the idea is that, your fields in your model has to match with the fields in your firebase snapshot fields.
enter link description here

How can I gather and show results of a survey made with survey_kit package in Flutter?

I am struggling in retrieving the results obtained from a survey built with survey_kit package. A SurveyResult object is supposed to contain a list of StepResults and the FinishReason. The StepResult contains a list of QuestionResults. I cannot access the StepResult in anyway.
Example proposed in the documentation:
SurveyKit
(
onResult: (SurveyResult result) {
//Read finish reason from result (result.finishReason)
//and evaluate the results }
)
I already tried to tap something like result.stepResult but no variable was suggested.
I'm not sure if its the best approach but after looking into SurveyKit I've found that the SurveyResult class holds a list of StepResult and each of them holds a list of QuestionResult which in turn holds a value field called valueIdentifier.
so you can Iterate over them and extract the values. but first, you need to ensure that all of your answers have value. for example:
answerFormat: const SingleChoiceAnswerFormat(
textChoices: [
TextChoice(text: 'option 1', value: '10'),
TextChoice(text: 'option 2', value: '-2'),
]
),
Then after you did that iterate over them like that:
onResult: (SurveyResult result) {
int score = 0;
for (var stepResult in result.results) {
for (var questionResultin stepResult.results) {
score += int.tryParse(questionResult.valueIdentifier ?? "0") ?? 0;
}
}
print("final Score is $score");
/* call a new widget to show the results*/
},
As you can see I use two null checks when adding a question value to the score.
The first one is because the value is of type "String?" (and not "String") and the second one is because the String value might be non-numeric.
Of course, after you have calculated the results you can show the result on a new screen with Navigator.

A value of type 'Iterable<Type>' can't be returned from the method 'genereMovies' because it has a return type of 'List<Type>'

I want to separate list of movies based on their geneType
List<Movie> genereMovies(String gene) {
return movie.where((mov) => mov.geneType.contains(gene));
}
I have this getter that will find and return Object Movie which contains that gene
my object of movie looks like this
Movie(
id: 'm3',
title: 'Golmaal 2',
description: 'Best comedy movie by actor Ajay devgan',
rating: '4.0/5',
imageUrl:
'https://pixabay.com/get/g2367e312e5b444252dd1639ba9f27edfa04e269538d22d4a5f2e843639b0f535f61d3350bd66de32d1024443ed6ee872a11db93c9ddc14035a7e73a82a4e359ab8344f145d4097cf1be4f9ee642acaf7_1920.jpg',
geneType: ['comedy', 'action']),
geneType takes a list of Strings
The method where returns an Iterable. That's why you get the error, since you want to return a List but you actually have an Iterable. The class Iterable has the method toList() which creates a List.
List<Movie> genereMovies(String gene) {
return movie.where((mov) => mov.geneType.contains(gene)).toList();
}

Get a list out of map result from a rest call

I get the following response from my NoSql database.
It is of type InternalLinkedHashMap.
// key // value
{
"42514738" : "iohuahges",
"25498724" : "hiuasfhyd",
"24257+62" : "opiasurj",
"35842942" : "ftysadyg",
"98472442" : "iouyaerj"
}
I want to use these values to construct a ListView.
But the above is not a list and I will not be able to know the keys in the above map beforehand.
Thus even though the following would work (String s assignment below), I can't do it in runtime cos I have no idea on what the keys will be.
How can I get an index out of the above result?
FutureBuilder(
future: methodReturningAboveMap(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
// want to use index something like snapshot.data['collection'][0] and get get first key value and so on.
String s = snapshot.data['collection']['42514738']; // will work but can't do it as mentioned above
},
);
},
),
You can extract your data and cast it as a Map<String, dynamic> (for type safety)
Map<String, dynamic> data = snapshot.data['collection'];
Then in your ListView, you can get the index by using
String key = data.keys.elementAt(index);
Then access its value using
String value = data[key];

Get index of list of map from map

How do you get index of list of maps from a map. I tried to use indexOf which works but by the time the position of values are mixed up then it returns -1.
UPDATE: It actually doesn't work even in right order
List<Map<String, dynamic>> list = [{'id':1, 'name':'a'}, {'id':2, 'name':'b'}];
Map<String, dynamic> item = {'name':'a','id':1}; //<- position name and id are in different places
print(list.indexOf(item)); // so it return -1
The best way would be to get index of list where item contains same id ... if you know what I mean... How to do it?
You can use indexWhere instead indexOf.
Map<String, dynamic> item = {'name':'a','id':1};
print(list.indexWhere((i) => i['id'] == item['id'])); // prints 0
Map<String, dynamic> item = {'name':'b','id':2};
print(list.indexWhere((i) => i['id'] == item['id'])); // prints 1