Displaying a Future List Function Firebase GetX - flutter

I'm trying to create a user feed, just like that of twitter using Firebase & GetX.
In the code snippet is my function..
List<PostModel> postListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((doc) {
return PostModel(
id: doc.id,
text: (doc.data() as dynamic)["text"] ?? "",
creator: (doc.data() as dynamic)["creator"] ?? "",
timestamp: (doc.data() as dynamic)["timestamp"] ?? 0,
);
}).toList();
}
Future<List<PostModel>> getFeed() async {
List<String> usersFollowing = await UserService() //['uid1', 'uid2']
.getUserFollowing(FirebaseAuth.instance.currentUser!.uid);
QuerySnapshot querySnapshot = await FirebaseFirestore.instance.collection("posts").where('creator', whereIn: usersFollowing)
.orderBy('timestamp', descending: true)
.get();
return postListFromSnapshot(querySnapshot);
}
What I want to do is to display the Future function getFeed(), I'm using GetX for state management. So, my problem is how can I display the result of this function using a ListView.Builder()
Here's how I used the Future builder
FutureBuilder(
future: _.listPost,
initialData: [PostModel(id: "2", creator: "Fm", text: "Testing", timestamp: Timestamp.now())],
builder: (BuildContext context, AsyncSnapshot snapshot){
if(snapshot.hasData == null){
return Text("Data is available");
} else{
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.toString().length,
itemBuilder: (context, index){
PostModel posts = snapshot.data[index];
return Column(
children: [
Text(posts.text)
],
);
},
);
}
},
)
And here's the error I got
The following NoSuchMethodError was thrown building:
The method '[]' was called on null.
Receiver: null
Tried calling: [](3)
It also pointed to an error on the
PostModel post line.. the [index] to be precise

First, make your AsyncSnapshot snapshot an AsyncSnapshot<List<PostModel>> snapshot. That is not your primary problem, but it will make things a lot easier to have proper typing and not have to guess around using dynamic.
Your problem is that hasData is a bool. It is either true or false, but never null. I wonder how you got that line past your compiler. Are you using an outdated version of Flutter? You should check this, your compiler is your friend, if it isn't helping you properly, this will be a hard and rocky road.
Anyway, you should check whether there is data, if there is none, you are still waiting:
FutureBuilder(
future: _.listPost,
builder: (BuildContext context, AsyncSnapshot<List<PostModel>> snapshot){
if(!snapshot.hasData){
return CircularProgressIndicator();
} else {
final postList = snapShot.requireData;
return ListView.builder(
shrinkWrap: true,
itemCount: postList .length,
itemBuilder: (context, index){
final post = postList[index];
return Column(
children: [
Text(post.text)
],
);
},
);
}
},
)

Related

A split second error on StreamBuilder Flutter

I'm trying to write full app for the first time as a beginner. I'm using getx in this app too.
I'm taking a split second RangeError (index): Invalid value: Valid value range is empty: 0 while using StreamBuilder. It is working fine after that split second red error page. How I am supposed to deal with this, should I use something related with getx to stream the data?
This is my StreamBuilder:
StreamBuilder(
stream: stream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasData) {
List<KTCardItem> collectionList = filterByTag(snapshot.data!.docs);
return PageView.builder(
controller: PageController(keepPage: true),
scrollDirection: Axis.vertical,
itemCount: collectionList.length,
itemBuilder: (BuildContext context, index) {
Future.delayed(const Duration(seconds: 3));
return NFTCardView(
index: index,
isFavorite: isFavoritedByUser(index),
ktCardItem: collectionList[index],
onFavChanged: () {
onFavoriteChanged(collectionList[index].eventId ?? "", index);
},
);
},
);
} else {
return const Center(child: CircularProgressIndicator());
}
// filter the list by choice of tag
},
);
I'm guessing that cause of this problem is the filterByTag method that I do before the return. filterByTag method:
List<KTCardItem> filterByTag(List<QueryDocumentSnapshot> snapshot) {
List<KTCardItem> collectionList = [];
for (var document in snapshot) {
Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
if (data["tags"].contains(_tag.value)) {
collectionList.add(KTCardItem.fromMap(data));
}
}
return collectionList;
}

