How to replace ListView with GroupedListView while using StreamBuilder with FireBase data in Flutter? - flutter

This is my ListView. It works perfectly:
StreamBuilder(
stream: FirebaseFirestore.instance.collection('products').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (streamSnapshot.hasData) {
return ListView.builder(
itemCount: streamSnapshot.data!.docs.length,
itemBuilder: (context, index) {
final DocumentSnapshot documentSnapshot = streamSnapshot.data!
.docs[index];
return Card(
margin: const EdgeInsets.all(5),
child: ListTile(
title: Text(documentSnapshot['name'] + " (" +
documentSnapshot['quantity'].toStringAsFixed(0) + ")"),
),
);
},
);
}
return const Center(
child: CircularProgressIndicator(),
);
}
),
I use a StreamBuilder to obtain data from FireBase. I turn that stream into snapshots, which are used inside the builder part of the ListView.
How do I replace my ListView with a GroupedListView?

This is the equivalent GroupedListView:
StreamBuilder(
stream: FirebaseFirestore.instance.collection('products').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
if (!streamSnapshot.hasData) return const Text("Loading...");
return GroupedListView<dynamic, String>(
elements: streamSnapshot.data!.docs,
groupBy: (element) => element['category'],
groupSeparatorBuilder: (String value) => Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
value,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
useStickyGroupSeparators: true,
floatingHeader: true,
order: GroupedListOrder.ASC,
itemComparator: (item1, item2) => item1['name'].compareTo(item2['name']),
itemBuilder: (context, dynamic element) {
return Card(
margin: const EdgeInsets.all(5),
child: ListTile(
title: Text(element['name']),
),
);
},
);
}
),

Related

StreamBuilder snapshot always returns empty value and null safety error

This is the code that I have implemented to get the data that can be seen on the home page of WhatsApp, i.e. the profile photo, name of the contact, last message, and the time sent of a Individual/Group Chat ListTile.
return firestore
.collection('users')
.doc(auth.currentUser!.uid)
.collection('chats')
.snapshots()
.asyncMap((event) async {
List<ChatContact> contacts = [];
for (var document in event.docs) {
var chatContact = ChatContact.fromMap(document.data());
var userData = await firestore
.collection('users')
.doc(chatContact.contactID)
.get();
var user = model.User.fromMap(userData.data()!);
contacts.add(
ChatContact(
name: user.name,
profilePic: user.photoURL,
contactID: chatContact.contactID,
timeSent: chatContact.timeSent,
lastMessage: chatContact.lastMessage,
),
);
}
return contacts;
});
I am calling this in a StreamBuilder to get the list of the contacts in the following way:
StreamBuilder<List<ContactList>>(
stream: ref.watch(chatControllerProvider).getchatContacts(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
var chatContactData = snapshot.data![index];
return Column(
children: [
InkWell(
onTap: () {
Navigator.pushNamed(
context, UserChatScreen.routeName,
arguments: {
'name': 'rr',
'selectedContactUID': 'uid'
});
},
child: const ListTile(
title: Text(
'name',
style: const TextStyle(
fontSize: 18,
),
),
subtitle: Padding(
padding: const EdgeInsets.only(top: 6.0),
child: Text(
' lastMessage',
style: const TextStyle(fontSize: 15),
),
),
leading: CircleAvatar(
backgroundImage: NetworkImage(
'https://png.pngitem.com/pimgs/s/649-6490124_katie-notopoulos-katienotopoulos-i-write-about-tech-round.png',
),
radius: 30,
),
trailing: Text(
'Date',
style: const TextStyle(
color: Colors.grey,
fontSize: 13,
),
),
))
],
);
});
}),
This is the error that I'm also facing right now:
Null check operator used on a null value
The relevant error-causing widget was
StreamBuilder<List>'
You are trying to return ListView with data, when snapshot still doesn't have data.
wrap the entire ListView.builder inside if(snapshot.hasData)
if(snapshot.hasData){
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
var chatContactData = snapshot.data![index];
return Column(
children: [
InkWell(
onTap: () {
Navigator.pushNamed(
context, UserChatScreen.routeName,
arguments: {
'name': 'rr',
'selectedContactUID': 'uid'
});
},
child: const ListTile(
title: Text(
'name',
style: const TextStyle(
fontSize: 18,
),
),
subtitle: Padding(
padding: const EdgeInsets.only(top: 6.0),
child: Text(
' lastMessage',
style: const TextStyle(fontSize: 15),
),
),
leading: CircleAvatar(
backgroundImage: NetworkImage(
'https://png.pngitem.com/pimgs/s/649-6490124_katie-notopoulos-katienotopoulos-i-write-about-tech-round.png',
),
radius: 30,
),
trailing: Text(
'Date',
style: const TextStyle(
color: Colors.grey,
fontSize: 13,
),
),
))
],
);
});
}
return CircularProgressIndicator(); // 👈 Add this line to return something when if condition is not met.

