How to handle _Future<dynamic> value in flutter? - flutter

I am trying to get value from firebase in flutter. during that time, I am receiving _Flutter value returning from the Future<> type returning function. please help someone
I am having a code for fetching values from firebase.. the function gets a value from firebase by querying with an attribute
class FirebaseMethods {
Future<List> findEvents(dynamic attribute, dynamic value) async {
CollectionReference eventCollection =
FirebaseFirestore.instance.collection('events');
return eventCollection
.where(attribute, isEqualTo: value)
.get()
.then((QuerySnapshot querySnapshot) {
List events = [];
querySnapshot.docs.forEach((doc) {
events.add(doc.data());
});
return events;
}).catchError((error) {
print("Failed to retrieve events: $error");
});
}
Future<List> findUsers(dynamic attribute, dynamic value) async {
CollectionReference userCollection =
FirebaseFirestore.instance.collection('profile');
return userCollection
.where(attribute, isEqualTo: value)
.get()
.then((QuerySnapshot querySnapshot) {
List users = [];
querySnapshot.docs.forEach((doc) {
users.add(doc.data());
});
return users;
}).catchError((error) {
print("Failed to retrieve users: $error");
});
}
}
And I am calling the above function 'findUsers' in the following way:
dynamic database_functions = FirebaseMethods();
class RenderProfileView extends StatefulWidget {
String email;
RenderProfileView(this.email, {super.key});
#override
State<RenderProfileView> createState() => _RenderProfileViewState();
}
class _RenderProfileViewState extends State<RenderProfileView> {
TextEditingController name_controller = TextEditingController();
TextEditingController phone_number_controller = TextEditingController();
late dynamic user_json = database_functions.findUser('email', widget.email); // without late I am getting error and getting values with attribute 'email' = widget.email
dynamic get_name() {
print(user_json);
return 'some_value';
}
}
When the 'findUser' function is called, the printing message is -> Instance of '_Future'
Someone please help.. if any other way to solve the issue please mention it.

Future describes async operations in flutter. you must await all Futures results. Either by using the await keyword or .then property.
You could try adding initState to your stateful widget or go with a FutureBuilder depending on your use case.
Below is an edited version of your code.
dynamic database_functions = FirebaseMethods();
class RenderProfileView extends StatefulWidget {
String email;
RenderProfileView(this.email, {super.key});
#override
State<RenderProfileView> createState() => _RenderProfileViewState();
}
class _RenderProfileViewState extends State<RenderProfileView> {
TextEditingController name_controller = TextEditingController();
TextEditingController phone_number_controller = TextEditingController();
late dynamic user_json;
#override
void initState() {
super.initState();
database_functions.findUser('email', widget.email).then((data) {
user_json = data
});
}
String get name => 'some_value';
}

Related

Provider keeps on executing and data gets added to the list infinite in flutter

