Switch between streams flutter firebase - flutter

I have a class of complains in which there is a field of status. This status can be pending , inprogress, completed , rejected. In UI I have designed buttons to filter complaints on the basis of status.
The issue that I am facing is that when I switch stream on button action. It still contains the data of previous stream.
Can anyone help me to have 2 streams and not either of it contains data of previous stream.
bool _isSwitched = false;
List<Complains> complains = [];
final Stream<QuerySnapshot> _all = FirebaseFirestore.instance
.collection('Complains').orderBy("startTime", descending: true)
.snapshots();
final Stream<QuerySnapshot> _pending = FirebaseFirestore.instance
.collection('Complains').where('status', isEqualTo: 'Pending').orderBy("startTime", descending: true)
.snapshots();
ElevatedButton(onPressed:(){
setState(() {
_isSwitched = !_isSwitched;
complains.clear();
complains2.clear();
});
} , child: Text("Switch Stream"),),
StreamBuilder<QuerySnapshot>(
initialData: null,
stream: _isSwitched?_all:_pending,
builder: (context, snapshot) {
if (snapshot.hasError) {
Text("Error");
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
//length of stream is greater than 0
if (snapshot.data!.docs.length == 0) {
return Center(
child: Text("No Complains"),
);
}
for (var element in snapshot.data!.docs) {
Complains complain = Complains.fromMap(element.data() as Map<String, dynamic>);
_isSwitched?complains.add(complain):complains2.add(complain);
}
return ListView.builder(
itemBuilder: (context, index) {
return InkWell(
onTap: () {
},
child: Card(
margin: EdgeInsets.all(8.0),
elevation: 5,
color: Colors.white70,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0))),
child: Container(
padding: EdgeInsets.all(20),
child:
Column(children: [
Text("Title\n ${complains[index].title}",style: TextStyle(fontStyle: FontStyle.italic),),
Table(
children: [
TableRow(children: [
Text("Name: " ),
Text("Address: "+ complains[index].address.toString(),),
]),
TableRow(children: [
Text("Priority: "+ complains[index].priority.toString(),),
Text("Status: "+complains[index].status.toString(),),
]),
TableRow(children: [
Text("Worker: "+ complains[index].worker.toString(),),
Text("Service: "+ complains[index].service.toString(),),
]),
],
),
],)
),
),
);
},
itemCount: _isSwitched?complains2.length:complains.length,
);
},
),
),

Related

Flutter : Auto refresh to update the data

