I have a collection called pending_appointments and I want to access all of the documents which have the field patienduid equal to the current user's uid. Then there is a stream builder, but snapshots.data becomes null.
Like when I go to that screen, it shows me the expected results for about half a second and then it changes to 'No appointment found'.
Also, if I remove that 'where' statement, it shows me all the pending appointments which is the expect result, but as soon as I add the where statement which should give me the desired result, it goes back to no appointment after showing me the desired results for half a second.
Widget build(BuildContext context) {
return SafeArea(
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('pending_appointments')
.where('patientUid', isEqualTo: user.uid)
.orderBy('date')
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.data != null) {
return ListView.builder(
scrollDirection: Axis.vertical,
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: snapshot.data.size,
itemBuilder: (context, index) {
DocumentSnapshot document = snapshot.data.docs[index];
print(_compareDate(document['date'].toDate().toString()));
if (_checkDiff(document['date'].toDate())) {
deleteAppointment(document.id);
}
return Card(
elevation: 2,
child: InkWell(
onTap: () {},
child: ExpansionTile(
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(left: 5),
child: Text(
'Dr. ${document['doctor']}',
style: GoogleFonts.lato(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
Text(
_compareDate(document['date'].toDate().toString())
? "TODAY"
: "",
style: GoogleFonts.lato(
color: Colors.green,
fontSize: 18,
fontWeight: FontWeight.bold),
),
SizedBox(
width: 0,
),
],
),
subtitle: Padding(
padding: const EdgeInsets.only(left: 5),
child: Text(
_dateFormatter(
document['date'].toDate().toString()),
style: GoogleFonts.lato(),
),
),
children: [
Padding(
padding: const EdgeInsets.only(
bottom: 20, right: 10, left: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Patient name: " + document['name'],
style: GoogleFonts.lato(
fontSize: 16,
),
),
SizedBox(
height: 10,
),
Text(
"Time: " +
_timeFormatter(
document['date']
.toDate()
.toString(),
),
style: GoogleFonts.lato(
fontSize: 16,
),
),
],
),
IconButton(
tooltip: 'Delete Appointment',
icon: Icon(
Icons.delete,
color: Colors.black87,
),
onPressed: () {
print(">>>>>>>>>" + document.id);
_documentID = document.id;
showAlertDialog(context);
},
),
],
),
),
],
),
),
);
},
);
} else {
return Center(
child: Text(
'No Appointment Scheduled',
style: GoogleFonts.lato(
color: Colors.grey,
fontSize: 18,
),
),
);
}
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
remove this
if (snapshot.connectionState == ConnectionState.active) {
}
add this to your code
if (Snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (Snapshot.connectionState == ConnectionState.none) {
return Container();
}
if (Snapshot.data.docs.isEmpty) {
return Text(
'Document Empty',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
);
}
if (!Snapshot.hasData) {
return Text(
'0',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
);
}
return ListView.builder(
scrollDirection: Axis.vertical,
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
final document = snapshot.data.docs[index].data();
print(_compareDate(document['date'].toDate().toString()));
if (_checkDiff(document['date'].toDate())) {
deleteAppointment(document.id);
}
});
Related
body: SafeArea(
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection("Daily Recipe ")
.snapshots(),
builder: (____,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.hasData && snapshot.data != null) {
print("Total Documents : ${snapshot.data!.docs.length}");
if (snapshot.data!.docs.isNotEmpty) {
return ListView.separated(
itemBuilder: (___, int index) {
Map<String, dynamic> docData =
snapshot.data!.docs[index].data();
if (docData.isEmpty) {
return const Text(
"Document is empty.",
textAlign: TextAlign.center,
);
}
String Name =
snapshot.data!.docs.elementAt(index).get("Name");
String Duration =
snapshot.data!.docs.elementAt(index).get("Duration");
List<dynamic> Ingredients = snapshot.data!.docs
.elementAt(index)
.get("Ingredients");
List<dynamic> Directions = snapshot.data!.docs
.elementAt(index)
.get("Directions");
return Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Card(
elevation: 100.0,
child: Stack(
children: [
Image.network(snapshot.data!.docs
.elementAt(index)
.get("Image")),
Center(
child: Padding(
padding: const EdgeInsets.only(
top: 150.0, left: 10.0),
child: Text(
Name,
style: const TextStyle(
fontSize: 25.0,
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
),
],
),
),
const Padding(
padding: EdgeInsets.all(10.0),
child: Text(
"Appetizers",
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
),
),
Card(
// child: cards(),
)
],
),
);
},
separatorBuilder: (___, ____) {
return const Divider();
},
itemCount: snapshot.data!.docs.length);
} else {
return const Center(
child: Text("Document aren't available."),
);
}
} else {
return Center(
child: Text("Getting Error"),
);
}
},
),
),
If I remove the awaits from my viewModel.sendMessage() it works as expected, however I can't set the state with the value returned. Is there a way to update the UI from setState?
I'm just not sure how to do both at the same time, the parent class for this widget is a StatefulWidget, Idk if that matters. My ConversationViewModel is just a changeNotifier.
#override
Widget build(BuildContext context) {
UsViewModel viewModel = Provider.of<UsViewModel>(context, listen: false);
viewModel.setUs();
var us = Provider.of<UsViewModel>(context, listen: true).us;
return Consumer<ConversationViewModel>(builder: (BuildContext context, viewModel, Widget child) {
return Scaffold(
key: viewModel.scaffoldKey,
appBar: AppBar(
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(
Icons.keyboard_backspace,
),
),
elevation: 0.0,
titleSpacing: 0,
title: buildName(),
),
body: Container(
height: MediaQuery.of(context).size.height,
child: Column(
children: [
Flexible(
child: StreamBuilder(
stream: messageListStream(widget.chatId),
builder: (context, snapshot) {
if (snapshot.hasData) {
List messages = snapshot.data.docs;
return ListView.builder(
controller: scrollController,
padding: EdgeInsets.symmetric(horizontal: 10.0),
itemCount: messages.length,
reverse: true,
itemBuilder: (BuildContext context, int index) {
Message message = Message.fromJson(
messages.reversed.toList()[index].data());
return ChatBubble(
message: '${message.content}',
time: message?.time,
isMe: message?.senderUid == us?.uid,
type: message?.type);
},
);
} else {
return Center(child: circularProgress(context));
}
},
),
),
Align(
alignment: Alignment.bottomCenter,
child: BottomAppBar(
elevation: 10.0,
child: Container(
constraints: BoxConstraints(maxHeight: 100.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Flexible(
child: TextField(
controller: messageController,
focusNode: focusNode,
style: TextStyle(
fontSize: 15.0,
color:
Theme.of(context).textTheme.headline6.color,
),
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
enabledBorder: InputBorder.none,
border: InputBorder.none,
hintText: "Type your message",
hintStyle: TextStyle(
color:
Theme.of(context).textTheme.headline6.color,
),
),
maxLines: null,
),
),
IconButton(
icon: Icon(
Feather.send,
color: Theme.of(context).colorScheme.secondary,
),
onPressed: () {
if (messageController.text.trim().isNotEmpty) {
sendMessage(viewModel, us);
}
},
),
],
),
),
),
)
],
),
),
);
});
}
sendMessage(ConversationViewModel viewModel, var us,
{bool isImage = false, int imageType}) async {
String msg;
msg = messageController.text.trim();
messageController.clear();
Message message = Message(
content: '$msg',
senderUid: us?.uid,
time: Timestamp.now(),
);
if (msg.isNotEmpty) {
String id = await viewModel.sendMessage(widget.usId, message);
setState(() {
chatId = id;
});
}
}
If you using ChangeNotifier with Provider package you don't need setState method. You should read Provider documentation: https://pub.dev/packages/provider
ft: That you create listeners for each item in this line.
isMe: message?.senderUid == context.watch<UsViewModel>.us ?.uid,
How to use a provider according to your question, added an example;
#override
Widget build(BuildContext context) {
context.read<UsViewModel>().setUs();
return Scaffold(
key: context.read<UsViewModel>().scaffoldKey,
appBar: AppBar(
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(
Icons.keyboard_backspace,
),
),
elevation: 0.0,
titleSpacing: 0,
title: buildName(),
),
body: Container(
height: MediaQuery.of(context).size.height,
child: Column(
children: [
Flexible(
child: StreamBuilder(
stream: messageListStream(widget.chatId),
builder: (context, snapshot) {
if (snapshot.hasData) {
List messages = snapshot.data.docs;
return ListView.builder(
controller: scrollController,
padding: EdgeInsets.symmetric(horizontal: 10.0),
itemCount: messages.length,
reverse: true,
itemBuilder: (BuildContext context, int index) {
Message message = Message.fromJson(
messages.reversed.toList()[index].data());
return ChatBubble(
message: '${message.content}',
time: message?.time,
isMe: message?.senderUid == context.watch<UsViewModel>.us ?.uid,
type: message?.type);
},
);
} else {
return Center(child: circularProgress(context));
}
},
),
),
Align(
alignment: Alignment.bottomCenter,
child: BottomAppBar(
elevation: 10.0,
child: Container(
constraints: BoxConstraints(maxHeight: 100.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Flexible(
child: TextField(
controller: messageController,
focusNode: focusNode,
style: TextStyle(
fontSize: 15.0,
color:
Theme.of(context).textTheme.headline6.color,
),
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
enabledBorder: InputBorder.none,
border: InputBorder.none,
hintText: "Type your message",
hintStyle: TextStyle(
color:
Theme.of(context).textTheme.headline6.color,
),
),
maxLines: null,
),
),
IconButton(
icon: Icon(
Feather.send,
color: Theme.of(context).colorScheme.secondary,
),
onPressed: () {
if (messageController.text.trim().isNotEmpty) {
sendMessage(context);
}
},
),
],
),
),
),
)
],
),
),
);
}
sendMessage(BuildContext context) async {
String msg;
msg = messageController.text.trim();
messageController.clear();
Message message = Message(
content: '$msg',
senderUid: context.read<UsViewModel>().us?.uid,
time: Timestamp.now(),
);
if (msg.isNotEmpty) {
String id = await context.read<UsViewModel>().sendMessage(widget.usId, message);
setState((){
chatId = id;
});
}
}
I did this personal project of mine where a barcode scanner would scan for data inside firestore database. I have this problem when I scanned a barcode thats not on the database it wont show the error message is just shows a empty scan item container which I made. Let me know if someone can figure why. I tried everything still couldnt fix it.
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("products")
.where("barcode", isEqualTo: '$barcodeScanRes')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Dialog(
child: Container(
height: 300,
child: Text('Product Not Found'),
),
);
} else {
return Dialog(
child: Container(
height: 350,
child: Column(children: [
Container(
height: 350,
width: 165,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot products =
snapshot.data!.docs[index];
return ScanCard(products: products);
},
)),
]),
),
);
#Scan Card
class ScanCard extends StatelessWidget {
const ScanCard({
Key? key,
required this.products,
}) : super(key: key);
final DocumentSnapshot products;
#override
Widget build(BuildContext context) {
final user = FirebaseAuth.instance.currentUser;
String _userId = user!.uid;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.all(10.0),
height: 180,
width: 160,
decoration: BoxDecoration(
color: Colors.blueAccent,
borderRadius: BorderRadius.circular(16)),
child: Image.network(products['img']),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0 / 4),
child: Text(
products['name'],
style: TextStyle(
color: Colors.blueGrey,
fontSize: 18,
),
),
),
Column(
children: [
Text(
"Size: " + products['size'],
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: Colors.brown),
),
SizedBox(
width: 30,
),
],
),
Row(
children: [
Text(
"\tRs. " + products['price'],
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
SizedBox(
width: 40,
),
Icon(
Icons.add_shopping_cart,
color: Colors.black,
size: 25,
),
],
),
SizedBox(
width: 10,
),
SizedBox(
child: Padding(
padding: const EdgeInsets.all(10),
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0)),
color: Colors.red,
child: Text(
"Add to cart",
style: TextStyle(color: Colors.white),
),
onPressed: () {
DocumentReference documentReference = FirebaseFirestore.instance
.collection('userData')
.doc(_userId)
.collection('cartData')
.doc();
documentReference.set({
'uid': FirebaseAuth.instance.currentUser!.uid,
'barcode': products['barcode'],
'img': products['img'],
'name': products['name'],
'size': products['size'],
'price': products['price'],
'id': documentReference.id
}).then((result) {
addToCartMessage(context).then((value) => {
Navigator.pop(context)
});
}).catchError((e) {
print(e);
});
},
),
),
)
],
);
}
}
The thing is you are showing Product not found based on the condition:- !snapshot.hasData but this conditon means that data is being fetched so at this time rather show a progress indicator.
And to handle when data is not present in backend then add another condition:- if(snapshot.data.docs.isEmpty) and here show your dialogbox of Product not found...
Final Code Snippet will look like:-
if (!snapshot.hasData)
return Center(child:CircularProgressIndicator));//or return a black container if you don't want to show anything while fetching data from firestore
else if (snapshot.data.docs.isEmpty) {
return Dialog(
child: Container(
height: 300,
child: Text('Product Not Found'),
),
);
} else {
return Dialog(
child: Container(
height: 350,
child: Column(children: [
Container(
height: 350,
width: 165,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot products =
snapshot.data!.docs[index];
return ScanCard(products: products);
},
)),
]),
),
);
}
I store a selected quarter in a Sqlite database. This quarter is displayed in an appbar along with some text a user can click to take them to a page where they can change the selected quarter. Once the user selects a quarter from the page, I push this neq quarter to the sqlite database so it can be displayed. I also use a Navigator.pop(context); to go back to the previous page when a new quarter is selected. My problem is that once a user selects a new quarter, the quarter isn't displayed on the page they were just on. You have to navigate away to see it or navigate away and back. the FutureBuilder doesn't seem to know that there is new data in the SQLite database that needs to be displayed. How would I go about making it display the new data when a user selects a new quarter? Any help is appreciated! Thanks!
How I display quarters. Also where the button to take you to the page to change quarters is.
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: FutureBuilder<dynamic>(
future: SessionDBProvider.sessionDB.getSessionObject(),
initialData: List(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
print(
'${snapshot.error}',
);
}
}
List session = snapshot.data;
return Container(
child: Column(children: <Widget>[
Flexible(
flex: 2,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.red],
begin: Alignment.topRight,
end: Alignment.topLeft,
stops: [0.2, 1])),
child: Container(
margin: EdgeInsets.only(left: 11),
child: Text(
"Test Page",
style: TextStyle(
color: Colors.white,
fontSize: 27,
fontFamily: 'Montserrat',
height: 1.1),
),
),
)
],
),
),
Flexible(
flex: 2,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
margin: EdgeInsets.only(left: 10),
child: Text(
"${session[0].selected_quarter}",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 25),
),
),
],
),
),);
}));
}
This is where you select a new quarter and push it to the Sqlite database.
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[200],
appBar: PreferredSize(
preferredSize: Size.fromHeight(95.0),
child: AppBar(
automaticallyImplyLeading: false, // hides leading widget
flexibleSpace: DataAppBar(),
),
),
body: FutureBuilder<dynamic>(
future: DataDBProvider.dataDB.getData(),
initialData: List(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
print(
'${snapshot.error}',
);
}
}
List data = snapshot.data;
return ListView.builder(
itemCount: snapshot.data.length,
shrinkWrap: true,
itemBuilder: (context, int index) {
return Padding(
padding: EdgeInsets.symmetric(
vertical: 1.0, horizontal: 4.0),
child: Card(
color: (index % 2 == 0) ? greycolor : Colors.white,
child: Container(
height: 60,
padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
child: InkWell(
onTap: () {
Data data = new Data();
data.quarter_status = data[index].status;
data.selected_quarter = data[index].quarter;
DataDBProvider.sessionDB.updateSession(session);
Navigator.pop(context);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.only(right: 5),
child: Text(data[index].quarter,
style: TextStyle(
fontSize: 20,
fontFamily: 'Montserrat',
color: Colors.blue),
textAlign: TextAlign.left),
),
],
),
),
),
),
);
},
);
}));
}}
I have a map in Firebase Database like this,
'abc#gmail.com' : { food order }
I want to create a stream of this data to update widgets when a new order is placed.
I tried using the below code but it is giving error.
DocumentSnapshot has no instance getter 'length'
StreamBuilder(
stream: Firestore.instance.collection('admin').document('current-orders').snapshots(),
builder: (context, snapshot) {
return snapshot.hasData?Column(
children: <Widget>[
Expanded(
child: Container(
child: Padding(
padding: EdgeInsets.all(10),
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, uid) {
return Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
snapshot.data.keys.toList()[uid],
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
),
RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30)),
color: Colors.blue[200],
child: Text(
'Order Ready',
style: TextStyle(color: Colors.black54),
),
onPressed: () {},
),
],
),
ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: snapshot.data.values.keys.toList().length,
itemBuilder: (context, orderID) {
return Text(
'${snapshot.data.values.keys[orderID].toString()} : ${snapshot.data.values.values[orderID].toString()}',
//'${_currentOrders[uid].keys.toList()[orderID]} : ${_currentOrders[uid].values.toList()[orderID]}',
style: TextStyle(fontSize: 16),
);
},
),
Padding(
padding: EdgeInsets.only(top: 10),
child: Container(
color: Colors.grey,
height: 1,
),
)
],
);
},
),
),
),
),
],
):Center(child: Text('No orders right now', style: TextStyle(fontSize: 30, color: Colors.black54),),);
}
);
Thank you.