I'm Getting blank Spaces in GridView.builder while displaying data from snapshots. I think its because I have applied condition inside GridView.builder so its leaving blank spaces. Is there any way to overcome this ?
StreamBuilder(
stream: fireStore,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3,
mainAxisExtent: MediaQuery.of(context).size.width/1.5),
itemCount: snapshot.data?.docs.length,
itemBuilder: (context, index) {
if (snapshot.data?.docs[index]["userType"] == "1" &&
snapshot.data?.docs[index]["about"] != "") {
return cardWidget(snapshot, index);
} else {
return SizedBox.shrink();
}
},
);
},
);
The problem is that you are still returning an empty Widget (SizedBox.shrink()), that's why it is rendered as empty space. What you need to do is, prepare the valid data before returning the GridView widget.
e.g
final validData = snapshot.data?.docs.where((d)=> d['userType']==1 && d['about']!= 'data').toList();
Now you can use the validData to feed your GridView instead of snapshot.data?.docs.
Related
I'm making a to-do list with Flutter and Firebase Firestore, but I have a slight problem. Whenever I add a new task, snapshot.data becomes null. If I do not add a check for snapshot.connectionState and render a different widget according to that, I get the following error: NoSuchMethodError (NoSuchMethodError: The getter 'docs' was called on null.
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection('tasks')
.doc(widget.uid)
.collection('mytasks')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: SizedBox(
height: 100, width: 100, child: CircularProgressIndicator()),
);
} else {
> final docs = snapshot.data.docs;
return ListView.builder(
itemCount: docs.length,
itemBuilder: (ctx, index) {
final currTask = docs[index];
return Dismissible(
direction: DismissDirection.startToEnd,
key: UniqueKey(),
onDismissed: (_) async {
FirebaseFirestore.instance
.collection('tasks')
.doc(widget.uid)
.collection('mytasks')
.doc(currTask['id'])
.delete();
.
.
.
I don't want to have to display a CircularProgressIndicator or an empty screen. I want the tasks to remain visible and seamlessly add new tasks. How can I achieve that? I know what I asked for in the question title might be a little silly, what should I do instead?
I am able to successfully create a listview.builder in flutter application as per the below code.
CODE
Container(
child: StreamBuilder<QuerySnapshot>(
stream: query2.snapshots(),
builder: (BuildContext context,AsyncSnapshot<QuerySnapshot> snapshot) {
var usernames = snapshot.data.docs.map((e) => e['itemName']);
print("usernames");
print(usernames);
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index){
// String itemname =snapshot.data.docs[index]['itemName'] ?? "";
return ListTile(title:Text(snapshot.data.docs[index]['itemName'] ?? ""
),);
});
}
),
)
But I want to have list view builder to be created on about 5 different if-else conditions which I am not able to do so, I tried to implement this thing on StreamBuilder but could not do it, though ternary operator works but for only two conditions at at time and not multiple conditions, how should I achieve it?
this is very simple.
if(condition1)
return Container(color:Colors.blue);
else if(condition2)
return Container(color:Colors.yellow);
else if(condition3)
return Container(color:Colors.green);
else if(condition4)
return Container(color:Colors.red);
...etc.
else
return SizedBox();
The relevant error-causing widget was
StreamBuilder. I've given the link of the project repo in github .
Widget build(BuildContext context) {
return StreamBuilder(
stream: FirebaseFirestore.instance
.collection("/chats/2DGQmBssJ4sU6MVJZYc0/messages")
.orderBy('createdAt', descending: true)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
Center(child: CircularProgressIndicator());
final documents = snapshot.data.documents;
return ListView.builder(
reverse: true,
itemCount: documents.length,
itemBuilder: (context, index) {
return MessageBubble(
documents[index]['text'],
documents[index]['userID'] == user.uid,
documents[index]['userID'],
documents[index]['imageurl']);
},
);
},
);
}
}
Github link of the project
Try the following:
if (snapshot.connectionState == ConnectionState.waiting)
Center(child: CircularProgressIndicator());
else if(snapshot.connectionState == ConnectionState.active){
final documents = snapshot.data.documents;
return ListView.builder(
reverse: true,
itemCount: docs.length,
itemBuilder: (context, index) {
return MessageBubble(
docs[index]['text'],
docs[index]['userID'] == user.uid,
docs[index]['userID'],
docs[index]['imageurl']);
},
);
},
},
);
When the connectionState is active meaning with data being non-null, then retrieve the data. Also use docs since it seems you are using the new version of Firestore.
documents became docs
Edit
Now that I've become little better than before, so I must update this answer for better understanding.
Let's take this above example:
return ListView.builder(
reverse: true,
itemCount: documents.length,
itemBuilder: (context, index) {
return MessageBubble(
documents[index]['text'],
documents[index]['userID'] == user.uid,
documents[index]['userID'],
documents[index]['imageurl']);
},
);
},
);
Here, all the documents in that particular collection MUST contain these 3 fields, i.e, 'text', 'userID', and 'imageurl' to successfully build each child inside the listView.builder().
But, if any of these 3 fields is missing, or even miss-spelled in One or more documents, then it would throw an error like the above.
Will throw error:
Will throw error:
So, to solve that we need to find that particular document which has the field missing, or field miss-spelled and correct it.
Or, delete that document and keep only those documents which has correct fields.
(By the way, I'm using "Dark Reader" chrome extension for dark theme for firebase console, and every other website on internet.)
My Old Answer:
In my case I was using Firebase Firestore for my chat app, and while loading the chat screen, I was getting this error.
So in Firestore Database I had 15-20 messages, So I deleted most of them and leave just 2-3 messages. And then the Error disappeared.
I don't know happened here.
You create a widget and then don't use it and follow through with the rest of your logic:
if (snapshot.connectionState == ConnectionState.waiting)
Center(child: CircularProgressIndicator());
You need to stop there and return that widget from your builder:
if (snapshot.connectionState == ConnectionState.waiting)
return Center(child: CircularProgressIndicator());
Searching is working but I have applied the search filter in itemBuilder whenever I search for something and it finds at position 5 for example than itemBuilder building view for those positions where items have not found.
I want the list of founded items or no record text single times.
Expanded(
child: ListView.builder(
itemCount: productList.length,
itemBuilder: (BuildContext context, int index) {
return filter == null || filter == "" ? productItem(productList[index][DatabaseHelper.columnProductName], height, productList[index][DatabaseHelper.columnPrice] )
: productList[index][DatabaseHelper.columnProductName].toLowerCase().contains(filter.toLowerCase())
? productItem(productList[index][DatabaseHelper.columnProductName], height, productList[index][DatabaseHelper.columnPrice] ) : new Container(child: Text('No record'),);
},
),
),
My Controller
#override
void initState() {
super.initState();
_query();
controller.addListener(() {
setState(() {
filter = controller.text;
});
});
}
Currently my I have a ListView that is warped by a StreamBuilder which gets data from firebase firestore (e.g a list of users). This is how it looks:
Widget UsersList = new StreamBuilder(
stream: Firestore.instance
.collection('users')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return const Text("loading");
return new ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) =>
_buildItem(context, snapshot.data.documents[index]),
);
}
);
The question is how to add to the top of the ListView a static widget (e.g. a button to create a new user), I don't want the button to stay on the top of the page all the time, it should scroll with the ListView.
A workaround: in the _buildItem() function I could receive a boolean if it is the first document (by passing to the function index==0), and if true build the static widget (e.g. the add user button) first. But I can think of three problems:
If there isn't any documents in the firestore collection, it won't render the static widget.
If the internet connection is slow, it won't render the static widget until the first document is downloaded.
It is a workaround...
You could check the length inside the ListView.builder and always add an item for the button.
Widget UsersList = new StreamBuilder(
stream: Firestore.instance.collection('users').snapshots(),
builder: (context, snapshot) {
return new ListView.builder(
itemCount: (snapshot?.data?.documents?.length ?? 0) + 1,
itemBuilder: (context, index) {
if (index == 0)
return FlatButton(child: Text("Add"));
else
_buildItem(context, snapshot.data.documents[index-1]);
},
);
},
),