The getter 'uid' not defined - firebase

i'm trying to create a food track app on android studio, it's my first time and i'm working with firebase_auth 3.3.12. my code in the aut.dart is:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:my_firstapp/models/user_model.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
AuthService();
// create user object based on FirebaseUser.
UserModel _userFromUser(User) {
return user != null ? UserModel(uid: user.uid) : null;
}
// auth change user stream
Stream<UserModel> get user {
return _auth.authStateChanges()
.map(_userFromUser);
}
Future<UserModel> getUser() async {
User user = await _auth.currentUser();
return _userFromUser(user);
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password);
User user = result.user;
return _userFromUser(user);
} catch(e) {
print(e.toString());
return null;
}
}
// sign up with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
User user = result.user;
// create a new user document in database
return _userFromUser(user);
} catch(e) {
print(e.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
return await _auth.signOut();
} catch(e){
print(e.toString());
return null;
}
}
}
However i'm getting 2 errors:
-The getter 'uid' isn't defined for the type 'Stream';
-The expression "await _auth.currentUser()" doesn't evaluate to a function, so it can't be invoked.
How can i rewrite the code? thanks

The _auth.currentUser is not a function (it used to be, but changed about a year ago), but rather a property. It also isn't asynchronous, so you don't need await nor to return a Future.
So:
UserModel getUser() {
User user = _auth.currentUser;
return _userFromUser(user);
}

In this code, your argument is capitalised ('User') but in the code block you write 'user'.
UserModel _userFromUser(User) {
return user != null ? UserModel(uid: user.uid) : null;
}
Furthermore, for _auth.currentUser(), you do not need to use await as it does not return a future.

Related

Is there a way to use the results of a catch block inside a widget in Flutter

I am building a flutter app with Firebase as the back end.
I have created an AuthService class on a separate file and import and use the Auth functions inside the log in screen.
This is my AuthService Class.
class AuthService {
Future<UserModel?> signInWithEmailAndPassword(
String email, String password) async {
try {
final cred = await _auth.signInWithEmailAndPassword(
email: email, password: password);
return _userFromFirebase(cred.user);
} on auth.FirebaseAuthException catch (e) {
print(e.toString());
return null;
}
}
}
In the sign in page, I initialize the function:
final auth = Provider.of<AuthService>(context);
Then use it in an onPressed :
press: () async {
// SIGN IN WITH EMAIL AND PASSWORD
dynamic result =
await auth.signInWithEmailAndPassword(
email, password);
// IF SIGN IN FAILS
if (result == null) {
setState(() {
errorSigningIn = 'Sign in error';
//this is where I want to use the error response.
});
}
},
I am stuck on using the error I catch in the signInWithEmailAndPassword function and assigning it to the errorSigningIn variable in the SignIn widget.
I am new to this, please help.
Thanks.
You can create your own class to handle auth results. For example:
class AuthResult {
final int code;
final UserModel? user;
final String? errorMessage;
AuthResult(this.code, {
this.user,
this.errorMessage,
});
}
This class can help you to handle all cases of logging in. And this is what you should do with your signing in method:
class AuthService {
Future<AuthResult> signInWithEmailAndPassword(
String email, String password) async {
try {
final cred = await _auth.signInWithEmailAndPassword(
email: email, password: password);
return AuthResult(200, user: _userFromFirebase(cred.user));
} on auth.FirebaseAuthException catch (e) {
print(e.toString());
return AuthResult(0 /*<-- your error result code*/, e.toString());
}
}
}
And, finally, your onPressed:
press: () async {
// SIGN IN WITH EMAIL AND PASSWORD
AuthResult result =
await auth.signInWithEmailAndPassword(
email, password);
// IF SIGN IN FAILS
if (result.code != 200) {
setState(() {
errorSigningIn = result.errorMessage; //<-- Get your error message
//this is where I want to use the error response.
});
}
},

Flutter - Get Firebase custom claims while writing Firebase user to own user instance

