How to await or rebuild Consumer when data is loaded - flutter

How do I reload Consumer when data is loaded or await for data to load. I am using Future Provider and everything is rebuilding itself when data is loaded (currentPosition Fetched) and using circularProgress() while waiting. But consumer is not rebuilding itself aslo can't use await with consumer package. When I save the code while debugging when it hot reload everything is okay but that's nit a solutiion. I want the consumer auto reload when data is fetched. I am fetching the data to make markers on google_Maps_Flutter
body: (currentPosition != null)
? Consumer<List<Bar>>(builder: (_, places, child) {
List.generate(places.length, (index) async {
print(places.length);
print(index);
print(imageUrl(places[index].photoRef));
List<String> wordList = places[index].name.split(" ");
bitmapIcon = await customBitmapDescriptor(
imageUrl: imageUrl(places[index].photoRef),
title: wordList[0],
);
markers = markerService.getBarMarkers(
places,
markerIcon: this.bitmapIcon,
);
print(markers.isEmpty);
});

Use setState((){}); to rebuild when data is loaded. Add setState((){}); where you want to rebuild e.g. if you want to reload when data is loaded in bitmapIcon then add
bitmapIcon = await convertImageFileToCustomBitmapDescriptor(
imageUrl: imageUrl(places[index].photoRef),
title: wordList[0],
).then((value) {
setState(() {});
});
And if you want to reload when data is loaded in marker then use
setState(() {
markers = markerService.getBarMarkers(
places,
markerIcon: this.bitmapIcon,
);
});
First Scenario
body: (currentPosition != null)
? Consumer<List<Bar>>(builder: (_, places, child) {
List.generate(places.length, (index) async {
print(places.length);
print(index);
print(imageUrl(places[index].photoRef));
List<String> wordList = places[index].name.split(" ");
bitmapIcon =await convertImageFileToCustomBitmapDescriptor(
imageUrl: imageUrl(places[index].photoRef),
title: wordList[0],
).then((value) {
setState(() {});
});
markers = markerService.getBarMarkers(
places,
markerIcon: this.bitmapIcon,
);
print(markers.isEmpty);
});
Second Scenario
body: (currentPosition != null)
? Consumer<List<Bar>>(builder: (_, places, child) {
List.generate(places.length, (index) async {
print(places.length);
print(index);
print(imageUrl(places[index].photoRef));
List<String> wordList = places[index].name.split(" ");
bitmapIcon = await convertImageFileToCustomBitmapDescriptor(
imageUrl: imageUrl(places[index].photoRef),
title: wordList[0],
);
if(!isSet){
setState(() {
markers = markerService.getBarMarkers(
places,
markerIcon: this.bitmapIcon,
);
});
}
print(markers.isEmpty);
});
Thumbs up if this solution helped

Using notifyListener() you can change the state of a consumer.
Below is a sample code. If You need more let me know.
Consumer<DataProviderClass>(
builder: (context, portfolioProvider, _) {
print("state changed");
return RaisedButton(
child: Text("Press"),
onPressed : () =>
Provider.of<DataProviderClass(context).fetchData(),
);
}
);
class DataProviderClass with ChangeNotifier{
fetchData(){
notifyListener();
}
}

Related

Future builder returns null although my list is not empty

I have this future builder which loads a list of movies in my provider class. Whenever I reload my screen, the movies do not get returned. Below is the future builder
FutureBuilder(
future: movieData.getTrendingMovies(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasData) {
return Swiper(
itemBuilder: (BuildContext context, i) {
return ChangeNotifierProvider(
create: (context) => Movie(),
child: MovieContainer(
imageUrl: movieData.movies[i].imageUrl,
id: movieData.movies[i].id,
rate: movieData.movies[i].rate,
title: movieData.movies[i].title,
),
);
},
itemCount: movieData.movies.length,
viewportFraction: 0.25,
scale: 0.4,
);
} else {
return Text(snapshot.error.toString()); // it returns null on the screen
}
}),
Also in my homescreen where I display my movies, after the build method, I create a listener(moviesData) to listen to all changes in the movies provider.
final movieData = Provider.of<Movies>(context, listen: false);
Below is also the methos which fetches the movies from a restfulAPI using http get request
Future<void> getTrendingMovies() async {
List<String> movieTitles = [];
List<String> movieImageUrls = [];
List<String> movieDescriptions = [];
List<String> movieReleaseDates = [];
List<String> movieRates = [];
List<String> movieIds = [];
const _apiKey = '******************************';
const url =
'https://api.themoviedb.org/3/trending/all/week?api_key=$_apiKey';
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode >= 400) {
print(response.statusCode);
return;
}
final extractedData = json.decode(response.body);
List moviesList = extractedData['results'] as List;
List<Movie> loadedMovies = [];
for (int i = 0; i < moviesList.length; i++) {
String movieTitle = moviesList[i]['original_title'] ?? '';
String? movieImage =
'https://image.tmdb.org/t/p/w400${moviesList[i]['poster_path']}'; //results[0].poster_path
String movieDescription =
moviesList[i]['overview'] ?? ''; //results[0].overview
String movieReleaseDate = moviesList[i]['release_date'] ?? '';
String? movieRate = moviesList[i]['vote_average'].toString();
String? movieId = moviesList[i]['id'].toString();
movieTitles.add(movieTitle);
movieImageUrls.add(movieImage);
movieDescriptions.add(movieDescription);
movieReleaseDates.add(movieReleaseDate);
movieRates.add(movieRate);
movieIds.add(movieId);
loadedMovies.add(
Movie(
id: movieIds[i],
title: movieTitles[i],
imageUrl: movieImageUrls[i],
description: movieDescriptions[i],
rate: double.parse(movieRates[i]),
releaseDate: movieReleaseDates[i],
),
);
}
_movies = loadedMovies;
notifyListeners();
//print(_movies.last.title); //This prints the name of the last movie perfectly....This gets called unlimited times whenever I set the listen of the **moviesData** to true
} catch (error) {
print(error);
}
}
There's a couple of things to unpack here.
Instead of a ChangeNotifierProvider, I believe you should use a Consumer widget that listens to your Movies provided service when you call the notifyListeners call, so make it Consumer<Movie>.
You can still call it using the Provider.of above for the sake of making the async call via the FutureBuilder, but I believe because you're not returning anything out of the getTrendingMovies and is just a Future<void> and you're querying the snapshot.hasData, well there is no data coming through the snapshot. Maybe instead you should call snapshot.connectionState == ConnectionState.done as opposed to querying for whether it has data.
Make sure that the response.body is truly returning a JSON value, but I believe your issue is in one of the points above.

flutter method checking if item is added to cart in firestore

I am having trouble with a method that is checking if the item is stored in the firestore database.
void add(BuildContext context, CartItem item) {
_items.add(item);
AuthService authService = Provider.of<AuthService>(context, listen: false);
Map<String, dynamic> cartMap = Map();
_items.forEach((CartItem item) {
cartMap['title'] = (item.product as Product).title;
cartMap['name'] = (item.product as Product).name;
});
_instance = FirebaseFirestore.instance;
_instance!
.collection('cart')
.doc(authService.getCurrentUser()) //need to get logged in account's id
.update({
'cartProduct': FieldValue.arrayUnion([cartMap])
}).then((value) {
print(_items.length);
notifyListeners();
});}
The add Method adds the item to the firestore in a way like the example image.
However, after I delete the data using the remove method,
void remove(BuildContext context, CartItem item) {
_items.remove(item);
AuthService authService = Provider.of<AuthService>(context, listen: false);
Map<String, dynamic> cartMap = Map();
// _items.forEach((CartItem item) {
cartMap['title'] = (item.product as Product).title;
cartMap['name'] = (item.product as Product).name;
// });
_instance = FirebaseFirestore.instance;
_instance!.collection('cart').doc(authService.getCurrentUser()).update({
'cartProduct': FieldValue.arrayRemove([cartMap]),
}).then((value) {
print(_items.length);
notifyListeners();
}); }
I check if the data is added to the cartProduct using isProductAddedToCart method and the result is still true. Also, when I print the _items.length, it doesn't decrease after I use the remove method.
bool isProductAddedToCart(Product? pro) {
return _items.length >= 0 ? _items.any(
(CartItem item) => item.product!.title == pro!.title) : false;
}
This is the code where I want to use the isProductAddedToCart method.
Consumer<CartService>(
builder: (context, cart, child) {
Widget renderedButton;
if (cart.isProductAddedToCart(widget.product) == false) {
renderedButton = DefaultButton(
text: "Participate",
press: () {
print(cart.isProductAddedToCart(widget.product));
cartService.add(context, CartItem(product: widget.product));
print(cart.isProductAddedToCart(widget.product));
},
);
} else {
renderedButton = DefaultButton(
text: "Delete",
press: () {
print(cart.isProductAddedToCart(widget.product));
cartService.remove(
context, CartItem(product: widget.product));
print(cart.isProductAddedToCart(widget.product));
},
);
}
return renderedButton;

StreamBuilder updating the stream only after hot restart

I am building an app where it collects all the orders and order details placed from Firebase. I have to get 2 things
Salon details from contactnumber which I saved using singleton method once the user logs in
Customer details from CustID
What happens right now is that during debugging I created this button, on pressing it fetches the salon details from database. But now, the details will only get fetched when I
Click the button first
Hot restart the app
Only then the streambuilder fetched the data
Here are my code snippets causing the problem :
Future<void> getSalonFromContact(String saloonContact) async {
await for (var docs in firestore.collection('Saloon').snapshots()) {
// final loop = snap.data!.docs;
for (var variable in docs.docs) {
if (variable.get(FieldPath(['Contact'])) == saloonContact) {
aadhar = variable.get(FieldPath(['Aadhar']));
getOrdersList(aadhar);
}
}
}
}
Future<void> getOrdersList(String aadhar) async {
ordersList.clear();
await for (var docs in firestore
.collection('orders')
.where('SalonID', isEqualTo: aadhar)
.snapshots()) {
for (var variable in docs.docs) {
if (variable.get('SalonID') == aadhar) {
ordersList.add(variable.data());
print('My orderlist is $ordersList');
} else {
continue;
}
}
}
}
Future<void> getCustomerDetails(String custID) async {
await for (var docs in firestore
.collection('Customers')
.where('Customer_Uid', isEqualTo: custID)
.snapshots()) {
// final loop = snap.data!.docs;
for (var variable in docs.docs) {
print(variable.data());
if (variable.get(FieldPath(['Customer_Uid'])) == custID) {
customerDetails.add(variable.data());
print('My customer details are ${customerDetails}');
}
}
}
}
#override
void didChangeDependencies() async {
await getSalonFromContact(contactNumber);
for (int i = 0; i < ordersList.length; i++) {
await getCustomerDetails(ordersList[i]['CustomerID']);
}
// TODO: implement didChangeDependencies
super.didChangeDependencies();
}
These codes are for finding out the details.
And this is my StreamBuilder code :
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('orders')
.where('SalonID', isEqualTo: aadhar)
.snapshots(),
builder: (context, snapshot) {
didChangeDependencies();
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Text('Loading...');
} else {
List<AppointmentCard> listitems = [];
return ListView(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
children: snapshot.data!.docs
.asMap()
.map((index, DocumentSnapshot document) {
getCustomerDetails(document['CustomerID']);
return MapEntry(
index,
AppointmentCard(
isCompleted: document['Status'],
name: customerDetails[index]['Name'],
contact: customerDetails[index]
['Contact'],
services: Flexible(
child: ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8),
itemCount:
document['Requested_Service']
.length,
itemBuilder: (BuildContext context,
int index) {
return Text(
document['Requested_Service']
[index]['name']);
}),
// child: Text(
// // "Text",
// " ${ordersList[i]['Requested_Service']} ",
//// .join(' '),
//
// softWrap: true,
// ),
),
),
);
})
.values
.toList(),
);
}
}),
Any idea what is going wrong and how I can fetch the data without the button and hot restart?
You use getCustomerDetails(document['CustomerID']); in before MapEntry and it is an asynchronous function. It will return probably after the MapEntry is built. You have to await getCustomerDetails function before put your variables which is updating in getCustomerDetails function.

Get Database from firestore and using within RefreshIndicator in Flutter

Hello everyone, I am getting data from firestore . In this case ı am trying to add in RefreshIndicator() to onRefresh(); when ı am using just a data without query , ı dont see any mistake ,it work clearly like that
tumGonderiler() async {
QuerySnapshot myQuerySnapshot = await akisRef.get();
setState(() {
this.tEdilenGonderiler = myQuerySnapshot.docs.map((e) => e.data()).toList();
});
}
but when ı am trying to query in my collection with where parameter , onRefresh() doesent work in my page , the code that does not work is as follows
tumGonderiler() async {
QuerySnapshot myQuerySnapshot = await akisRef.where("ownerID", whereIn: takipEdilenKullanicilar.map((e) => e.id).toList()).get();
setState(() {
this.tEdilenGonderiler = myQuerySnapshot.docs.map((e) => e.data()).toList();
});
}
I've changed my code a little bit untill now it's working right now you can follow from below
tumGonderiler() async {
QuerySnapshot snapshot1 = await takipEdilenRef.doc(anlikKullanici!.id).collection("takipEdilenler").get();
List<tEdilen> kullaniciress = snapshot1.docs.map((doc) => tEdilen.fromDocument(doc)).toList();
setState(() {
this.takipEdilenKullanicilar = kullaniciress;
});
QuerySnapshot myQuerySnapshot = await akisRef.where("ownerID", whereIn: takipEdilenKullanicilar.map((e) => e.id).toList()).get();
List<Gonderiler> kullanicires = myQuerySnapshot.docs.map((e) => Gonderiler.fromDocument(e)).toList();
setState(() {
this.tEdilenGonderiler = kullanicires;
});
}
return Scaffold(
backgroundColor: Colors.white,
appBar: baslik(context, strBaslik: "Kartlar", geriButonuYokSay: true),
body: RefreshIndicator(
color: Colors.black,
child: ListView(
children: tEdilenGonderiler,
),
onRefresh: () {
return tumGonderiler();
}),
);

How to NOT show the current user in a Grid View?

I have a function called getAllUsers() that returns all users from a database. The problem is that I want GridView.builder() to display all the users except the current user, but despite all the research I did, nothing seems to work out.
If i use the if condition like if(snapshot.data.documents[i].data["username"] != currentUserId within itemBuilder:, it returns a blank tile which represents the current user which creates a gap within the grid view. Thus, it makes the grid view look really bad.
I believe this problem could have been solved if I knew how to include the inequality query in the getAllUsers() method. But my understanding is that Firestore has yet to provide this function/argument.
HomeFragment class
Database _database = Database();
Stream _stream;
String currentUserId;
#override
void initState() {
getCurrentUserId();
getAllUsers();
super.initState();
}
getAllUsers() async {
return await _database.getAllUsers().then((val) {
if (mounted)
setState(() => _stream = val);
});
}
getCurrentUserId() async {
FirebaseUser currentUser = await FirebaseAuth.instance.currentUser();
currentUserId = currentUser.uid;
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: _stream,
builder: (context, snapshot) {
return snapshot.data == null ? Center(child: CircularProgressIndicator())
: Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child:
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 8.0,
mainAxisSpacing: 8.0,
),
itemCount: snapshot.data.documents.length,
itemBuilder: (context, i) {
return Container(
child: Text(snapshot.data.documents[i].data["username"])
);
}
// etc etc..
Database class
getAllUsers() async {
return await _firestore.collection("users").snapshots();
}
I tried to use this, but _stream2 returns null
Stream _stream, _stream2;
getAllUsers() async {
return await _database.getAllUsers().then((val) {
if (mounted) {
List<String> list;
setState(() {
_stream = val;
_stream2 = _stream.where((snapshot) {
_querySnapshot = snapshot;
for (int i = 0; i < _querySnapshot.documents.length; i++)
list.add(_querySnapshot.documents[i].data["userId"]);
return list.contains(currentUserId) == false;
});
});
}
});
}
I also tried this, it is not working
getAllUsers() async {
Stream<QuerySnapshot> snapshots = await _database.getAllUsers();
_stream = snapshots.map((snapshot) {
snapshot.documents.where((documentSnapshot) {
return documentSnapshot.data["userId"] != currentUserId;
});
});
}
Maybe you can try something like this. You filter the query result:
getAllUsers() async {
final Stream<QuerySnapshot> snapshots = await _firestore.collection("users").snapshots();
return snapshots.map((snapshot) {
final result = snapshot.documents
.map((snapshot) => User.fromMap(snapshot.data)
.where((user) => user.id != currentUser.id)
.toList();
return result;
}
}
If you do not have an User class, you can replace some lines with this. But the result will be a list of Map<String, dynamic> instead of a list of User objects.
return snapshots.map((snapshot) {
final result = snapshot.documents
.map((snapshot) => snapshot.data
.where((user) => user['id'] != currentUser.id)
.toList();
return result;
This solution worked well for me.
firestore.collection('your collection').where('x', isNotEqualTo: auth.currentUser!.uid).snapshots();