I am developing a cart page which contains + and - buttons, on pressing it , the value in the backend changes, but it doesn't automatically change in the frontend.
Cartpage.dart
class CartUI extends StatefulWidget {
const CartUI({Key? key}) : super(key: key);
#override
State<CartUI> createState() => _CartUIState();
}
class _CartUIState extends State<CartUI> {
#override
Widget build(BuildContext context) {
final user = Provider.of<Userr?>(context, listen: false);
return Scaffold(
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(children: [
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('myOrders')
.doc(user?.uid)
.collection('items')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: Lottie.asset('assets/animations/delivery.json'),
);
} else {
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (BuildContext context, int index) {
final DocumentSnapshot documentSnapshot =
snapshot.data!.docs[index];
return Container(
height: 120,
width: 300,
child: Row(
children: [
Column(
children: [
SizedBox(
width: 200,
child: Text(
documentSnapshot['name'],
style: const TextStyle(
color: Colors.black87,
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
Text(
documentSnapshot['quantity'].toString(),
style: const TextStyle(
fontSize: 15,
),
),
Text(
'Rs.${documentSnapshot['price'].toString()}',
style: const TextStyle(
color: Colors.black87,
fontSize: 15,
),
),
Row(
mainAxisAlignment:
MainAxisAlignment.start,
children: [
const SizedBox(
width: 40,
),
ElevatedButton(
onPressed: () {
if (documentSnapshot['value'] != 0.0) {
setState(() {
String id = documentSnapshot['docid'];
final user = Provider.of<Userr?>(context, listen: false);
var postDocRef = FirebaseFirestore.instance.collection('myOrders').doc(user?.uid).collection('items').doc();
Provider.of<Calculations>(context, listen: false).updatecartdata(
context,
{
'value': documentSnapshot['value'] - 0.5,
'price': documentSnapshot['price'] - (documentSnapshot['ogprice'] / 2),
},id
);
}
);
}
if (documentSnapshot['value'] ==
0.5) {
String id =
documentSnapshot['docid'];
Provider.of<ManageData>(
context,
listen: false)
.deleteData(context, id);
}
},
child: const Text('-'),
),
const SizedBox(width: 20),
Text(documentSnapshot['value']
.toString()),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () {
String id =
documentSnapshot['docid'];
final user =
Provider.of<Userr?>(context,
listen: false);
var postDocRef =
FirebaseFirestore.instance
.collection('myOrders')
.doc(user?.uid)
.collection('items')
.doc();
Provider.of<Calculations>(context, listen: false).updatecartdata(
context,
{
'value': documentSnapshot['value'] + 0.5,
'price': documentSnapshot['price'] + (documentSnapshot['ogprice'] / 2),
},id
);
},
child: const Text('+'),
),
]),
],
),
],
),
);
});
}
},
),
),
_BillDetailView(),
]),
],
),
),
);
}
}
class _BillDetailView extends StatelessWidget {
#override
Widget build(BuildContext context) {
final textStyle =
Theme
.of(context)
.textTheme
.bodyText1!
.copyWith(fontSize: 16.0);
return Container(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Bill Details',
style:
Theme
.of(context)
.textTheme
.headline6!
.copyWith(fontSize: 17.0),
),
SizedBox(
height: 5,
),
FutureBuilder(
future: Provider.of<Calculations>(context, listen: false)
.getTotalCost(context),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Item total', style: textStyle),
Text('${snapshot.data}', style: textStyle),
],
);
} else if (snapshot.hasError) {
return Text("Error: ${snapshot.error.toString()}");
} else {
return const CircularProgressIndicator();
}
},
),
],
),
);
}
}
Calculation.dart
Future<dynamic> getTotalCost(BuildContext context) async {
final user = Provider.of<Userr?>(context, listen: false);
double totalCost = 0.0;
QuerySnapshot snapshot = await FirebaseFirestore.instance
.collection('myOrders')
.doc(user?.uid)
.collection('items')
.get();
for (var doc in snapshot.docs) {
totalCost += doc["price"];
}
print(totalCost.toString());
return totalCost.toString();
}
The value in the front end changes but last updated(Added or subtracted) value is not reflecting.After hot reload it changes, but not automatically.
How to change this code to automatically update the item total in the front end.
You need to use StreamBuilder instead of FutureBuilder to get live updates.
Changes your Calculation.dart to:
Stream<dynamic> getTotalCost(BuildContext context) async {
final user = Provider.of<Userr?>(context, listen: false);
double totalCost = 0.0;
QuerySnapshot snapshot = await FirebaseFirestore.instance
.collection('myOrders')
.doc(user?.uid)
.collection('items')
.snapshots();
for (var doc in snapshot.docs) {
totalCost += doc["price"];
}
print(totalCost.toString());
return totalCost.toString();
}
And change FutureBuilder to StreamBuilder:
StreamBuilder(
stream: Provider.of<Calculations>(context, listen: false)
.getTotalCost(context),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Item total', style: textStyle),
Text('${snapshot.data}', style: textStyle),
],
);
} else if (snapshot.hasError) {
return Text("Error: ${snapshot.error.toString()}");
} else {
return const CircularProgressIndicator();
}
},
),

error: the method [] was called on null. tried calling ()[merchantId]

