Strange error using Flutter app + Deno + MongoDB backend - mongodb

I want to write a simple registration function and this is my code:
auth_screen.dart:
Future<void> _submit() async {
if (!_formKey.currentState!.validate()) {
// Invalid!
return;
}
_formKey.currentState!.save();
setState(() {
_isLoading = true;
});
try {
if (_authMode == AuthMode.Login) {
// Log user in
await Provider.of<Auth>(context, listen: false).login(
_authData['email'] as String,
_authData['password'] as String,
);
} else {
// Sign user up
await Provider.of<Auth>(context, listen: false).signup(
_authData['email'] as String,
_authData['password'] as String,
);
}
} on HttpException catch (error) {
var errorMessage = 'Authentication failed';
print("this is the auth data");
print(_authData);
if (error.toString().contains('EMAIL_EXISTS')) {
errorMessage = 'This email address is already in use.';
} else if (error.toString().contains('INVALID_EMAIL')) {
errorMessage = 'This is not a valid email address';
} else if (error.toString().contains('WEAK_PASSWORD')) {
errorMessage = 'This password is too weak.';
} else if (error.toString().contains('EMAIL_NOT_FOUND')) {
errorMessage = 'Could not find a user with that email.';
} else if (error.toString().contains('INVALID_PASSWORD')) {
errorMessage = 'Invalid password.';
}
_showErrorDialog(errorMessage);
} catch (error) {
var errorMessage = 'Could not authenticate you. Please try again later.' +
error.toString();
_showErrorDialog(errorMessage);
}
setState(() {
_isLoading = false;
});
}
auth.dart:
Future<void> signup(String email, String password) async {
return _authenticate(email, password, 'register');
}
Future<void> _authenticate(
String email, String password, String urlSegment) async {
final url = Uri.parse('http://10.0.2.2:8000/api/$urlSegment');
// final url = Uri.http('http://localhost:8000/api/', 'urlSegment');
try {
final response = await http.post(
url,
body: json.encode(
{
'email': email,
'password': password,
//'returnSecureToken': true,
},
),
);
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
_token = responseData['idToken'];
_userId = responseData['localId'];
_expiryDate = DateTime.now().add(
Duration(
seconds: int.parse(
responseData['expiresIn'],
),
),
);
_autoLogout();
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode(
{
'token': _token,
'userId': _userId,
'expiryDate': _expiryDate!.toIso8601String(),
},
);
prefs.setString('userData', userData);
} catch (error) {
throw error;
}
}
This is the backend(Deno) part of the project:
auth_controller.ts:
async register(ctx: RouterContext) {
const { email, password } = await ctx.request.body().value;
let user = await User.findOne({ email });
if (user) {
ctx.response.status = 422;
ctx.response.body = { message: "Email is already exist" };
return;
}
const hashedPassword = hashSync(password);
user = new User({ email, password: hashedPassword });
await user.save();
ctx.response.status = 201;
ctx.response.body = {
id: user.id,
name: user.name,
email: user.email
};
}
And this is the user.ts class:
export default class User extends BaseModel {
public id: string = "";
public name: string = "";
public email: string = "";
public password: string = "";
constructor({ id = "", name = "", email = "", password = "" }) {
super();
this.id = id;
this.name = name;
this.email = email;
this.password = password;
}
static async findOne(params: object): Promise<User | null> {
const user = await userCollection.findOne(params);
if (!user) {
return null;
}
return new User(User.prepare(user));
}
async save() {
const _id = await userCollection.insertOne(this);
this.id = _id;
return this;
}
}
I get this error message when I want to test the application on Android emulator:
type 'Null' is not a subtype of type 'String'
When I try the backend server using Postman and send post request to http://0.0.0.0:8000/api/register address. I get correct response and it works, but I don't know why do I get Null response using the Flutter app?
I tried to print the variables in both front and backend side of the application and it seems they all are good and correct but I can not understand why do I get this error message?!