I am trying to implement the example given at How do I access custom claims? to my existing code.
I have a Stream which listens to auth changes and updates my own user object with the responded Firebase user. When I store my user object, I would like to get the custom claims of that user as well.
The problem is in _userFromFirebaseUser.
It says "The await expression can only be used in an async function.
Try marking the function body with either 'async' or 'async*'."
But when I do so, the error is hops to my stream where it then says "The argument type 'Future Function(User)' can't be assigned to the parameter type 'User Function(User)'." for "_userFromFirebaseUser" in
// auth change user stream
Stream<local.User> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
Here is my complete authentication class:
import 'package:<my-pckg>/models/user.dart' as local;
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:<my-pckg>/services/database.dart';
//import 'package:shared_preferences/shared_preferences.dart';
class AuthService {
final auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;
// create user obj based on firebase user
local.User _userFromFirebaseUser(auth.User user) {
final isAdmin = (await _currentUserClaims)['admin'] == true;
return user != null
? local.User(
uid: user.uid,
email: user.email,
displayName: user.displayName,
isAdmin: isAdmin)
: null;
}
// auth change user stream
Stream<local.User> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
// sign in anon
Future signInAnon() async {
try {
auth.UserCredential result = await _auth.signInAnonymously();
auth.User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
print('Successfully logged in, User UID: ${user.uid}');
return user;
} catch (error) {
print(error.toString());
return null;
}
}
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
// create a new document for the user with the uid
await DatabaseService(uid: user.uid).updateUserData(null);
print('Successfully registered, User UID: ${user.uid}');
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
print('User signed out');
return await _auth.signOut();
} catch (error) {
print(error.toString());
return null;
}
}
Future<Map<dynamic, dynamic>> get _currentUserClaims async {
final user = _auth.currentUser;
// If refresh is set to true, a refresh of the id token is forced.
final idTokenResult = await user.getIdTokenResult(true);
return idTokenResult.claims;
}
}
Am I heading into the wrong direction? Is there anything obvious, that I simply do not consider?
Thanks for your help!
For those, heading into the same problem, I found the solution after further research:
You will have to change the .map to .asyncMap.
Here is the code, which works for me:
import 'package:<my-pckg>/models/user.dart' as local;
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:<my-pckg>/services/database.dart';
//import 'package:shared_preferences/shared_preferences.dart';
class AuthService {
final auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;
// create user obj based on firebase user
Future<local.User> _userFromFirebaseUser(auth.User user) async {
final isAdmin = (await _userClaims)['admin'] == true;
return user != null
? local.User(
uid: user.uid,
email: user.email,
displayName: user.displayName,
isAdmin: isAdmin)
: null;
}
// auth change user stream
Stream<local.User> get user {
return _auth.authStateChanges().asyncMap(_userFromFirebaseUser);
}
// sign in anon
Future signInAnon() async {
try {
auth.UserCredential result = await _auth.signInAnonymously();
auth.User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
print('Successfully logged in, User UID: ${user.uid}');
return user;
} catch (error) {
print(error.toString());
return null;
}
}
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
// create a new document for the user with the uid
await DatabaseService(uid: user.uid).updateUserData(null);
print('Successfully registered, User UID: ${user.uid}');
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
print('User signed out');
return await _auth.signOut();
} catch (error) {
print(error.toString());
return null;
}
}
Future<Map<dynamic, dynamic>> get _userClaims async {
final user = _auth.currentUser;
// If refresh is set to true, a refresh of the id token is forced.
final idTokenResult = await user.getIdTokenResult(true);
return idTokenResult.claims;
}
}
Found here: In flutter, how can I "merge" Firebase onAuthStateChanged with user.getTokenId() to return a Stream?

Getting Boolean State from Cloud_Firestore in dart

