Read list of items from firebase database flutter - firebase

I'm trying to build a list of items from this database:
but i'm getting this error:
_TypeError (type 'List<dynamic>' is not a subtype of type 'List<DataSnapshot>')
i can get the item values just using snap2.value but i need to get the element key because it's my item id, to build my list.
import 'package:firebase_database/firebase_database.dart';
final DatabaseReference _refdata =
FirebaseDatabase.instance.reference().child('host');
getItems() async {
String _refOnline;
await _refdata.child("ref").once().then((value) => _refOnline = value.value);
if (dataRef != _refOnline) {
await _refdata.child("values/valores").once().then((DataSnapshot snap2) {
List<DataSnapshot> result = snap2.value;
lista = result
.map((element) => Item(
element.key,
element.value["nome"],
element.value["preco"].toDouble(),
element.value["precoantes"] ?? "",
element.value["tipo"],
element.value["disponivel"],
element.value["descricao"],
element.value["calorias"]))
.toList();
});
edit:
with this change im able to return values:
List<Map<dynamic, dynamic>> result =
List<Map<dynamic, dynamic>>.from(snap2.value);
result.forEach((element) {
if (element != null) {
print(element);
}
});
but i cant return the keys (1,2,3,4,5)
and it's the same as doing this(suposing query ordered by keys):
List<dynamic> result = snap2.value;
int _i = 1;
result.forEach((value) {
if (value != null) {
lista.add(Item(
_i.toString(),
value["nome"],
value["preco"].toDouble(),
value["precoantes"] ?? "",
value["tipo"],
value["disponivel"],
value["descricao"],
value["calorias"]));
_i += 1;
print(value["nome"]);
print(value);
print(lista.length);
}
and now im getting this error:
NoSuchMethodError (NoSuchMethodError: The method 'add' was called on null.
Receiver: null
Tried calling: add(Instance of 'Item'))

There is no way in FlutterFire to get the child nodes as a list of DataSnapshot objects.
The closest I got was:
currentRoundListener = dbRoot.child('rounds/$currentRoundKey').once.then((snapshot) {
currentRound = List<String>.from(snapshot.value as List<dynamic>);
});
You could give this a try with:
List<DataSnapshot> result = List<DataSnashot>.from(snap2.value as List<dynamic>);
But more likely the values under the snapshot will only be available as a map:
Map<String, dynamic> result = Map<String, dynamic>.from(snap2.value as Map<dynamic, dynamic>);
If you need to maintain the order of the child nodes, have a look here: Flutter: Firebase Real-Time database orderByChild has no impact on query result and here: Flutter Firebase Database wrong timestamp order

I've managed to solve it using this implementation:
getItems() async {
String _refOnline;
List<dynamic> result;
await _refdata.child("ref").once().then((value) => _refOnline = value.value);
if (dataRef != _refOnline) {
await _refdata.child("values/valores").once().then((DataSnapshot snap2) {
result = snap2.value;
int _i = 1;
result.forEach((value) {
if (value != null) {
Item _a = Item(
_i.toString(),
value["nome"],
value["preco"].toDouble(),
value["precoantes"] ?? "",
value["tipo"],
value["disponivel"],
value["descricao"],
value["calorias"]);
lista.add(_a);
_i += 1;
}
});
savepref("ref_key", _refOnline);
});
} else {
lista = [oi, oi2, oi3, oi4, oi5, oi6, oi7, oi8, oi9, oi10, oi11, oi12];
//readIt();
}
}
there was a problem on lista definition blocking it to receive new items in lista.add function.
I've now defined lista as:
List<Item> lista = [];
And with the above function, everything now works.
thx Frank van Puffelen !

Related

I am getting "Bad state: No element" when using firstwhere in Flutter List even when the data exists

Found a few similar questions but my scenario is totally different.
My Plans file :
class Plans {
FirebaseFirestore db = FirebaseFirestore.instance;
List<Plan> plans = [];
Plan findByID(String id) {
return plans.firstWhere(
(plan) => plan.id == id,
);
}
Future<List> getPlans() async {
await db.collection('Plans').get().then(
(QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach(
(individualPlan) {
String _id = individualPlan.id;
List _tags = [];
for (var i = 0; i < 4; i++) {
_tags.add(individualPlan["Tags"][i]);
}
final newPlan = Plan(
id: _id,
name: individualPlan["PlanName"],
price: individualPlan["PlanPrice"],
validity: individualPlan["PlanValidity"],
tags: _tags,
);
plans.add(newPlan);
},
);
//foreach ends here
String _id = plans[0].id;
print(findByID(_id).name);
print(findByID(_id).price);
},
);
return plans;
}
}
Here, String _id = plans[0].id; returns an item just fine and the print statements following work very well.
I call the same findById in my details screen as follows :
Plan selectedPlan = _plansService.findByID(id);
This is how I get my id from the listing page to my details screen:
String id = ModalRoute.of(context)!.settings.arguments as String;
print(id);
Here id is not empty and the print statment prints an id that is passed.
I only get the Bad State: no element in the details page while there is an id that is passed to it. I am pretty much doing the same as I am doing in the Plans class but it just wouldn't work.

Error: Expected a value of type 'int', but got one of type 'String'; flutter

I am trying to fetch quizzes data from realtime database using http mathod "GET".
The data is being retreived but it's not showing up in listview and when i print the length of my list it's 0. And this is what the error is being shown in my terminal: Error: Expected a value of type 'int', but got one of type 'String'
I can't figure out what the problem is. Please help me solve this problem because i am trying for around 5 days but can't solve it.
Thank you.
These are my codes for fetching the data in lists.
Future<void> getList() async {
list.clear();
final url = Uri.parse(
'https://testisnotesttheisthelearningapp-default-rtdb.firebaseio.com/Schools/${widget.uid}/ResultsFolder/${widget.id}.json');
final response = await get(url);
print(response.body);
var map = json.decode(response.body) as Map<String, dynamic>;
// print(map);
if (map != null) {
map.forEach((key, value) {
print(value['Name']);
var temp = Result(
value['Percent'],
(value['choice'] as List<dynamic>)
.map((e) => Model(e['index'], e['question'], []))
.toList(),
value['Score'],
(value['question'] as List<dynamic>).map((e) => e).toList(),
value['Name']);
list.add(temp);
});
}
}
This is my data from real-time database in JSON format:
{
"Name" : "Miets Digital",
"Percent" : 100,
"Score" : 2,
"choice" : [ "Correct", "Correct" ],
"question" : [ {
"question" : "WHo is Elon Musk?"
}, {
"question" : "How did Elon musk got rich?"
} ]
}
Try this:-
Future<void> getList() async {
list.clear();
final url = Uri.parse(
'https://testisnotesttheisthelearningapp-default-rtdb.firebaseio.com/Schools/${widget.uid}/ResultsFolder/${widget.id}.json');
final response = await get(url);
print(response.body);
var map = json.decode(response.body) as Map<String, dynamic>;
// print(map);
if (map != null) {
print(map['Name']);
var temp = Result(
map['Percent'],
(map['choice'] as List<dynamic>)
.map((e) => Model(e['index'], e['question'], []))
.toList(),
map['Score'],
(map['question'] as List<dynamic>).map((e) => e).toList(),
map['Name']);
list.add(temp);
}
}
forEach() loop goes through each key value pair in a map. The variables key and value gives you the key and the value the loop is currently working on. If you want to access the values of a map you will have to use the syntax map['key_name'].
This also explains the error, as your Result() constructor is expecting an integer but is getting a string value.
It's not a good practice to parse JSON maps. You just call over the model class
Try to follow the below code
Future<QuizModelResponse> getList() async {
// here write your code
var map = json.decode(response.body);
return QuizModelResponse.fromJsonMap(map);
}
List Response class
class QuizModelResponse {
List<QuizItemModel> content;
QuizModelResponse.fromJsonMap(dynamic data) :
content = List<QuizItemModel>.from(
data.map((it) => QuizItemModel.fromJson(it)));
}
Model class
class QuizItemModel {
String name;
int percent;
int score;
List<String> choice;
List<Question> question;
QuizItemModel(
{this.name, this.percent, this.score, this.choice, this.question});
QuizItemModel.fromJson(Map<String, dynamic> json) {
name = json['Name'];
percent = json['Percent'];
score = json['Score'];
choice = json['choice'].cast<String>();
if (json['question'] != null) {
question = new List<Question>();
json['question'].forEach((v) {
question.add(new Question.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['Name'] = this.name;
data['Percent'] = this.percent;
data['Score'] = this.score;
data['choice'] = this.choice;
if (this.question != null) {
data['question'] = this.question.map((v) => v.toJson()).toList();
}
return data;
}
}
class Question {
String question;
Question({this.question});
Question.fromJson(Map<String, dynamic> json) {
question = json['question'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['question'] = this.question;
return data;
}
}

Flutter How to group lists from data in a SQLite database?

I am newbie, and I am using package grouped_list to group the list with data from the SQLite database, grouped_list works fine with an existing list just like in its example, but when I replace it equals list with data retrieved from SQLite database then it doesn't work and I get error:
The following NoSuchMethodError was thrown building GroupedListView<dynamic, String>(dirty, state: _GroupedListViewState<dynamic, String>#c2ba2):
Class 'DrinkDatabase' has no instance method '[]'.
Receiver: Instance of 'DrinkDatabase'
Tried calling: []("drinkGroup")
Below is my code. If you need more information please let me know, please help, thank you!
GroupedListView<dynamic, String>(
shrinkWrap: true,
elements: homeController.drinkList,
groupBy: (element) => element['drinkGroup'],
groupComparator: (value1, value2) => value2.compareTo(value1),
itemComparator: (item1, item2) =>
item1['drinkDateTime'].compareTo(item2['drinkDateTime']),
order: GroupedListOrder.DESC,
useStickyGroupSeparators: true,
groupSeparatorBuilder: (String value) =>
Container(),
itemBuilder: (c, element) {
return Container();
},
)
This is how I declare the list:
List drinkList = List<DrinkDatabase>();
And class DrinkDatabase:
class DrinkDatabase {
int drinkId;
int drinkVolume;
String drinkGroup;
String drinkCategory;
String drinkDateTime;
DrinkDatabase({
this.drinkId,
this.drinkVolume,
this.drinkGroup,
this.drinkCategory,
this.drinkDateTime,
});
Map<String, dynamic> toMap() {
return {
"drinkId": drinkId,
"drinkVolume": drinkVolume,
"drinkGroup": drinkGroup,
"drinkCategory": drinkCategory,
"drinkDateTime": drinkDateTime,
};
}
#override
String toString() {
return 'drinkTable('
'"drinkId": ${this.drinkId}, '
'"drinkVolume": ${this.drinkVolume}, '
'"drinkGroup": ${this.drinkGroup}, '
'"drinkCategory": ${this.drinkCategory}, '
'"drinkDateTime": ${this.drinkDateTime}, ';
}
}
What I did and it worked for me
The way to declare the list
List drinkList = [];
Then replace the line
elements: homeController.drinkList,
with
elements: drinkList,
To build the list from the database query result list
Future<void> getSampleList() async {
drinkList.clear();
List<Map<String, dynamic>> result =
await DrinkDatabaseHelper.instance.queryAll();
print('result= $result');
var map = new Map<String, dynamic>();
for (int i = 0; i < result.length; i++) {
map['drinkId'] = result[i]['drinkId'].toString();
map['drinkVolume'] = result[i]['drinkVolume'].toString();
map['drinkGroup'] = result[i]['drinkGroup'];
map['drinkCategory'] = result[i]['drinkCategory'];
map['drinkDateTime'] = result[i]['drinkDateTime'];
drinkList.add(Map<dynamic, String>.from(map));
}
In DrinkDatabaseHelper
Future<List<Map<String, dynamic>>> queryAll() async {
Database db = await instance.database;
return await db.query(_tableName);
}
If you have more questions let me know

Flutter async programming

After using a dataset I want to check how many datasets are unused. If I'm above the threshold I want to fetch new data.
useQuestion(Question question) async {
print("using question $question");
question.used=1;
final db = await database;
int count = await db.rawUpdate(
'UPDATE Question SET used = ? WHERE question = ?',
[question.used,question.question]);
print(question);
print("Made $count changes");
var questions = await _checkQuestionThreshold();
print(questions);
for (var q in questions) {
newQuestion(Question.fromJson(q));
}
}
Check Threshold
_checkQuestionThreshold() async {
print("count questions...");
final db = await database;
var res = await db.query("Question");
int count = Sqflite.firstIntValue(
await db.rawQuery('SELECT COUNT(*) FROM Question'));
int countUsed = Sqflite.firstIntValue(
await db.rawQuery('SELECT COUNT(*) FROM Question where used="1"'));
int i = 0;
if (count < 1 || (countUsed / count) < 0.5) {
print("Okay... we fetch new...");
return await _fetchFromFirebase();
}
Fetching from DB:
_fetchFromFirebase() async {
var questionJson;
databaseReference.once().then((DataSnapshot snapshot) async {
questionJson = await snapshot.value;
}).catchError((e) {
print(e);
});
return questionJson;
}
However I get the following error when calling for (var q in questions) {
newQuestion(Question.fromJson(q));
} and I'm wondering what exactly I am missing.
I/flutter ( 5150): count questions...
I/flutter ( 5150): Okay... we fetch new...
I/flutter ( 5150): null
E/flutter ( 5150): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: NoSuchMethodError: The getter 'iterator' was called on null.
E/flutter ( 5150): Receiver: null
Your issue is that questions is null, so trying to iterate over it is going to throw an error.
Looking at your code, the root of the error seems to come from your _fetchFromFirebase method. In this case, you call databaseReference.once(), and in the then part you assign the result to questionJson. However, you never await on this call, so the _fetchFromFirebase method ends up returning the value of questionJson immediately after the call is made without waiting for it to finish. At that point, questionJson will be null, so that's what gets returned.
In general, I advise to not mix the Future.then.catchError pattern with the async/await pattern, as it can result in confusing logic that hides what is actually happening. As such, I'd recommend sticking just to async/await like so:
_fetchFromFirebase() async {
try {
final snapshot = await databaseReference.once();
final questionJson = await snapshot.value;
return questionJson;
} catch (e) {
print(e);
return null;
}
}

Flutter Firestore query with startAfter

I am using Flutter (cloud_firestore) and trying to get data from Firestore after document with title 'xxx', but it returns 0 results.
return Firestore.instance.collection('products')
.orderBy('title')
.startAfter([{'title': 'xxx'}
]);
What am I doing wrong? How can I properly implement flutter pagination?
You should pass a value, not a map:
return Firestore.instance.collection('products')
.orderBy('title')
.startAfter(['xxx']);
The documentation on this is not particularly clear.
This is what worked for me. My orderBy is based on {FirstName, LastName & Email}. So, the startAfter should also match these field values. I saved the document in a variable and later made a list with the values of those fields and used as startAfter parameter.
static final Query queryBase = userCollectionRef
.orderBy(FieldNames.FIRST_NAME)
.orderBy(FieldNames.LAST_NAME)
.orderBy(FieldNames.EMAIL);
DocumentSnapshot _lastUser;
Future<List<DocumentSnapshot>> getAllUserDocuments({
bool next,
int limit = 10,
}) async {
List<DocumentSnapshot> _userDocSnaps = [];
try {
if (!next) _lastUser = null;
Query _query = queryBase;
if (_lastUser != null)
_query = _query.startAfter([
_lastUser.data[FieldNames.FIRST_NAME],
_lastUser.data[FieldNames.LAST_NAME],
_lastUser.data[FieldNames.EMAIL],
]);
var _userDocsSnap = await _query.limit(limit).getDocuments();
if (_userDocsSnap != null && _userDocsSnap.documents != null) {
_userDocSnaps = _userDocsSnap.documents;
if (_userDocSnaps != null && _userDocSnaps.length >= 1)
_lastUser = _userDocSnaps[_userDocSnaps.length - 1];
}
} catch (err) {
String errMessage = 'Exception in method _getAllUserDocuments';
PrintHelper.handlePrint(errMessage, err);
}
return _userDocSnaps;
}
You can pass entire doc using startAfterDocument method
_db
.collection('requirement')
.orderBy('createdAt', descending: true)
.startAfterDocument(lastVisible)

Resources