probably you got a null value in your map in this part since map return a null value when can't find a key, check if the Map value is not null
try {
if (_authMode == AuthMode.Login) {
// Log user in
await Provider.of<Auth>(context, listen: false).login(
_authData['email'] as String, // can return a null value
_authData['password'] as String, // can return a null value
);
} else {
// Sign user up
await Provider.of<Auth>(context, listen: false).signup(
_authData['email'] as String, // can return a null value
_authData['password'] as String, // can return a null value
);
}
}

Seems you're getting a null as a response value instead of a String.
EDIT:
Future<void> signup(String email, String password) async {
return _authenticate(email, password, 'register');
}
Check email and password are not null. And getting the correct values.

Actually the problem was from these two lines of code:
_token = responseData['idToken'];
_userId = responseData['localId'];
Because it wasn't passing these data in response from the backend side.

Related

Flutter Riverpod Firebase currentUser Provider not updated

I'm beginner in Flutter, Riverpod for the state management and firebase for the authentication.
I'm looking to retrieve the logged user's email to pass to my postgres database and retrieve all the user information. In a first time, I just try to display the nickname of the current user. I am facing a problem when I log out of the app to log back in. The auth providers are not updated so I get informations from the very first connected user. For example currentUserEmailProvider still get the first connected user email. Any help is welcome, I'm really stuck.
My auth_repository.dart:
class AuthRepository {
const AuthRepository(this._auth);
final FirebaseAuth _auth;
Stream<User?> get authStateChange => _auth.idTokenChanges();
Stream<User?>get authUserChange => _auth.userChanges();
String? get currentUserEmail => _auth.currentUser?.email;
Future<User?> signInWithEmailAndPassword(
String email, String password) async {
_auth.currentUser?.reload();
try {
final result = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
return result.user;
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
throw const ErrorHandler(message: 'User not found');
} else if (e.code == 'wrong-password') {
throw const ErrorHandler(message : 'Wrong password');
} else {
throw const ErrorHandler(message: 'An error occurred. Please try again later');
}
}
}
Future<AppUser?> registerWithEmailAndPassword(String email, String password, String nickname, String role, String firstname, String lastname) async {
// Sans ces deux lignes la création d'un nouveau compte entraîne un login automatique sur ce compte
FirebaseApp app = await Firebase.initializeApp(
name: 'Secondary', options: Firebase.app().options);
try {
AppUser? appUser = await UserRepository(email).saveUser(email, nickname, role, firstname, lastname);
if(appUser != null) {
try {
UserCredential result =
await FirebaseAuth.instanceFor(app: app).createUserWithEmailAndPassword(email: email, password: password);
User? user = result.user;
if(user == null) {
throw Exception("user from firebase not found");
}
return appUser;
} on FirebaseException catch(e) {
await UserRepository(email).deleteUser(email);
print(ErrorHandler(message: e.code.toString()));
}
} else {
throw Exception("user from postgres database not found");
}
} on PostgreSQLException catch(e) {
print(ErrorHandler(message: e.code.toString()));
}
return null;
}
Future<void> signOut() async {
await _auth.signOut();
}
}
My user_repository.dart:
class UserRepository {
final String email;
PostgreSQLConnection? connection;
UserRepository(this.email){
connection = (connection == null || connection!.isClosed == true
? PostgreSQLConnection(
'10.0.2.2', 5432, DatabaseAccess.databaseName,
queryTimeoutInSeconds: 3600,
timeoutInSeconds: 3600,
username: DatabaseAccess.databaseUser,
password: DatabaseAccess.databasePassword) : connection);
}
}
Future<AppUser?> getCurrentUser(String? currentEmail) async {
print(currentEmail);
try {
await connection!.open();
final result = await connection!.mappedResultsQuery(
'select * from public.user where email = #emailValue',
substitutionValues: {
'emailValue': currentEmail,
},
allowReuse: true,
timeoutInSeconds: 30,
);
final userFromDataBase = result[0]['user']!;
return AppUser(
email: userFromDataBase['email'],
nickname: userFromDataBase['nickname'],
role: userFromDataBase['role'],
firstname: userFromDataBase['firstname'],
lastname: userFromDataBase['lastname'],
);
} on PostgreSQLException catch(e) {
print(ErrorHandler(message: e.toString()));
return null;
}
}
}
My providers.dart:
final authRepositoryProvider = Provider<AuthRepository>((ref) {
return AuthRepository(FirebaseAuth.instance);
});
final authStateProvider = StreamProvider<User?>((ref) {
return ref.read(authRepositoryProvider).authStateChange;
});
final currentUserEmailProvider = Provider<String?>((ref) {
return AuthRepository(FirebaseAuth.instance).currentUserEmail;
});
final userRepositoryProvider = Provider.autoDispose<UserRepository>((ref) {
return UserRepository(ref.read(currentUserEmailProvider)!);
});
final futureCurrentUserProvider = Provider<Future<AppUser?>>((ref) {
return ref.read(userRepositoryProvider).getCurrentUser(ref.read(currentUserEmailProvider));
});
final currentUserProvider = FutureProvider.autoDispose<AppUser?>((ref) => ref.read(userRepositoryProvider).getCurrentUser(ref.read(currentUserEmailProvider)));
My home_screen.dart:
class HomeScreen extends HookConsumerWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final currentUser = ref.watch(currentUserProvider);
return Scaffold(
body: currentUser.when(
data: (user) => _buildBody(context, user, ref),
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, _) => _errorBody(context, ref),
)
);
}
Widget _buildBody(BuildContext context, AppUser? user, WidgetRef ref) {
if(user == null) {
return _errorBody(context, ref);
} else {
return Center(child: Text(
'Welcome ${user.getNickname}',
style: const TextStyle(fontSize: 20),
));
}
}
Widget _errorBody(BuildContext context, WidgetRef ref) {
return const Center(child: Text(
"Error: No user found",
style: TextStyle(fontSize: 20, color: Colors.red),
));
}
}
Try changing
final currentUserProvider = FutureProvider.autoDispose<AppUser?>((ref) => ref.read(userRepositoryProvider).getCurrentUser(ref.read(currentUserEmailProvider)));
to
final currentUserProvider = FutureProvider.autoDispose<AppUser?>((ref) => ref.read(userRepositoryProvider).getCurrentUser(ref.watch(currentUserEmailProvider)));
and changing
final currentUserEmailProvider = Provider<String?>((ref) {
return AuthRepository(FirebaseAuth.instance).currentUserEmail;
});
to
final currentUserEmailProvider = Provider<String?>((ref) {
return ref.read(authRepositoryProvider).currentUserEmail;
});
Ok I resolved my problem with this following. To be simplier I removed the singleton in my user_repository.dart. The problem was from my providers and I found a way to use the authUserChanges() method :
providers.dart :
final futureCurrentUserProvider = Provider<Future<AppUser?>>((ref) {
return UserRepository().getCurrentUser(ref.watch(emailChangeProvider));
});
final currentUserProvider = FutureProvider.autoDispose<AppUser?>((ref) => UserRepository().getCurrentUser(ref.watch(emailChangeProvider)));
final authChangeProvider = StreamProvider<User?>((ref) {
return ref.read(authRepositoryProvider).authUserChange;
});
final emailChangeProvider = Provider<String?>((ref) {
return ref.watch(authChangeProvider).value?.email;
});