The getter 'length' was called on null. Receiver: null Tried calling: length Previous solutions didn't work for me

my question has definitely been asked before here but none of the solutions worked for me, hence i thought I should post my code here. I am building a todo app and Im stuck on the FutureBuilder widget I am getting the following error when running the code below. I have tried adding "AsyncSnapshot" in "builder: (context, snapshot)" but still getting the error. Could someone come to my rescue please?
The getter 'length' was called on null. Receiver: null Tried calling: length
Expanded(
child: FutureBuilder(
initialData: [],
future: _dbHelper.getTasks(),
builder: (context, AsyncSnapshot snapshot) {
return ScrollConfiguration(
behavior: NoGlowBehaviour(),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return TaskCard(
title: snapshot.data[index].title,
);
},
),
);
},
),
)
],
Here is where the getTasks() method was initialised:
class DatabaseHelper {
Future<Database> database() async {
return openDatabase(
join(await getDatabasesPath(), 'todo.db'),
onCreate: (db, version) {
// Run the CREATE TABLE statement on the database.
return db.execute(
'CREATE TABLE tasks(id INTEGER PRIMARY KEY, title TEXT, description TEXT)',
);
},
version: 1,
);
}
Future<void> insertTask(Task task) async {
Database _db = await database();
await _db.insert('task', task.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace);
}
Future<List<Task>> getTasks() async {
Database _db = await database();
List<Map<String, dynamic>> taskMap = await _db.query('task');
return List.generate(taskMap.length, (index) {
return Task(
id: taskMap[index]['id'],
title: taskMap[index]['title'],
description: taskMap[index]['description']);
});
}
}
While using FutureBuilder consider checking snapshot state like bellow.
It is only possible to build list with items while future done with fetching and contains data.
I prefer checking
ConnectionState
Error
Data
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return Text("loassding");
else if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done)
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
// final word = snapshot.data![index];
print(snapshot.data!.length);
print(snapshot.data![index].model![0].word);
return Column(
children: [
// Text("${word.model!.length} ${word.model![0].word!} "),
],
);
});
else if (snapshot.hasError) {
return Text(
snapshot.error.toString(),
);
} else
return Text("something else");
},
);
Does it solve in your case?
I know this is a late reply, but your query is not returning any data because that table does not exist. You have a typo in you retrieve code. You are trying to load data from "task". The same goes for you insert function. They both need to change to change to "tasks".
in your insertTask method change the line:
await _db.insert('tasks', task.toMap(),
in your getTasks method change the line:
List<Map<String, dynamic>> taskMap = await _db.query('tasks');

flutter data not retrieved from firestore when using stream

DatabaseService databaseService = new DatabaseService();
Stream questionsSnapshot;
so Im using a stream and and a database service to retrieve data (questions and answers of a quiz) to my listView builder
the called function from database service is
getAquizData(String quizId) async{
return await Firestore.instance
.collection("quiz")
.document(quizId)
.collection("questionReponses")
.snapshots();
}
the init state function
#override
void initState() {
databaseService.getAquizData(widget.quizId).then((value){
questionsSnapshot = value;
setState(() {});
});
super.initState();
}
my listViewBuilder
#override
Widget build(BuildContext context) {
return Scaffold(
// an appbar
) ,
body: Container(
child: Column(children: <Widget>[
StreamBuilder(
stream: questionsSnapshot,
builder: (context, snapshot) {
return snapshot.data == null
? Container(child: Text("empty"),) : ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: snapshot.data.documents.length,
itemBuilder: (context,index){
return QuizPlayTile(
questionModel: getQuestionModelFromDatasnapshot(
snapshot.data.documents[index]),
index: index,
);
});
},
)
],)
)
);
}
}
when running it just show the word empty for a second and then it shows questions without answers
[1]: https://i.stack.imgur.com/E4CS4.jpg
We can use stream builder without calling it in initState method. Following code works for me. user quizStreamer in build method.
quizStreamer(){
return StreamBuilder(
stream: Firestore.instance
.collection('quiz')
.document(quizId)
.collection('questionReponses')
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
//loadWidgets method takes snapshot and render defined widgets
return loadWidgets(snapshot);
},
);
}