Future not setting default value on load

We are running a Future which should be setting the initial/default at time of load but we cannot seem to get this to work. The default seems to update only state change
return FutureBuilder<List<Payment>>(
future: DatabaseService.getPayments(widget.user!.id),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: [
const Divider(),
ListView.separated(
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return Dismissible(
direction: DismissDirection.endToStart,
key: Key(snapshot.data![index].cardId!),
onDismissed: (direction) {
// Remove the item from the data source.
setState(() {
snapshot.data!.removeAt(index);
});
},
// Show a red background as the item is swiped away.
background: Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
color: Colors.red,
alignment: Alignment.centerRight,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(
Icons.delete_forever_outlined,
color: Colors.white,
size: 32,
),
Text(
'Delete',
style: TextStyle(color: Colors.white),
),
],
),
),
confirmDismiss:
(DismissDirection dismissDirection) async {
switch (dismissDirection) {
case DismissDirection.endToStart:
case DismissDirection.startToEnd:
return await _showConfirmationDialog(
context,
'delete',
snapshot.data![index],
widget.user) ==
true;
case DismissDirection.horizontal:
case DismissDirection.vertical:
case DismissDirection.up:
case DismissDirection.down:
case DismissDirection.none:
break;
}
return false;
},
child: ListTile(
onTap: () {
setState(() {
paymentDefault = snapshot.data![index].cardId;
DatabaseService.createDefaultPayment(
context,
snapshot.data![index].cardId,
widget.user!.id);
});
},
leading: CircleAvatar(
backgroundColor:
snapshot.data![index].brand == 'MasterCard'
? Colors.amber[100]
: Colors.blue[100],
radius: 30,
child: loadImage(snapshot.data![index].brand)),
selected:
paymentDefault == snapshot.data![index].cardId,
title: Text('•••• ${snapshot.data![index].last4}'),
subtitle: Text(
'Exp. ${snapshot.data![index].expMonth}/${snapshot.data![0].expYear}'),
trailing:
paymentDefault == snapshot.data![index].cardId
? const Icon(Icons.check, color: Colors.green)
: const SizedBox.shrink(),
));
},
separatorBuilder: (context, index) {
return Divider(
height: 0,
color: Colors.grey[300],
);
}),
],
);
}
Use initialData prop in FutureBuilder
The data that will be used to create the snapshots provided until a non-null future has completed.
return FutureBuilder<List<Payment>>(
initialData: <Your initial Data here> 👈 Here
future: DatabaseService.getPayments(widget.user!.id),
builder: (context, snapshot) {
...
}

Unable to use GroupedListView with Firebase stream

We are using the GroupedListView package which for the most part seems to be serve what we need in our application. The issue occurs when adding a new record to the Stream that the GroupedListView complains that Bad State: Stream has already been listened to.
Widget buildMessages(convoId) {
return StreamBuilder(
key: widget.key,
stream: DatabaseService.getChatMessages(convoId),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
message = snapshot.data!.docs;
return GroupedListView<dynamic, String>(
reverse: true,
elements: message,
groupBy: (element) =>
DateFormat('yMMdd').format(element['timestamp'].toDate()),
order: GroupedListOrder.DESC,
groupSeparatorBuilder: _createGroupHeader,
itemBuilder: (context, dynamic element) => Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: _buildMessage(element)),
controller: listScrollController,
);
} else {
return const SizedBox();
}
},
);
}
Widget _createGroupHeader(element) {
if (isToday(DateTime.parse(element))) {
return Container(
padding: const EdgeInsets.all(10),
child: Text(
'Today'.toUpperCase(),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.labelMedium!,
),
);
}
if (isYesterday(DateTime.parse(element))) {
return Container(
padding: const EdgeInsets.all(10),
child: Text(
'Yesterday'.toUpperCase(),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.labelMedium!,
),
);
}
return Container(
padding: const EdgeInsets.all(10),
child: Text(
DateFormat.yMMMd().format(DateTime.parse(element)).toUpperCase(),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.labelMedium!,
),
);
}

how can I use flutter slidable let only one item move??not working key