I'm getting errors for my controller listener in Flutter

I am building a password manager flutter application and I'm using Firestore database to store the data(passwords) entered by the users. I have created 4 different text fields on my Flutter application and on the firestore database, namely : title, email, userpassword and url.
I am getting some errors for my listeners.
void _titleControllerListener() async {
final password = _password;
if (password == null) {
return;
}
final title = _titleController.text;
await _passwordsService.updatePassword(
documentId: password.documentId,
title: title,
);
}
void _emailControllerListener() async {
final password = _password;
if (password == null) {
return;
}
final email = _emailController.text;
await _passwordsService.updatePassword(
documentId: password.documentId,
email: email,
);
}
void _userPasswordControllerListener() async {
final password = _password;
if (password == null) {
return;
}
final userpassword = _userPasswordController.text;
await _passwordsService.updatePassword(
documentId: password.documentId,
userpassword: userpassword,
);
}
void _urlControllerListener() async {
final password = _password;
if (password == null) {
return;
}
final url = _urlController.text;
await _passwordsService.updatePassword(
documentId: password.documentId,
url: url,
);
}
Errors: These 4 four errors are repeated for a total of 24 errors.
The named parameter 'url' is required, but there's no corresponding argument.
Try adding the required argument.
The named parameter 'email' is required, but there's no corresponding argument.
Try adding the required argument.
The named parameter 'userpassword' is required, but there's no corresponding argument.
Try adding the required argument.
The named parameter 'title' is required, but there's no corresponding argument.
Try adding the required argument.
update password() code(I'm not getting any errors here).
Future<void> updatePassword({
required String documentId,
required String title,
required String email,
required String userpassword,
required String url,
}) async {
try {
await passwords.doc(documentId).update({titleFieldName: title});
await passwords.doc(documentId).update({emailFieldName: email});
await passwords.doc(documentId).update({userpasswordFieldName: userpassword});
await passwords.doc(documentId).update({urlFieldName: url});
} catch (e) {
throw CouldNotUpdatePasswordException();
}
}
Pay attention that when you are calling:
await _passwordsService.updatePassword(
documentId: password.documentId,
url: url,
);
You are not passing all required parameters. You have various calls to this method and each time you are calling it with different arguments.
I.E.
void _userPasswordControllerListener() async {
final password = _password;
if (password == null) {
return;
}
final userpassword = _userPasswordController.text;
await _passwordsService.updatePassword( //// <----
documentId: password.documentId,
userpassword: userpassword,
);
}
The method updatePassword has five required arguments:
updatePassword({
required String documentId, /// <-------
required String title, /// <-------
required String email, /// <-------
required String userpassword, /// <-------
required String url, /// <-------
})
so you must pass all of them.
An example call could be:
await _passwordsService.updatePassword(
documentId: password.documentId,
userpassword: userpassword,
title: YOUR_TITLE,
url: YOUR_URL,
email: YOUR_EMAIL
);
In updatePassword method you specified title, email, userpassword and url as required.
You can make them optional:
Future<void> updatePassword({
required String documentId,
String? title,
String? email,
String? userpassword,
String? url,
}) async {
try {
if (title != null) await passwords.doc(documentId).update({titleFieldName: title});
if (email != null) await passwords.doc(documentId).update({emailFieldName: email});
if (userpassword != null) await passwords.doc(documentId).update({userpasswordFieldName: userpassword});
if (url != null) await passwords.doc(documentId).update({urlFieldName: url});
} catch (e) {
throw CouldNotUpdatePasswordException();
}
}
I was able to solve the errors by splitting the updatePassword() code into 4 different functions. 1 function for each text field.
Future<void> updatePasswordTitle({
required String documentId,
required String title,
}) async {
try {
await passwords.doc(documentId).update({titleFieldName: title});
} catch (e) {
throw CouldNotUpdatePasswordException();
}
}
Future<void> updatePasswordEmail({
required String documentId,
required String email,
}) async {
try {
await passwords.doc(documentId).update({emailFieldName: email});
} catch (e) {
throw CouldNotUpdatePasswordException();
}
}
Future<void> updatePasswordUserpassword({
required String documentId,
required String userpassword,
}) async {
try {
await passwords.doc(documentId).update({userpasswordFieldName: userpassword});
} catch (e) {
throw CouldNotUpdatePasswordException();
}
}
Future<void> updatePasswordUrl({
required String documentId,
required String url,
}) async {
try {
await passwords.doc(documentId).update({urlFieldName: url});
} catch (e) {
throw CouldNotUpdatePasswordException();
}
}
After creating the above 4 updatePassword functions. I made changes to the controller listeners. So, that they call their own updatePassword function.
void _titleControllerListener() async {
final password = _password;
if (password == null) {
return;
}
final title = _titleController.text;
await _passwordsService.updatePasswordTitle(
documentId: password.documentId,
title: title,
);
}
void _emailControllerListener() async {
final password = _password;
if (password == null) {
return;
}
final email = _emailController.text;
await _passwordsService.updatePasswordEmail(
documentId: password.documentId,
email: email,
);
}
void _userPasswordControllerListener() async {
final password = _password;
if (password == null) {
return;
}
final userpassword = _userPasswordController.text;
await _passwordsService.updatePasswordUserpassword(
documentId: password.documentId,
userpassword: userpassword,
);
}
void _urlControllerListener() async {
final password = _password;
if (password == null) {
return;
}
final url = _urlController.text;
await _passwordsService.updatePasswordUrl(
documentId: password.documentId,
url: url,
);
}

"on HttpException catch (error)" desn't work in flutter

I made this code to handle error from the server Firebase with flutter :
This is the main function :
try {
if (_authMode == AuthMode.Login) {
print("log in");
await Provider.of<Auth>(context, listen: false)
.signIn(_authData['email'], _authData['password']);
} else {
await Provider.of<Auth>(context, listen: false)
.signUp(_authData['email'], _authData['password']);
}
} on HttpException catch (error) {
print("Check error");
if (error.toString().contains("EMAIL_EXISTS")) {
_ServerError =
"The email address is already in use by another account.";
}
if (error.toString().contains("TOO_MANY_ATTEMPTS_TRY_LATER")) {
_ServerError =
"We have blocked all requests from this device due to unusual activity.\n Try again later.";
} else {
_ServerError = "Something wrong. \n Try again later!";
}
} catch (error) {
print(error.toString() );
}
This is the called function :
Future<void> signIn(String? email, String? password) async {
const _url =
"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=APICODE";
_authentication(_url, email, password);}
Future<void> _authentication(
String _url, String? email, String? password) async {
try {
final _response = await http.post(Uri.parse(_url),
body: json.encode({
'email': email,
'password': password,
'returnSecureToken': true
}));
final _responseData = json.decode(_response.body);
if (_responseData['error'] != null) {
throw HttpException(_responseData['error']['message']);
}
} catch (error) {
throw error;
}}
But the problem is when the called function throw the HttpException error, I don't get it in the main function because the Catch doesn't work because I don't get the message "check error" in the panel ?!
this is the panel :
Can you help me please ?
The problem is I forgot to add return to called function :
Future<void> signIn(String? email, String? password) async {
const _url =
"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=APICODE";
return _authentication(_url, email, password);
}

Flutter, firebase. I want to show my registration form data other than the email and password and connect to a unique uid

Register Screen On Pressed method given, I believe there is a problem with calling Firebase user = result.user
onPressed: () async {
if(_formKey.currentState.validate()){
setState(() => loading = true);
dynamic result = await _auth.registerWithEmailAndPassword(email, password);
FirebaseUser user = result.user;
await DatabaseService(uid: user.uid).newUserInfo(
_nameC.text,
_cityC.text,
_contactnoC.toString()
);
if(result == null) {
setState(() {
error = 'Please supply a valid email';
loading = false;
});
}}},
// Database backend
class DatabaseService {
final String uid;
DatabaseService ({this.uid});
final CollectionReference userdata2 = Firestore.instance.collection('UserData');
Future newUserInfo(String name, String city, String contactno) async {
return await userdata2.document(uid).setData({
'name' : name,
'city' : city,
'contactno' : contactno
});
}}
// authentication backend
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
DatabaseService(uid: user.uid);
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
} }
// user.dart
class User {
final String uid;
User({this.uid});
}

Cannot access function parameters from async function

In the moment of registering, when I try to print the username which is a parameter of the function, it just won't print anything on the screen and I cannot understand why.
Future registerWithEmailAndPassword(String username, String email, String password) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
// create a new document for the user with the uid
print(username);
await DatabaseService(uid: user.uid).updateUserData(username);
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
}
}
Edit: this is the function which calls the registerWithEmailAndPassword function:
onPressed: () async {
if (error.length > 0)
error = "";
if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result = await _auth.registerWithEmailAndPassword(username, email, password);
if (result == null){
if (this.mounted){
setState(() {
error = 'Please supply a valid email';
loading = false;
});}
}
}
}