I am trying to get the list of products by shop from the firestore. However, getting the following error:
I/flutter ( 7294): NoSuchMethodError: The method '[]' was called on null.
I/flutter ( 7294): Receiver: null
I/flutter ( 7294): Tried calling: []("merchantId")
I am using FutureBuilder to get shop data and StreamBuilderWrapper to get the products:
class Shop extends StatelessWidget {
Widget getProductsAndImages(BuildContext context, String outletID) {
return StreamBuilderWrapper(
shrinkWrap: true,
stream: productsRef.doc(outletID).collection('products')
.orderBy('dateTime', descending: true).snapshots(),
itemBuilder: (_, DocumentSnapshot snapshot) {
ProductModel posts = ProductModel.fromJson(snapshot.data() as Map<String, dynamic>);
return posts.productId != null
? Padding(
padding: const EdgeInsets.only(bottom: 12.0),
child:
ProductItem(productName: posts.proName!, productPrice: posts.price!,
count: posts.count!, images: posts.images!,
category: posts.category!, productDescription: posts.productDescription!,
productId: posts.productId!),
)
: Container(
child: Center(
child: Text('No products'),
),
);
});
}
#override
Widget build(BuildContext context) {
final outletData = Provider.of<OutletData>(context);
final outletId = ModalRoute.of(context)!.settings.arguments as String;
print('outlet id--->>> $outletId');
return Scaffold(
appBar: appHeader(context),
backgroundColor: color2,
extendBodyBehindAppBar: true,
body: Stack(
children: <Widget>[
Container(
decoration: const BoxDecoration(
image: DecorationImage(image: AssetImage("assets/images/background.jpg"),
fit: BoxFit.cover,),
),
),
FutureBuilder(
future: outletData.getOutletById(outletId),
builder: (ctx, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting ||
snapshot.data == null) {
return
Center(
child: Text("Юм олдсонгүй"),
);
}
return snapshot.data.length == 0
? Center(
child: Text("Бүтээгдэхүүн байхгүй"),
)
: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ShopHeader(
outletName: snapshot.data()[outletName],
outletImage: snapshot.data()[outletImg],
location: snapshot.data()[outletLocation],
description: snapshot.data()[outletIntro],
phoneNum: snapshot.data()[outletPhone],
merchantId: snapshot.data()[merchantId],
outletId: outletId,
outletRating: snapshot.data()['outletRating'],
),
Padding(
padding: const EdgeInsets.all(15.0),
child: Text(
"Recommended products:",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 17,
color: Colors.black54,
),
),
),
ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (ctx, index) {
return
getProductsAndImages(context, outletId);
},
),
],
),
);
},
)
]));
}
}
my getOutletById method is in the provider class:
Future<Map<String, dynamic>> getOutletById(String outId) async {
var outletsById;
final Map<String, dynamic> idOutlet = {};
_outletMerchant.clear();
try {
outletsById =
await firestore.collection(outletsCollection).doc(outId).get();
var outletResponse = outletsById.data();
var outletMerchantData = await firestore
.collection(allUserCollection)
.doc(outletResponse[merchantId])
.get();
var merchantResponse = outletMerchantData.data();
_outletMerchant.addAll(merchantResponse!);
print(_outletMerchant);
idOutlet.addAll(outletResponse);
print(merchantResponse[merchantId]);
} catch (err) {
print(err);
}
return idOutlet;
}
print() method prints nothing here my firestore database is as follows:
You can see that there is the merchantId for the outlets collection. where is the error here?

Flutter: get a document from Firestore inside a widget that returns a Widget

