How To get The Total of values in Flutter firestore? elevated button text - flutter

so below iam able to get the total sum of my prices from firestore but i cant seem to be able to call it to text in elevated button here is my code the total sum comes to me correct as i said but the thing is calling the final value to my button any help will be appreciated
class cartpage extends StatefulWidget {
const cartpage({Key? key}) : super(key: key);
#override
State<cartpage> createState() => _cartpageState();
}
class _cartpageState extends State<cartpage> {
AuthService get _auth => AuthService();
final Stream<QuerySnapshot> Cart = FirebaseFirestore.instance
.collection('Cart')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection("UserCart")
.doc('test')
.collection('final')
.snapshots();
var total = FirebaseFirestore.instance
.collection('Cart')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection("UserCart")
.doc('test')
.collection('final')
.get()
.then((querySnapshot) {
num sum = 0.0;
querySnapshot.docs.forEach((element) {
num value = element.data()["Price"];
sum = sum + value;
});
return sum;
});
#override
Widget build(BuildContext context) {
return // i removed some of the code from here //
ElevatedButton(
onPressed: null,
child: Text('$sum'),
)
],
)
],
);
}
}
Update this is my current code i get the sum and it shows in the button but as i mentioned when on this cart page and want to remove something from cart the changes doesnt apply.
FutureBuilder(
future: FirebaseFirestore.instance
.collection('Cart')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection("UserCart")
.doc('test')
.collection('final')
.get(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> QuerySnapshot) {
if (QuerySnapshot.hasError) {
return Text("Something went wrong");
}
if (QuerySnapshot.connectionState == ConnectionState.done) {
QuerySnapshot.data!.docs.forEach((doc) {
sumtotal = sumtotal +
doc["Price"]; // make sure you create the variable sumTotal somewhere
});
return ElevatedButton(
onPressed: null,
child: Text('Submit total price RM ${sumtotal}'));
}
return Text("loading");
},

Your Firebase request returns you some data correctly, but you should remember that it takes some time. In your example you are trying to use sum variable, but this variable does not receive the data form Firebase. You should first display some Loading (e.q.: CircularProgressIndicator widget) and call a Firebase request. When you receive the response, then you can change the state and pass sum to your widget.
So create asynchronous method and move your Firebase request call there with await keyword.
PS. So you use some state management? e.g: BLoC?

Related

Flutter how I will get Future<List<Placemark>> to <List<Placemark>?

I have a provider where a method , by this method if I send lat and long it will give me place name.
Future<List<Placemark>> getAndSetAddressFromLatLong(double startLat)async {
List<Placemark> placemarksStart = await placemarkFromCoordinates(startLat,startLong);
return placemarksStart;
}
So, When I'm trying to call and fetch the data in view file like below
#override
Widget build(BuildContext context) {
var data = Provider.of<MapProvider>(context).getAndSetAddressFromLatLong(
widget.history.startLat!.toDouble(),
widget.history.startLong!.toDouble(),
).then((value) => value);
print(data);
I'm getting the output I/flutter (25255): Instance of 'Future<List<Placemark>>' , But In then() if I print value without return I'm getting my desire list.
How I will get List<Placemark> here from Instance of 'Future<List>' ?
Since you're using provider call notifyListeners() after awaiting the results. In the widget use consumer to show the results
List<Placemark> _placemarksStart = [];
List<Placemark> get placemarksStart => [..._placemarksStart];
Future<void> getAndSetAddressFromLatLong(double startLat, double startLong) async {
_placemarksStart = await placemarkFromCoordinates(startLat,startLong);
notifyListeners();
}
Widget, similarly you can achieve loading with a boolean
Consumer<MyType>(
builder: (context, provider, child) {
if (provider.placemarksStart.isEmpty) {
return Center(child: Text('Loading...'),);
}
return ListView.builder(itemBuilder: (context, index) {
final item = provider.placemarksStart[index];
return Text("TODO");
}, itemCount: provider.placemarksStart.length,);
},
),
And call the method getAndSetAddressFromLatLong in the initState
late List<placemark> data;
#override
Widget build(BuildContext context) {
Provider.of<MapProvider>(context).getAndSetAddressFromLatLong(
widget.history.startLat!.toDouble(),
widget.history.startLong!.toDouble(),
).then((value){
data = value;
print(data);
}
);

problem when get user data from firebase firestore depend on data from firebase auth

i have this problem, when try to get user from firebase auth using streambuilder, and then get the user data from firestore depending on the user id, always this:
userDoc.data()
return a null?
this is the code :
StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, authSnapshot) {
// If the snapshot has user data, then they're already signed in. So Navigating to the Dashboard.
if (authSnapshot.hasData && authSnapshot.data != null) {
//return const TeacherDashboard();
return StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection("users")
.doc(authSnapshot.data?.uid)
.snapshots(),
builder: (context,
AsyncSnapshot<DocumentSnapshot> userSnapshot) {
if (userSnapshot.hasData && userSnapshot.data != null) {
final userDoc = userSnapshot.data;
print(userDoc!.get('isTeacher'));
final user = (userDoc != null
? userDoc.data()
: {"isTeacher": 0}) as Map<String, dynamic>;
if (user['isTeacher'] == 1) {
return const TeacherDashboard();
} else {
return const StudentsScreen();
}
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
});
I assume You want to know the user is a teacher or a student. if teacher, go to teacher page, if student go to student page. and you are using a value to detect the user is a teacher or student. the value is 1.
so, if user value is == 1 go to teacher page. or go to student page.
if you want this function only you do not need to create a streambuilder here. you just need to get the user value. That you can achieve like this:
// Here I created one HomePage to decide which Screen to visit.
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int? _value;
#override
void initState() {
super.initState();
getUserValue();
}
void getUserValue() async {
DocumentSnapshot snap = await FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.get();
setState(() {
_value = (snap.data() as Map<String, dynamic>)['isTeacher'];
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _value == null
? const Center(
child: CircularProgressIndicator(),
)
: (_value == 1)
? const TeacherDashboard()
: const StudentsScreen(),
);
}
}
sidenote: I think you getting the error because You using Stateless widget. It's very important to use a Stateful widget and initially keep the value null. and if value is null show something like CircularProgressIndicator(). once value is available go to different Screen. in Stateless widget once the widget is built already it will get the value but will not rebuilt anything. so null value will decide your widget what gives you the error. and You must setState() Once you get the value.
Hope this will solve your problem.

Null check operator used on a null value problem, I am very confused

I am new in flutter app.
I have made a subcollection products in users collections. It will show to all when a user will log in to their account. When the user clicks on the My Products button it will only show those products which are created by the login user. I user stream builder and use this FirebaseFirestore.instance
.collection('users')
.doc(LoginUser!.uid)
.collection('products')
.snapshots() , to get the data.
But when I click on the button it throws an exception. Which provide on the screen shots.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class UserProductList extends StatefulWidget {
UserProductList({Key? key}) : super(key: key);
#override
_UserProductListState createState() => _UserProductListState();
}
class _UserProductListState extends State<UserProductList> {
User? LoginUser;
#override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() async{
var LoginUser=await FirebaseAuth.instance.currentUser;
print(LoginUser!.email);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('users')
.doc(LoginUser!.uid)
.collection('products')
.snapshots() ,
builder:(BuildContext, AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot){
return ListView(
children: snapshot.data!.docs.map((document){
return ElevatedButton(onPressed: getCurrentUser, child: Text('data'));
}).toList(),
);
},
),
);
}
}
FIrst of all, FirebaseAuth.instance.currentUser is not a Future it doesn't need to be awaited. You can use it straight away in your StreamBuilder
.doc(FirebaseAuth.instance.currentUser?.uid ?? '')
My mistake was by making the currentUser future by using async and await. that's why steamBulder did not get the user id to fetch the data and throwing error for null user.
void getCurrentUser() async{
var LoginUser=await FirebaseAuth.instance.currentUser;
print(LoginUser!.email);
}```
So, I just remove this portion code and instead of that I just use this **var LoginUser = FirebaseAuth.instance.currentUser;** to get my **login user Uid** and it's working perfectly

Nested Future in Flutter

I'm new to Flutter, (comming from web and especially JS/VueJS)
I'm have a db in firebase that has a collection called edito and inside, i have different artist with a specific Id to call Deezer Api with it.
So what i want to do is first called my db and get the Id for each of artist and then put this id in a function as parameter to complete the url.
I did 2 Future function, one to call the db and one to call the api.
But i don't understand how to use one with the others in the build to get a listview with the information of the api of deezer for each data.
i'm getting the list but it's stuck in and endless loop.
All of my app will be on this nested function, is it possible to do this and call it in any widget that i want ?
here is my code, thanks
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class GetAlbum extends StatefulWidget {
#override
_GetAlbumState createState() => _GetAlbumState();
}
class _GetAlbumState extends State<GetAlbum> {
Map mapResponse;
Future<QuerySnapshot> getDocument() async{
return FirebaseFirestore.instance.collection("edito").get();
}
Future<dynamic> fetchData(id) async{
http.Response response;
response = await http.get('https://api.deezer.com/album/' + id);
if(response.statusCode == 200){
setState(() {
mapResponse = json.decode(response.body);
});
}
}
Future<dynamic> getDocut;
Future<dynamic> getArtist;
#override
void initState() {
getDocut = getDocument();
getArtist = fetchData(null);
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<QuerySnapshot>(
future : getDocut,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot){
if(!snapshot.hasData) {
return CircularProgressIndicator();
}else{
return new ListView(
children: snapshot.data.docs.map<Widget>((document){
print(document.data().length);
return FutureBuilder(
future: fetchData(document.data()['idDeezer'].toString()),
builder: (context, snapshot){
return Container(
child: mapResponse==null?Container(): Text(mapResponse['title'].toString(), style: TextStyle(fontSize: 30),),
);
}
);
}).toList(),
);
}
},
);
}
}
Here's a simplified example of making two linked Future calls where the 2nd depends on data from the first, and using the results in a FutureBuilder:
import 'package:flutter/material.dart';
class FutureBuilder2StatefulPage extends StatefulWidget {
#override
_FutureBuilder2StatefulPageState createState() => _FutureBuilder2StatefulPageState();
}
class _FutureBuilder2StatefulPageState extends State<FutureBuilder2StatefulPage> {
Future<String> _slowData;
#override
void initState() {
super.initState();
_slowData = getAllSlowData(); // combined async calls into one future
}
// linked async calls
Future<String> getAllSlowData() async {
int id = await loadId(); // make 1st async call for id
return loadMoreData(id: id); // use id in 2nd async call
}
Future<int> loadId() async {
int _id = await Future.delayed(Duration(seconds: 2), () => 42);
print('loadId() completed with: $_id'); // debugging
return _id;
}
Future<String> loadMoreData({int id}) async {
return await Future.delayed(Duration(seconds: 2), () => 'Retrieved data for id:$id');
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('FutureBldr Stateful'),
),
body: FutureBuilder<String>(
future: _slowData,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Center(child: Text(snapshot.data));
}
return Center(child: Text('Loading...'));
},
),
);
}
}
This avoids having to nest the FutureBuilder which may be error prone.
And calling future methods directly from a FutureBuilder is not recommended since the call could be made many times if its containing widget is rebuilt (which can happen a lot).
I tried to add firebase in the first one but i get null for the id in the get AllSlowDAta but i got it right with the Future.delayed.
// linked async calls
Future<String> getAllSlowData() async {
String id = await loadId(); // make 1st async call for id
return loadMoreData(id: id); // use id in 2nd async call
}
Future<dynamic> loadId() async {
//return await Future.delayed(Duration(seconds: 2), () => '302127');
await FirebaseFirestore.instance.collection("edito")
.get()
.then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((doc) {
return doc.data()["idDeezer"];
});
});
}
Future<dynamic> loadMoreData({String id}) async {
http.Response response;
response = await http.get('https://api.deezer.com/album/' + id);
if(response.statusCode == 200){
setState(() {
return json.decode(response.body);
});
}
}

Flutter error : The argument type 'List<Future<Widget>>' can't be assigned to the parameter type 'List<Widget>'

I'm trying to do a list of item from Firebase Firestore (this is done) and to get for each item a different image URL from Firebase Cloud Storage.
I use a function called getPhotoUrl to change the value of the variable photoUrl. The problem is that the return is executed before getPhotoUrl. If I add await in front of the function getPhotoUrl and async after _docs.map((document), I got an error saying that The argument type 'List<Future>' can't be assigned to the parameter type 'List'.
My code:
class PhotosList extends StatefulWidget {
#override
_PhotosListState createState() => _PhotosListState();
}
class _PhotosListState extends State<PhotosList> {
String photoUrl = 'lib/assets/default-image.png';
List<DocumentSnapshot> _docs;
getPhotoUrl(documentID) {
Reference ref = storage
.ref('Users')
.child(currentUser.uid)
.child('Photos')
.child(documentID)
.child('image_1.jpg');
ref.getDownloadURL().then((value) {
setState(() {
photoUrl = value.toString();
});
}).catchError((e) {
setState(() {
print(e.error);
});
});
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: firestore
.collection('Users')
.doc(currentUser.uid)
.collection('Photos')
.orderBy('date')
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return CircularProgressIndicator();
_docs = snapshot.data.docs;
if (_docs.isEmpty)
return Center(
child: Text("The list is empty."));
return Container(
child: ResponsiveGridList(
desiredItemWidth: 100,
squareCells: true,
minSpacing: 5,
children: _docs.map((document) {
getPhotoUrl(document.id);
return PhotosListItem(photoUrl: photoUrl);
}).toList(),
),
);
},
);
}
}
I think you mix 2 different ways. In every build cicle you map your docs and request that photoUrl, but inside that method you call setState, which re-triggers your build method. That way you should end in infinite loop of getting photo url and building your widget.
You have three options:
Load your photoUrls and store them inside your widget -> call set state -> check inside your mapping function if your photo is loaded, if yes, take it, if no, call your getPhotoUrl function
Load your photoUrls synchronously and return url from your function and set it to your PhotosListItem
(I would prefer this) Add your documentId to your photosListItem in your mapping function and inside your item you load this photo url. In this PhotoListItem you have a variable with your imageUrl and in initState you call your getPhotoUrl function
Inside your PhotoItem:
String imageUrl;
#override
void initState() {
Future.delayed(Duration.zero, () {
setState(() {
// load your data and set it to your variable
imageUrl = ..
});
});
super.initState();
}
You might use a FutureBuilder because StreamBuilder seems to be synchronous :
How to convert Future<List> to List in flutter?
Thanks for your answers guys, actually I found an other solution which is to get and write the URL in Firestore directly after uploading the image on the Storage.
This is the article which helped me a lot : https://medium.com/swlh/uploading-images-to-cloud-storage-using-flutter-130ac41741b2
(PS: some of the Firebase names changed since this article but it's still helpful.)
Regards.