How to use transaction with int data - Firestore Flutter - firebase

Upon creating an account the user's firestore creates a field that displays the current amount of plastics the user has. So far, I have a button that updates that amount using the user's text field input. I've heard of something called a transaction which apparently allows one to intsead add the input amount to the overall data for it to be displayed? How would I accomplish this in my case when the use inputs a new amount?
Code:
database.dart
Future<void> userSetup(String displayName) async {
int plastics = 0;
final CollectionReference users =
FirebaseFirestore.instance.collection('UserNames');
FirebaseAuth auth = FirebaseAuth.instance;
String uid = auth.currentUser.uid.toString();
users.doc(uid).set({'displayName': displayName, 'uid': uid});
//This is the field for plastics amount starting at 0.
users.doc(uid).update({'plastics': plastics});
return;
}
How I retrieve the amount data:
final firestore = FirebaseFirestore.instance;
FirebaseAuth auth = FirebaseAuth.instance;
Future<String> getPlasticNum() async {
final CollectionReference users = firestore.collection('UserNames');
final String uid = auth.currentUser.uid;
final result = await users.doc(uid).get();
return result.data()['plastics'].toString();
}
How I display it:
FutureBuilder(
future: getPlasticNum(),
builder: (_, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
return Text(snapshot.data,
style: TextStyle(color: Colors.amber[400], fontSize: 20));
},
),
Currently how the user can replace the amount, but not add to it(The problem)
OnPressed: () async {
try {
final String uid = auth.currentUser.uid;
FirebaseFirestore.instance
.collection('UserNames')
.doc(uid)
.update({
"plastics": int.parse(_plasticController.text),
});
Navigator.of(context).pop();
} catch (e) {}
},

I made a separate future to take care of adding:
final firestore = FirebaseFirestore.instance; //
FirebaseAuth auth = FirebaseAuth.instance;
Future<bool> addPlastic(String amount) async {
try {
String uid = auth.currentUser.uid;
var value = double.parse(amount);
DocumentReference documentReference =
FirebaseFirestore.instance.collection('UserNames').doc(uid);
FirebaseFirestore.instance.runTransaction((transaction) async {
DocumentSnapshot snapshot = await transaction.get(documentReference);
if (!snapshot.exists) {
documentReference.set({'plastics': value});
return true;
}
double newAmount = snapshot.data()['plastics'] + value;
transaction.update(documentReference, {'plastics': newAmount});
return true;
});
} catch (e) {
return false;
}
}
Then I just called it when the button was pressed
onPressed(){
addPlastics(_plasticController.text);
}

Related

Fetching user name returns null at first time with Flutter Firebase