In my build I have this StreamBuilder which will get the data of the posts
and map it into a method that will return a widget to build the posts
StreamBuilder<List<Post>>(
stream: readPosts(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.data == null) {
print(snapshot.error.toString());
return Text(snapshot.error.toString());
} else {
final posts = snapshot.data;
print('from streambuilder');
return Column(
children: posts!.map(buildPost).toList(),
);
}
} else {
return Center(
child: CircularProgressIndicator(),
);
}
})
this is the readPosts function that provides the stream of the posts
Stream<List<Post>> readPosts() {
return FirebaseFirestore.instance
.collection('posts')
.where('availableFor', whereIn: ['All', 'Business Adminstration'])
.snapshots()
.map((snapshot) {
// print(snapshot);
return snapshot.docs.map((doc) {
// print(doc.data());
return Post.fromJson(doc.data());
}).toList();
});
}
and then the the list of posts are mapped into the buildPost function which will return the post widget
Widget buildPost(Post post) {
final organizations = getUserData(post.owner) //I want this final property to get an
Organization value as a return type
// however it is returning a Future<Organizations>
value
//is there any way I can use to convert
it to an Organizations type?
//and I want to keep this function as a
widget so that the stream builder
//does not give me an error
return Container(
padding: EdgeInsets.all(10),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0))),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8.0),
topRight: Radius.circular(8.0),
),
child: Image.network(post.imageUrl,
loadingBuilder: ((context, child, loadingProgress) {
return loadingProgress == null
? child
: LinearProgressIndicator();
}), height: 200, fit: BoxFit.fill
),
),
ListTile(
isThreeLine: true,
subtitle: Text(
'valid until: ${post.validUntil} for ${post.availableFor}',
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade400,
fontWeight: FontWeight.bold),
),
leading: ClipOval(
child: Container(
height: 30,
width: 30,
child: Image.network(organizations!.imageUrl,
loadingBuilder: ((context, child, loadingProgress) {
return loadingProgress == null
? child
: LinearProgressIndicator();
}), height: 200, fit: BoxFit.fill
),
),
),
title: Text(
post.description,
style: TextStyle(fontSize: 14),
),
trailing: Text(organizations!.id),
)
],
),
),
);
}
this is the getUserData function
Future<Organizations> getUserData(organizationId) async {
var organizations;
await FirebaseFirestore.instance
.collection('organizations')
.where('id', isEqualTo: organizationId)
.get()
.then((event) {
if (event.docs.isNotEmpty) {
Map<String, dynamic> documentData =
event.docs.single.data(); //if it is a single document
print(documentData.toString());
organizations = Organizations.fromJson(documentData);
}
}).catchError((e) => print("error fetching data: $e"));
return organizations;
}
is there a way to use the organizations data in build post method?

Bad State field does not exist within DocumentSnapshotPlatform for less than a second

I'm going crazy because of this error.
It just appears by opening the screen for a fraction of a second and then is gone.
Every new app installation will have this problem for once only.
I do not know why it can't find the documents fields in this small timeperiod and why it is working after this like it should be working.
My Code Snippet:
getMembers() {
// checkField();
return StreamBuilder<QuerySnapshot> (
///Hier sollte eher aus den User>> Groups>> und dann den Usernamen von dem userid dokument ausgeben
stream: FirebaseFirestore.instance
.collection("Groups")
.doc(widget.groupname)
.collection("Debt")
.snapshots(),
builder: (ctx, streamSnapshot) {
if (!streamSnapshot.hasData || !streamSnapshot.data!.docs.isNotEmpty) {
return Center(
child: CircularProgressIndicator(),
);
}
final documents = streamSnapshot.data!.docs;
return Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: documents.length ,
itemBuilder: (ctx, index) {
String docID = streamSnapshot.data!.docs[index].id;
///Ermitteln der Document ID !!!!
return Container(
padding: EdgeInsets.all(2),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(45),
),
child: ListTile(
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: MediaQuery.of(context).size.width / 3,
child: Text(
"${documents[index]["username"]}",
),
),
],
),
),
),
);
},
),
);
},
);
}
EDIT
changing expanded to a column doesn't work either :S
Could you tell me why this one is working without any problems and the one at the top is still showing this error?
I don't understand why it can't find the data, because the data are definetly there...
Here is the one from a different screen, working fine:
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("Chats")
.orderBy("createdAt", descending: true)
.where("group", isEqualTo: widget.groupname)
.snapshots(),
builder: (ctx, streamSnapshot) {
if (!streamSnapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
final documents = streamSnapshot.data!.docs;
return ListView.builder(
reverse: true,
itemCount: documents.length,
itemBuilder: (ctx, index) {
String docID = streamSnapshot.data!.docs[index].id;
return Row(
mainAxisAlignment: documents[index]["User"] == userid
? MainAxisAlignment.end
: MainAxisAlignment.start,
children: [
Container(
width: 300,
padding: EdgeInsets.symmetric(
vertical: 0.2, horizontal: 4),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(45)),
color: documents[index]["User"] == userid
? Colors.grey[400]
: Colors.brown[300],
child: ListTile(
leading: FaIcon(FontAwesomeIcons.comment),
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
documents[index]["name"] +
": " +
documents[index]["input"],
),
Text(
"${documents[index]["Datum"]}",
style: TextStyle(fontSize: 10),
)
],
),
trailing: delete
? IconButton(
icon: Icon(
Icons.delete,
color: Colors.red,
),
onPressed: () async {
await chats.doc(docID).delete();
},
)
: null,
),
),
),
],
);
},
);
},
),
EDIT ERROR STACK:
The Error I get
StreamBuilder triggered twice. InitialData can be useful.
I tried to add a better structure. But if (!streamSnapshot.hasData || streamSnapshot.data!.isEmpty) { return const SizedBox(); } is for extra precaution.
getMembers() {
// checkField();
return StreamBuilder<List<Debt>>(
///Hier sollte eher aus den User>> Groups>> und dann den Usernamen von dem userid dokument ausgeben
stream: FirebaseFirestore.instance
.collection("Groups")
.doc(widget.groupname)
.collection("Debt")
.snapshots().map((event) => event.docs.map((map) => Debt.fromMap(map.data())).toList()),
initialData: const [],
builder: (ctx, streamSnapshot) {
if (streamSnapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (!streamSnapshot.hasData || streamSnapshot.data!.isEmpty) {
return const SizedBox();
}
final documents = streamSnapshot.data!;
if (documents.isEmpty) {
return const SizedBox();
}
return Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: documents.length,
itemBuilder: (ctx, index) {
String docID = documents[index].id;
final userName = documents[index]["username"];
if(userName == null){
return const SizedBox();
}
///Ermitteln der Document ID !!!!
return Container(
padding: const EdgeInsets.all(2),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(45),
),
child: ListTile(
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
width: MediaQuery
.of(context)
.size
.width / 3,
child: Text(
"$userName",
),
),
],
),
),
),
);
},
),
);
},
);
}
#immutable
class Debt {
final String id;
final String? username;
const Debt({required this.id, this.username});
Map<String, dynamic> toMap() {
return {
'id': id,
'username': username,
};
}
factory Debt.fromMap(Map<String, dynamic> map) {
return Debt(
id: map['id'] as String,
username: map['username'] as String? ?? '',
);
}
}