I'm making an app that needs to display two separate home pages depending on whether or not a counselor value equals true on my Cloud Firestore Database. I am new to Object Oriented Programing, dart, and Firestore so please bear with me.
What I'm trying to do is initialize a variable called counselor and set that equal to the Counselor field value on my database. Then I wish to first check if the user has signed in and if they have signed it that's when I use a series of if statements to see whether or or not the counselor boolean equals true or false. and depending on the result it will display a certain homepage.
I get an error message on my app saying that the I'm returning a null value on my counselor widget. I suspect this is because I'm setting the counselor variable to equal the name of the field Counselor on my database and not it's actual boolean value. Problem is I'm not aware of the syntax to circumvent this problem.
Here is my code
import 'package:strength_together/models/user.dart';
import 'package:strength_together/Screens/authenticate/authenticate.dart';
import 'package:strength_together/Screens/home/home.dart';
import 'package:strength_together/Screens/home/wrapper.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:strength_together/services/database.dart';
import 'package:strength_together/Screens/home/counsHome.dart';
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<SUser>(context);
bool counselor = getCounselor();
//return either authenticate or home/couselor home
if (user == null){
return Authenticate();
}else if(counselor == true){
return CounselorHome();
}else if(counselor == false){
return Home();
}
}
}
// Database class
import 'package:cloud_firestore/cloud_firestore.dart';
class DatabaseService {
final String uid;
DatabaseService({this.uid});
//collection reference
final CollectionReference userCollection = FirebaseFirestore.instance
.collection('User Data');
Future updateUserData(String name, bool counselor) async {
return await userCollection.doc(uid).set({
'name': name,
'Counselor': true,
});
}
//get user data stream
Stream<QuerySnapshot> get userData {
return userCollection.snapshots();
}
Future<DocumentSnapshot> getDataSnapshotForCounselor() async
{
return await FirebaseFirestore.instance.collection('User Data')
.doc(uid)
.get();
}
bool getCounselorValue(DocumentSnapshot dataSnapshotForCounselor) {
//modify this by passing proper keyValue to get counselor.
return dataSnapshotForCounselor.data()['Counselor'];
}
}
// auth class
import 'package:firebase_auth/firebase_auth.dart';
import 'package:strength_together/Screens/authenticate/register.dart';
import 'package:strength_together/models/user.dart';
import 'package:strength_together/services/database.dart';
import 'package:flutter/material.dart';
class AuthService{
final FirebaseAuth _auth = FirebaseAuth.instance;
//instance of counselor
bool _counselor;
bool get counselor => counselor;
void setCounselor(bool counselor) {
_counselor = counselor;
}
//create user obj based on firebase user from my code
SUser _userFromFirebaseUser(User user){
return user != null ? SUser(uid: user.uid) : null;
}
//auth change user stream
Stream<SUser> get user {
return _auth.authStateChanges()
.map(_userFromFirebaseUser);
}
//method to sign in anon
Future signInAnon() async{
try{
UserCredential result = await _auth.signInAnonymously();
User user = result.user;
return _userFromFirebaseUser(user);
}catch(e){
print(e.toString());
return null;
}
}
//method to sign in with email/pass
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;
}
}
//method to register with email/pass
Future registerWithEmailAndPassword(String email, String password) async{
try{
UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
User user = result.user;
//create a new document for that user with the uid
await DatabaseService(uid: user.uid).updateUserData('New user', _counselor);
return _userFromFirebaseUser(user);
}catch(e){
print(e.toString());
return null;
}
}
//sign in with google
//sign out
Future signOut() async{
try{
return await _auth.signOut();
}catch(e){
print(e.toString());
return null;
}
}
There are lots of problems with your code . First of all you are using Firestore.instance and .document which are deprecated . Use FirebaseFirestore.instance and FirebaseFirestore.instance.collection(...).doc(...) instead.
Coming to your code , Use a FutureBuilder to decide which page to show , while deciding , you can show a CircularProgressIndicator to the user.
Next , this code
Firestore.instance.collection('User Data').document('Counselor');
returns a DocumentReference and not the actual value that is required .
So after you get the DocumentReference use .get() which returns a Future<DocumentSnapshot> , DocumentSnapshot class has a method data() which returns Map<String,dynamic>. Once you get the Map<> , you can simply get the data you want by passing the key value.
The whole process would look similar to this :
Future<bool> getCounselorValue() async {
return await FirebaseFirestore.instance.collection(...).doc(...).get().data()['keyvalue'];
}
Aagain , remember you are dealing with a Future so you have to use either await , .then() or FutureBuilder . I recommend FutureBuilder for your case , if you are not sure how to use it, refer to this . I also answered this which might be helpful if you want to dynamically decide which page to show to user first (refer to first method).
Edit:
Ok , so there was a little mistake in my code .Now I am sharing with you a little more code which you might find helpful .
make your class Stateful from Stateless and follow this code :
class Wrapper extends StatefulWidget{
WrapperState createState()=> WrapperState();
}
class WrapperState extends State<Wrapper>
{
Future<DocumentSnapshot> documentSnapshot;
User getUser(BuildContext context)
{
return Provider.of<User>(context);
}
dynamic getStartingPage(bool counselor,User user)
{
if (user == null){
return Authenticate();
}else if(counselor == true){
return CounselorHome();
}else if(counselor == false){
return Home();
}
}
Future<DocumentSnapshot> getDocumentSnapshotForCounselor() async
{
return await FirebaseFirestore.instance.collection(...).doc(...).get();
}
bool getCounselorValue(DocumentSnapshot documentSnapshotForCounselor)
{
//modify this by passing proper keyValue to get counselor.
return documentSnapshotForCounselor.data()['keyValue'];
}
#override
void initState()
{
super.initState();
documentSnapshot=getDocumentSnapshotForCounselor();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future:documentSnapshot,
builder: (BuildContext ctx, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Center( child : const CircularProgressIndicator());
}
else
return getStartingPage(getCounselorValue(snapshot.data),getUser(context));
}
);
}
}
Modify getDocumentSnapshotForCounselor and getCounselorValue methods ,so that you can get the value of the field from the database.
Upgrade to latest version of cloud_firestore , visit this to understand how to do it and what to import .
Edit 2:
When you sign in user ,either by email password or using google sign in etc. It returns a Future<UserCredential> , UserCredential has a property user , which returns User. Now what we want is, a unique uid of the user which we can get using User.uid :
UserCredential userCredential =await _auth.createUserWithEmailAndPassWord();
String uid= userCredential.user.uid;
Now create an entry for the user in the database using this uid:
FirebaseFirestore.instance.collection("User Data").doc(uid).set({"Counselor":true,"name":name});
Afterwards , to get the counselor value , :
FirebaseFirestore.instance.collection("User Data").doc(uid).get();
and
documentSnapshotForCounselor.data()['Counselor'];
The above code is just for an explanation , make appropriate changes to your code.