Can't combine firestore stream

So, i want to write query like this
... where from = x or to =x
I can't find any documentation about using where condition. So, i using StreamZip
#override
void initState() {
getEmail();
stream1 = databaseReference
.collection("userChat")
.where("from", isEqualTo: userId)
.orderBy("messageDate", descending: true)
.snapshots();
stream2 = databaseReference
.collection("userChat")
.where('to', isEqualTo: userId)
.orderBy("messageDate", descending: true)
.snapshots();
}
and here is my StreamBuilder
StreamBuilder(
stream: StreamZip([stream1, stream2]),
builder: (context, snapshot) {
print(snapshot.data.documents);
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
default:
return new Flexible(
child: new ListView.builder(
controller: _scrollController,
padding: new EdgeInsets.all(8.0),
reverse: false,
itemBuilder: (context, index) {
print("Time to show data");
List rev = snapshot
.data.documents.reversed
.toList();
MessageFromCloud messageFromCloud =
MessageFromCloud.fromSnapshot(
rev[index]);
return new ChatMessage(
data: messageFromCloud,
userFullname: userFullname,
userId: userId,
roomId: documentId);
},
itemCount: (messagesCloud != null)
? messagesCloud.length
: 0,
),
);
}
}),
When i run it, i get this error
Class 'List' has no instance getter 'documents'.
Receiver: _List len:2 Tried calling: documents
Did i miss something?
StreamZip - emits lists of collected values from each input stream
It means that your snapshot.data is a List.
Would suggest checking out this answer: Combine streams from Firestore in flutter

(Flutter) ListView.builder using FutureBuilder not working

I am trying to display items in a ListView using ListView.builder inside a FutureBuilder. My future function for FutureBuilder looks like this:
_fetchListItems() async {
wait() async {
number = await db.getNumber(userId); }
await wait();
List rawFavouriteList = await db.getList(number);
setState((){
rawFavouriteList.forEach((item){
_faouriteList.add(Model.map(item));
}});
return _faouriteList;
}
My FutureBuilder looks like this:
FutureBuilder(
future: _fetchListItems(),
builder:(context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {Container( child: ListView.builder(
itemCount: _faouriteList.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Text(
'${_faouriteList[index].title}');
}));}})
he following assertion was thrown building FutureBuilder(dirty, state:
I/flutter (24728): _FutureBuilderState#f12a3):
I/flutter (24728): A build function returned null.
I/flutter (24728): The offending widget is: FutureBuilder
I/flutter (24728): Build functions must never return null
Another exception was thrown: A build function returned null.
Note:
I tried to call _fetchListItems() from initState and not use FutureBuilder and that didn't work for me as well.
Here is a link to that case: (Flutter/Dart) Two async methods in initState not working
Please let me know if I should use FutureBuilder or initState to wait for List to load it's data. And how to make it work since none of the methods seem to work for me :(
Your fetch _fetchListItems method is not the problem as far as I can tell.
Your error message is very clear, " a build function returned null". In this case, the method that returns null is the anonymous function passed as the builder argument to the FutureBuilder. You're just missing the return keyword inside the else case because you're just creating the instance of Container but not returning it.
Something like this:
FutureBuilder(
future: _fetchListItems(),
builder:(context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return Container(
child: ListView.builder(
itemCount: _faouriteList.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Text('${_faouriteList[index].title}');
}
)
);
}
}
)
I don't know if there are any other problems with your code but this should solve the particular error message you are asking about.
It is not necessary to use setState for your case, try this:
Fetch async function
_fetchListItems() async {
number = await db.getNumber(userId);
List rawFavouriteList = await db.getList(number);
List _faouriteList = rawFavouriteList.map((item)=>Model.map(item)).toList();
return _faouriteList;
}
FutureBuilder
FutureBuilder(
future: _fetchListItems(),
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
Container(
child: ListView.builder(
itemCount: snapshot.data.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Text('${snapshot.data[index].title}');
}));
}
});