flutter: StreamBuilder doesn't accept merged stream

i have a problem with merged stream and StreamBuilder.
im trying to merge multiple streams from firestore that each one represents a grocery list.
my result should be a ListView that combines all list in a some group.
from some reason,my StreamBuilder shows a single stream list in one tab but doesnt show it in another.
group list app photo
personal list app photo
code:
Stream<QuerySnapshot<Map<String, dynamic>>> getPesonalStream<T>() {
final userGroceries =
_fireStore.collection("users").doc(email).collection("groceries");
return userGroceries.snapshots();
}
Stream<QuerySnapshot<Map<String, dynamic>>> getGroupStream<T>() {
List<Stream<QuerySnapshot<Map<String, dynamic>>>> list = [];
_fireStore
.collection("groups")
.doc(gid)
.snapshots()
.forEach(((docSnapshot) {
List<dynamic> members = docSnapshot.data()!["members"];
list = members
.map((member) => _fireStore
.collection("users")
.doc(member)
.collection("groceries")
.snapshots())
.toList();
}));
return CombineLatestStream(list,
(values) => values.last as QuerySnapshot<Map<String, dynamic>>)
.asBroadcastStream();
// return StreamGroup.merge(list).asBroadcastStream();
}
as you can see iv tried a few ways to combine my streams and non workes
body:
body: TabBarView(children: [
_buildContent(
context,
db.getGroupStream(),
),
_buildContent(
context,
db.getPesonalStream(),
),
]),
my builder:
Widget _buildContent(BuildContext context, Stream<QuerySnapshot> stream) {
return StreamBuilder<QuerySnapshot>(
stream: stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
final docs = snapshot.data!.docs;
if (docs.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"The list is empty",
style: TextStyle(fontSize: 32, color: Colors.black54),
),
Text(
"Add a new item to get started",
style: TextStyle(fontSize: 16, color: Colors.black54),
),
],
));
}
int index = -1;
final cards = docs
.map((doc) => CrossableListTile(
doc, _showSetGroceryButtomSheetForm, index++))
.toList();
return Container(
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
child: ListView(
children: cards,
),
);
} else if (snapshot.hasError) {
return Center(
child: Column(
children: [Text("An error has occured while loading you'r list")],
),
);
}
return Center(
child: CircularProgressIndicator(
color: Colors.black,
));
},
);
}