SignInAnonymously() doesn't return full user information, it returns "FirebaseUser(Instance of 'PlatformUser')"

Instead of returning full user info like UserId and stuff, I receive FirebaseUser(Instance of 'PlatformUser'). What seems to go wrong?
import 'package:firebase_auth/firebase_auth.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// sign in anon
Future signInAnon() async {
try {
AuthResult result = await _auth.signInAnonymously();
FirebaseUser user = result.user;
return user;
} catch (e) {
print(e.toString());
return null;
}
}
// sign in with email & password
// register with email & password
// sign out
}

With Flutter, how can I display Firebase Auth error messages caught within an AuthService class within a separate log in page widget?

I have a Registration Page with my sign up form and then an AuthService class I call to sign up the user, returning a mapped custom User class. I can check if the result of the function call is not null and therefore navigate my user to the home page, but I can't work out how to setState or similar to actually show the user the Firebase Auth messages in my Registration page, as the try/catch block is within my auth service class.
This is my abbreviated Registration screen widget:
class RegistrationScreen extends StatefulWidget {
#override
_RegistrationScreenState createState() => _RegistrationScreenState();
}
class _RegistrationScreenState extends State<RegistrationScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
//abbreviated...
RoundedButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
dynamic result = await _auth.registerWithEmailAndPassword(email, password, displayName);
if (result != null) {
Navigator.pushNamedAndRemoveUntil(context, Home.id, (_) => false);
}
}
}
}
}
The registerWithEmailAndPassword is within an imported AuthService class auth.dart:
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
final _firestore = Firestore.instance;
//create User object
User _userFromFirebaseUser(FirebaseUser user) {
return user != null ? User(uid: user.uid, displayName: user.displayName) : null;
}
//auth change user stream
Stream<User> get user {
return _auth.onAuthStateChanged
.map(_userFromFirebaseUser);
}
// register
Future registerWithEmailAndPassword(String email, String password, String displayName) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
return _userFromFirebaseUser(user);
} catch(e) {
print(e);
}
}
}
If I then test this with a badly formatted email, I correctly print to the console:
flutter: PlatformException(ERROR_INVALID_EMAIL, The email address is badly formatted., null)
However how can I use that PlatformException to setState or similar within my registration screen to show the e.message to the user?
Thanks.
You can create a class like this;
class Errors {
   static String show(String errorCode) {
     switch (errorCode) {
       case 'ERROR_EMAIL_ALREADY_IN_USE':
         return "This e-mail address is already in use, please use a different e-mail address.";
       case 'ERROR_INVALID_EMAIL':
         return "The email address is badly formatted.";
       case 'ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL':
         return "The e-mail address in your Facebook account has been registered in the system before. Please login by trying other methods with this e-mail address.";
       case 'ERROR_WRONG_PASSWORD':
         return "E-mail address or password is incorrect.";
       default:
         return "An error has occurred";
     }
   }
}
And then, when you get PlatformException error, you can show an alert dialog to user like this;
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
return _userFromFirebaseUser(user);
} catch(e) {
print(Errors.show(e.code)); // On this line, call your class and show the error message.
}

Resources