I use flutter_slidable: ^0.6.0.
I hope working only one item slidable in my listview.
if one item slide by user, all the others(whatever open or close) r closed.
some docs say use key.but mine is not working.
return ListView.builder(
physics: ClampingScrollPhysics(),
itemCount: snapshot.data!.size,
shrinkWrap: true,
itemBuilder: (BuildContext context, count) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Slidable(
key: Key(snapshot.data!.docs[count].id),
controller: slidableController,
actionPane: SlidableDrawerActionPane(),
actionExtentRatio: 0.25,
actions: <Widget>[
widget.pin == 0
? IconSlideAction(
caption: 'pin 제거',
color: Colors.black45,
icon: Icons.push_pin_rounded,
onTap: () {
pinEraseRoom(widget.roomId);
},
)
: IconSlideAction(
caption: 'Pin',
color: Colors.black45,
icon: Icons.push_pin_outlined,
onTap: () {
pinRoom(widget.roomId);
},
),
],
secondaryActions: <Widget>[
IconSlideAction(
caption: 'Delete',
color: Colors.red,
icon: Icons.delete,
onTap: () {
deleteRoom(widget.roomId);
},
),
],
mine
https://i.stack.imgur.com/eFW7J.jpg
resolved
/// parent
return StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(other)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container();
}
return ConversationList(
controller: slidableController,
//////// child widget in coversationList widget
itemBuilder: (BuildContext context, count) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Slidable(
key: Key(snapshot.data!.docs[count].id),
controller: widget.controller,
actionPane: SlidableDrawerActionPane(),
actionExtentRatio: 0.25,
actions: <Widget>[
widget.pin == 0
//
More clarity can be found here

check firestore has document or not using Future Builder

Full code
class yourBookings extends StatefulWidget {
#override
_yourBookingsState createState() => _yourBookingsState();
}
class _yourBookingsState extends State<yourBookings> {
StateModel appState;
bool _loadingVisible = false;
#override
Widget build(BuildContext context) {
appState = StateWidget.of(context).state;
final number = appState?.user?.number ?? '';
Future getPosts() async {
var firestore = Firestore.instance;
QuerySnapshot qn = await firestore
.collection("confirmed_c_rides2")
.document(number)
.collection("1")
.getDocuments();
return qn.documents;
}
return Scaffold(
appBar: AppBar(
title: Text("Your Bookings :"),
),
body: Container(
child: FutureBuilder(
future: getPosts(),
builder: (_, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: Text("Loading ..."),
);
} else if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, index) {
Widget image_carousel = new Container(
height: 200.0,
child: new Carousel(
//borderRadius: BorderRadius.all(Radius.circular(2.0)),
boxFit: BoxFit.fitHeight,
images: [
Image.network(
"${snapshot.data[index].data["driverImage"]}"),
Image.network(
"${snapshot.data[index].data["carImage"]}")
],
autoplay: true,
animationCurve: Curves.fastOutSlowIn,
animationDuration: Duration(milliseconds: 1000),
dotSize: 4.0,
indicatorBgPadding: 6.0,
dotBgColor: Colors.transparent,
),
);
return Card(
child: ListTile(
title: Column(
children: <Widget>[
SizedBox(height: 10),
Text(
"Status: ${snapshot.data[index].data["status"]}",
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: 10),
image_carousel,
Text(
"Name: ${snapshot.data[index].data["driverName"]}"),
SizedBox(height: 10),
Text(
"Gender: ${snapshot.data[index].data["gender"]}"),
SizedBox(height: 10),
Text(
"Experience: ${snapshot.data[index].data["experience"]}"),
SizedBox(height: 10),
Text(
"Number: ${snapshot.data[index].data["driverNumber"]}"),
SizedBox(height: 10),
Text(
"Time: ${snapshot.data[index].data["time"]}"),
SizedBox(height: 10),
Text(
"Scheduled on: ${snapshot.data[index].data["rideOn"]}"),
SizedBox(height: 10),
RaisedButton(
color: Colors.black,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => new issue()));
},
child: Text(
"Having issue",
style: TextStyle(color: Colors.white),
),
),
SizedBox(height: 10),
RaisedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => rating1()));
},
child: Text("Rate driver"),
)
],
),
),
);
});
} else if (snapshot==null) {
return Center(
child: Text("not found..."),
);
}
return Center(
child: Text("not found 2..."),
);
}),
),
);
}
}
getPosts() refers to the firestore location for fetching data.
I want to check whether firestore contains number or not as a document using Future Builder.How can i do that?
number -> 1 contains further details.
If number does not exists then show data from firestore else show "not found...".How can i do that?
Future builder is used to stream firestore.