I want to have the username (in Arabic) on the home page from firebase. When I ran the app the first time every time I turn on the emulator it returns null. But if I run it one more time/hot reload it is good.
I tried using toString() but it also did not work.
Here is my code:
//user's info
late User user;
final _auth = FirebaseAuth.instance;
late User signedInUser;
var sex;
var age;
var name;
Future<void> _getData() async {
FirebaseFirestore.instance
.collection('users')
.get()
.then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((doc) {
if (doc["email"] == signedInUser.email) {
name = doc['name'];
age = doc['age'];
sex = doc['sex'];
print(doc['name']); //might delete
}
});
});
}
Widget build(BuildContext context) {
return FutureBuilder(
future: _getData(),
builder: (context, snapshot) => snapshot.connectionState ==
ConnectionState.waiting
? //more code
Text(
'$name'.toString(),
style: TextStyle(
),
) ```
Since _getData is an asynchronous method, it needs to return a Future, or it can use await, but in your code it does neither of those things.
For example, this now uses await to ensure the method only completes once the name is set:
Future<void> _getData() async {
var querySnapshot = await FirebaseFirestore.instance
.collection('users')
.get();
querySnapshot.docs.forEach((doc) {
if (doc["email"] == signedInUser.email) {
name = doc['name'];
age = doc['age'];
sex = doc['sex'];
print(doc['name']); //might delete
}
});
}

I'm trying to display name on profile user but having errors why?

I followed some tutos and I add user on firestore successly but I can't print this user on the profile page when the current user is logging because doc. is underlined in red in my backend, they say : The getter 'doc' isn't defined for the type 'DocumentSnapshot<Object?>'.
This is my entire backend
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
Future<void> userSetup(String displayName) async {
CollectionReference users = FirebaseFirestore.instance.collection('Users');
FirebaseAuth auth = FirebaseAuth.instance;
String uid = auth.currentUser!.uid.toString();
users.add({'displayName': displayName, 'uid' : uid });
final result = await users.doc(uid).get();
return result.doc.data()['displayName'];
}
Plus I'm having a error too in my front end but I think it's because the backend wrond initialized or just a code wrote.
So this is my front end code:
(userId is red underlined because it's undefined and my [] operators too),
hope you help me !
FutureBuilder(
future: FirebaseFirestore.instance
.collection('users')
.doc('uid')
.get(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Text(snapshot.data['displayName'],
);
} else {
return Text("Loading...");
I edited
FutureBuilder(
future: FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.get(),
builder: (context, snapshot) {
if (snapshot.hasData)
return Text("Loading...");
if (snapshot.data == null) {
print('Document does not exist on the database');
}else{
return Text("Researching data...");
}
if (snapshot.connectionState == ConnectionState.done) {
var name = snapshot.data as DocumentSnapshot;
return Text(name['displayName'],
);
} else {
return Text("Loading..");
}
},
)
You are not specifying the document ID when creating user, so then, is created unique ID for your user document, and you can't access the document by user ID. And you should also await the creating process. The solution is easy, just specify the document ID when creating user:
Future<void> userSetup(String displayName) async {
CollectionReference users = FirebaseFirestore.instance.collection('Users');
FirebaseAuth auth = FirebaseAuth.instance;
String uid = auth.currentUser!.uid.toString();
await users.doc(uid).set({'displayName': displayName, 'uid': uid });
final result = await users.doc(uid).get();
final data = result.data() as Map<String, dynamic>;
return data['displayName'];
}
Hope it works!

Flutter Firestore doc get returning null

I am trying to get a document from a Firestore collection using the following code:
firebase_service.dart:
class FirebaseService {
final firestoreInstance = FirebaseFirestore.instance;
final FirebaseAuth auth = FirebaseAuth.instance;
Map<String, dynamic> getProfile(String uid) {
firestoreInstance.collection("Artists").doc(uid).get().then((value) {
return (value.data());
});
}
}
home_view.dart:
Map<String, dynamic> profile =
firebaseService.getProfile(auth.currentUser.uid);
When stepping through the code the profile variable is null in home_view.dart, but value.data() in firebase_service.dart contains a map. Is there a reason why this value isn't being returned in home_view.dart?
Your code needs a few edits, as the getProfile function is async.
class FirebaseService {
final firestoreInstance = FirebaseFirestore.instance;
final FirebaseAuth auth = FirebaseAuth.instance;
// set the return type to Future<Map<String, dynamic>>
Future<Map<String, dynamic>> getProfile(String uid) async { // insert async here
/// insert a return and await here
return await firestoreInstance.collection("Artists").doc(uid).get().then((value) =>
return value.data(); // the brackets here aren't needed, so you can remove them
});
}
}
Then finally in home_view.dart
// insert await here:
Map<String, dynamic> profile = await
firebaseService.getProfile(auth.currentUser.uid);
If you plan to use the getProfile function I suggest you to use a FutureBuilder.
In you home_view.dart's build function write this:
return FutureBuilder(
future: firebaseService.getProfile(auth.currentUser.uid),
builder: (context, snapshot){
if (!snapshot.hasData){
return Center(child: CircularProgressIndicator(),);
}
final Map<String, dynamic> profile = snapshot.data.data();
return YourWidgets();
});
And now you don't need to write:
Map<String, dynamic> profile = await
firebaseService.getProfile(auth.currentUser.uid);
This is an async operation and you have to await for its value.
For reference, you can take a look here at documentation of how propper authentication and CRUD operations made in Firebase with flutter.

Flutter App stuck at splash screen not moving to login page or homepage

I tried to add splash screen to add splash screen to my app but it gets stuck at splash screen itsalf and does not move to next screens
I have added the code here:-
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
super.initState();
getUserInfo();
}
Future getUserInfo() async {
await getUser();
setState(() {});
print(uid);
navigateUser();
}
navigateUser()
{
if(uid!=null && authSignedIn != false)
{
Timer(Duration(seconds: 2),
()=>Navigator.pushReplacementNamed(context, "/toprofilepage")
);
}
else{
Timer(Duration(seconds: 2),
()=>Navigator.pushReplacementNamed(context, "/tologinpage")
);
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Login',
initial route:'/',
routes: {
'/toprofilepage':(context)=>FirstScreen(),
'/tologinpage':(context)=>LoginPage(),
},
home: Scaffold(
body: Center(
child: Text("Saraswat",style: TextStyle(fontSize: 40,fontWeight: FontWeight.bold,fontStyle: FontStyle.italic),),
),
)
);
}
}
I am getting this following message in console also:-
E/flutter ( 5947): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: Navigator operation requested with a context that does not include a Navigator.
Code for sign in:-
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
bool authSignedIn;
String uid;
String name;
String imageUrl;
Future getUser() async {
// Initialize Firebase
await Firebase.initializeApp();
SharedPreferences prefs = await SharedPreferences.getInstance();
bool authSignedIn = prefs.getBool('auth') ?? false;
final User user = _auth.currentUser;
if (authSignedIn == true) {
if (user != null) {
uid = user.uid;
name = user.displayName;
imageUrl = user.photoURL;
}
}
}
Future<String> signInWithGoogle() async {
// Initialize Firebase
await Firebase.initializeApp();
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final UserCredential userCredential = await _auth.signInWithCredential(credential);
final User user = userCredential.user;
if (user != null) {
// Checking if email and name is null
assert(user.uid != null);
assert(user.displayName != null);
assert(user.photoURL != null);
uid = user.uid;
name = user.displayName;
imageUrl = user.photoURL;
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final User currentUser = _auth.currentUser;
assert(user.uid == currentUser.uid);
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool('auth', true);
return 'Google sign in successful, User UID: ${user.uid}';
}
return null;
}
void signOutGoogle() async {
await googleSignIn.signOut();
await _auth.signOut();
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool('auth', false);
uid = null;
name = null;
imageUrl = null;
print("User signed out of Google account");
}
I tried a lot of things but did not get any solution pls help!.Should I place the navigator function anywhere else or is there some other error pls help!.
Try doing the flow conditions in initialRoute only
Since the firebase has updated the way we check the user is logged in or not. Its not a async task so you can use directly in the MyApp class.
initialRoute: FirebaseAuth.instance.currentUser != null
? HomeScreen.route_name
: AuthScreen.route_name
Or you can use the listener for auth change
FirebaseAuth.instance.onAuthStateChanged.listen((firebaseUser) {
// do whatever you want based on the firebaseUser state
});
so when the auth is changed it will re-direct it to the page you want to, like this
home: StreamBuilder(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (streamContext, userSnapshot) {
if (userSnapshot.connectionState == ConnectionState.waiting)
return SplashScreen();
if (userSnapshot.hasData) {
return HomeScreen();
}
return AuthScreen();
},
),

How to edit a user in flutter firestore

I have been learning how does flutter work with firestore and now I am working in user auth with password, email and username, when a user is created the email and password are saved with an uid but the username and the email(again) are saved in firestore with a different uid, by the way I have tried a lot of things to make it have the same id but I currently cant find the way. in addition to this, there is also a function that is supposed to edit the username and save those changes. The problem comes when trying to implement the edit functinality because the edit form doesnt return anything as an output except the loading screen, I think this error is happening because of the uids. How can I fix this problem?
models/user.dart
class CustomUser {
final String uid;
CustomUser({this.uid});
}
class UserData {
final String uid;
final String name;
UserData({this.uid, this.name});
}
models/username.dart
class Username {
final String name;
Username({this.name});
}
services/auth.dart
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// create user obj based on fb user
CustomUser _userFromFirebaseUser(User user) {
return user != null ? CustomUser(uid: user.uid) : null;
}
Stream<CustomUser> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
//signin email password
Future signInWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
//signup
Future registerWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
//signout
Future signOut() async {
try {
return await _auth.signOut();
} catch (e) {
print(e.toString());
return null;
}
}
services/database.dart
class DatabaseService {
final String uid;
DatabaseService({this.uid});
final CollectionReference userCollection = FirebaseFirestore.instance.collection('usernames');
Future updateUserData(String name) async { // this is the function that has to edit the username
return await userCollection.doc(uid).set({
'name': name,
});
}
Future uploadUserInfo(userMap) async { // this function adds username and email to firestore
return await userCollection.doc(uid).set(userMap);
}
List<Username> _usernameListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((doc) {
return Username(
name: doc.data()['name'] ?? '',
);
}).toList();
}
// userData from snapshot
UserData _userDataFromSnapshot(DocumentSnapshot snapshot) {
return UserData(
uid: uid,
name: snapshot.data()['name'],
);
}
Stream<List<Username>> get usernames {
return userCollection.snapshots().map(_usernameListFromSnapshot);
}
Stream<UserData> get userData {
return userCollection.doc(uid).snapshots().map(_userDataFromSnapshot);
}
}
register.dart(code that registers the user with a username)
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result = await _auth.registerWithEmailAndPassword(email, password).then((val) {
Map<String, String> userInfoMap = {
"name": name,
"email": email,
};
databaseService.uploadUserInfo(userInfoMap);
});
if (result == null) {
setState(() {
error = 'please suply a valid email';
loading = false;
});
}
}
}),
editForm.dart
final _formKey = GlobalKey<FormState>();
String _currentName;
final user = Provider.of<CustomUser>(context);
StreamBuilder<UserData>(
stream: DatabaseService(uid: user.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData userData = snapshot.data;
return Form(
key: _formKey,
child: Column(
children: <Widget>[
Text('edit username!'),
SizedBox(
height: 30,
),
TextFormField(
// initialValue: userData.user gives a initial text to the input
validator: (val) => val.isEmpty ? 'Please enter a name' : null,
onChanged: (val) => setState(() => _currentName = val),
),
RaisedButton(
child: Text('Save'),
onPressed: () async {
if (_formKey.currentState.validate()) {
print('update if good');
await DatabaseService(uid: user.uid).updateUserData(
_currentName ?? userData.name,
);
}
Navigator.pop(context);
})
],
));
} else {
return Loading();
}
},
);
If you have any questions please let me know;)
In your register.dart, the registerWithEmailAndPassword method returns a User object which contains the uid internally created by FirebaseAuth however, it doesn't seem like you took used this uid to update your Firestore user document. I've implemented a sample of what should have been done below.
dynamic result = await _auth.registerWithEmailAndPassword(email, password).then((val) {
Map<String, String> userInfoMap = {
"name": name,
"email": email,
};
DatabaseService(uid: val.uid).uploadUserInfo(userInfoMap);
});
I just realized that your registerWithEmailAndPassword function returns a CustomUser instead of a Firebase User. I just modified it to make it work.
//signup
Future<User> registerWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
return result.user;
} catch (e) {
print(e.toString());
return null;
}
}
//editFrom.dart
//form validation function
Map<String, String> userMap = {'name': currentName};
await DatabaseService(uid: user.uid).uploadUserInfo(userMap);
Side note: when working with Futures, it helps if you specify the expected return type as this will help you with debugging. I've done it for the function above

Resources