I am using Provider to manage the app state in flutter and use provider to call api and store it in model class and provider is called inside build method, its an inventory app.
when cart page is opened, provider to fetch cart items is called and cart items will be displayed, but the issue is the api to fetch cart items keeps on executing infinitly, in ui data gets replicated. Can anyone please help with this issue.
the code to call cart provider is
#override
Widget build(BuildContext context)
{
final FirebaseAuth _auth = FirebaseAuth.instance;
User? user = _auth.currentUser;
var phoneNumber = user!.phoneNumber;
final cartProvider = Provider.of<CartProvider>(context);
cartProvider.fetchCartByUser(user.phoneNumber.toString());
final testString = ModalRoute.of(context)!.settings.arguments as String;
return
isEmpty
? Scaffold(body: CartEmpty())
:
Scaffold
( //display cart items)
cart provider class is
class CartProvider with ChangeNotifier {
List<CartModel> _cartItems = [];
List<CartModel> get cartItems {
return [..._cartItems];
}
CollectionReference cart = FirebaseFirestore.instance.collection('cart');
Future<void> fetchCartByUser(String phoneNumber) async
{
await Server.getCartData().then((value)
{
for(var item in value)
{
_cartItems.insert
(
0,
CartModel
(
cartId: item['cartId'],
clientAddress: item['clientAddress'],
clientFullname: item['clientFullname'],
clientPhoneNumber: item['clientPhoneNumber'],
createdAt: item['createdAt'],
merchant: item['merchant'],
merchantCode: item['merchantCode'],
price: item['price'],
productCode: item['productCode'],
productImage: item['productImage'],
productName: item['productName'],
productQty: item['productQty'],
productSKU: item['productSKU'],
),
);
}
});
notifyListeners();
}
Future<void> deleteSingleCart(String cartId) async {
await cart
.doc(cartId)
.delete()
.then((value) => print("Cart Deleted"))
.catchError((error) => print("Failed to delete user: $error"));
// this.fetchCartByUser();
notifyListeners();
}
Future<void> updateProductQty(String cartId, String qty) async {
await cart.doc(cartId).update({'productQty': qty});
}
double get totalAmount {
var total = 0.0;
for (var i = 0; i < _cartItems.length; i++) {
total +=
int.parse(_cartItems[i].price) * int.parse(cartItems[i].productQty);
}
return total;
}
}
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((_) {
// Here you can fetch data login
});
}

Cannot get a field on DocumentSnapshotPlatform which does not exist

I am trying to display the name to the appbar but userid part seems like giving an error. iam a beginner to flutter and firestore can someone help me
class Profile extends StatefulWidget {
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
//To retrieve the Userid
User? user;
Future<void> getUserData() async {
User userData = await FirebaseAuth.instance.currentUser!;
setState(() {
user = userData;
print(userData.uid);
});
}
///////////////////////////////////////
Future<String>? _title;
#override
void initState() {
getUserData().then((value) => _title = _getAppBarNameWidget());
// _title = _getAppBarNameWidget();
super.initState();
}
//To retrieve the name from firestore
Future<String> _getAppBarNameWidget() async =>
await FirebaseFirestore.instance
.collection('customer')
.doc(user!.uid)
.get()
.then((DocumentSnapshot ds) async {
var name = ds['name'];
return name;
});
DocumentSnapshot ds does not directly contain document data, only document id, you have to use data() function to get data. It is also a good idea to check whether the document really exists, because you will get a snapshot event if the document is not found.
Examples:
ds.exists // will return true if document is found
ds.id // will return document reference
ds.data()!['name'] // will return 'name' field of document

How can i always setState when the page is opened?

My issue is, when the page is opened my variable isn't set. So when i'm querying from firestore i get the error The method '[]' was called on null. Receiver: null Tried calling: []("userbio")
I declared 2 variables
var userid;
var useremail;
Then i wrote this future
Future getUserData() async {
var firestore = Firestore.instance;
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
final String uid = user.uid.toString();
final String email = user.email.toString();
DocumentReference qn = await firestore
.collection("users")
.document(email)
.collection('userdetails')
.document(uid);
void initState() {
setState(() {
userid = uid;
useremail = email;
});
}
return qn.documentID;
}
This is how i'm currently trying to setState but it's not working
void initState() {
setState(() {
userid = uid;
useremail = email;
});
}
Please help, thanks.
comment if you need more context
Seems like you're declaring the nested function initState() but you're not calling it. I would just remove the function and call setState() directly :
...
.document(uid);
setState(() {
...
EDIT : Call getUserData() inside initState :
// In a class extending State<T>
#override
void initState() {
super.initState();
// synchronous call if you don't care about the result
getUserData();
// anonymous function if you want the result
() async {
var result = await getUserData();
}();
}
PS : initState(), like build() or setState(), is a predefined name in Flutter. Try not to use those names for functions that are not overriding the original ones.
Try assign your variable an empty value instead of leaving it null.
var userid = '';
var useremail = '';

Streambuilder only showing data from local cache on first launch of app

I'm using StreamBuilder to stream data from firestore.
StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('meals').where('email', isEqualTo: loggedInUser.email).orderBy('date', descending: true).snapshots(),
If i take out the .where section of the stream, it returns all data to the device. Once this has been done, I can then put the .where section back in and it works fine. However, it doesn't work straight away. This would suggest the .where section only works once the cache already has data. Also, if I add a document using firestore console, it doesn't update the app with the new data. But for some reason it will show all of the updated documents if i remove the .where part.
I'm really confused. Any ideas?
Thanks Jason
UPDATE: I've now figured out how to solve this problem. Please see my answer below for how I solved it.
I finally figured out the answer to my problem.
I added queryUserData(); to the initState(). Here's how it looks in the code:
class HomeScreen extends StatefulWidget {
static const String id = 'home_screen';
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final _firestore = Firestore.instance;
final _auth = FirebaseAuth.instance;
FirebaseUser loggedInUser;
#override
void initState() {
super.initState();
getCurrentUser();
queryUserData();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser();
if (user != null) {
loggedInUser = user;
print('this is a test${loggedInUser.email}');
}
} catch (e) {
print(e);
}
}
void queryUserData() async {
final user = await _auth.currentUser();
loggedInUser = user;
final query = await _firestore.collection('meals').orderBy('date', descending: true).where('email', isEqualTo: '${loggedInUser.email}').getDocuments(source: Source.cache);
var totalEquals = query.documents.length;
print('$totalEquals records found for this user');
if (totalEquals >= 1) {
print(query);
print('cache has data. Therefore data will now only be read from cache');
} else {
print('data will be read from firestore until you have at least 1 meal');
getFirestoreInitialData();
}
}
void getFirestoreInitialData() async {
final query = await _firestore.collection('meals').getDocuments();
print(query);
print('data still being read from firestore');
}

Flutter - Translate oninit

Class 1
#override
void initState() {
super.initState();
text =DemoLocalizations.of(context).trans('connection');
}
Class 2 (DemoLocalizations)
Future<bool> load() async {
String data = await rootBundle.loadString('locale/i18n_${locale.languageCode}.json');
Map<String, dynamic> _result = json.decode(data);
this._sentences = new Map();
_result.forEach((String key, dynamic value) {
this._sentences[key] = value.toString();
});
return true;
}
Return
So the question is: How can i load custom string (internazionalization) when screen load (oninit)?
Use didChangeDependencies instead of initState.
It is called once after widget creation and again when the state of DemoLocalizations is changed.