how to wait till firebase function get all necessary values - flutter

i wanted to wait till firebase auth retrive verification_id and then return function
but in current code variable value newVerificationId is getting return first as error without updating from firebase
Future<String> phoneAuthontication(
phoneNumberController,
) async {
String newVerificationId = "error";
try {
await auth.verifyPhoneNumber(
phoneNumber: phoneNumberController.text,
verificationCompleted: (_) {},
verificationFailed: (e) {
print(e);
},
codeSent: (String verificationId, int? token) {
print("====" + verificationId);
newVerificationId = verificationId;
},
codeAutoRetrievalTimeout: (e) {
print(e);
});
} catch (e) {
print(e);
}
print("---" + newVerificationId);
return newVerificationId;
}

Related

In Flutter How to add user id in firebase

how to put the user id in this code when I am generating a to-do list and i need to retrieve the tasks that a certain user?
Future<void> create(String todo, String description) async {
try {
await firestore.collection("Todo").add({
'todo': todo,
'description': description,
'timestamp': FieldValue.serverTimestamp()
});
} catch (e) {
print(e);
}
}
i solved my issue
Future<void> create(String todo, String description) async {
String? Uid= FirebaseAuth.instance.currentUser!.uid;
print(Uid);
print(FirebaseAuth.instance.currentUser!.uid);
try {
await firestore
.collection("TodoList")
.doc(Uid)
.collection("Todo")
.add({
'todo': todo,
'description': description,
'timestamp': FieldValue.serverTimestamp()
});
} catch (e) {
print(e);
}
}
Future<void> signUpController(email, password, username, phone) async {
print("$email,$password,$username,$phone");
try {
CommanDialog.showLoading();
final credential =
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: email.trim(),
password: password,
);
print(credential);
CommanDialog.hideLoading();
try {
CommanDialog.showLoading();
var response =
await FirebaseFirestore.instance.collection('userlist').add({
'user_id': credential.user!.uid,
'user_name': username,
'phone': phone,
'password': password,
'joindate': DateTime.now().millisecondsSinceEpoch,
'email': email
});
print("response:: ${response.toString()}");
CommanDialog.hideLoading();
Get.back();
} catch (execption) {
CommanDialog.hideLoading();
print("error saving data ${execption}");
}
Get.back();
} on FirebaseAuthException catch (e) {
CommanDialog.hideLoading();
if (e.code == 'weak-password') {
CommanDialog.showErrorDialog(
description: "The password provided is too weak.");
print('The password provided is too weak.');
} else if (e.code == 'email-already-in-use') {
CommanDialog.showErrorDialog(
description: "The account already exists for that email.");
print('The account already exists for that email.');
}
} catch (e) {
CommanDialog.hideLoading();
CommanDialog.showErrorDialog(description: "something went wrong");
print(e);
}
}

FlutterFire Auth verifyPhoneNumber method isn't being waited on

I'm trying to create an auth service and I want to return the verificationId from the custom method. However, calling this method throws the null check exception because it doesn't wait for the Future to complete before returning.
Future<String> sendPhoneVerification({
required String phoneNumber,
}) async {
String? result;
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: '+1$phoneNumber',
verificationCompleted: (
PhoneAuthCredential credential,
) {
result = credential.verificationId;
},
verificationFailed: (e) {
if (e.code == 'invalid-phone-number') {
throw InvalidPhoneNumberAuthException();
} else if (e.code == 'too-many-requests') {
throw TooManyRequestsAuthException();
} else {
throw GenericAuthException();
}
},
codeSent: (verificationId, resendToken) {
print('ver_id $verificationId');
result = verificationId;
},
codeAutoRetrievalTimeout: (_) {},
);
print('This is the result $result');
return result!;
}
Here is the output in the terminal.
flutter: This is the result null
flutter: ver_id <ver_id>
Please add this property timeout: const Duration(seconds: 60), in the verifyPhoneNumber() method
I figured out the solution. I found out the verifyPhoneNumber method returns a future but the implementation doesn't await that async call. I used a [completer][1] to return a future.
Future<String> sendPhoneVerification({required String phoneNumber}) async {
Completer<String> result = Completer();
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: '+1$phoneNumber',
verificationCompleted: (
PhoneAuthCredential credential,
) {
result.complete(credential.verificationId);
},
verificationFailed: (e) {
if (e.code == 'invalid-phone-number') {
result.completeError(InvalidPhoneNumberAuthException());
} else if (e.code == 'too-many-requests') {
result.completeError(TooManyRequestsAuthException());
} else {
result.completeError(GenericAuthException());
}
},
codeSent: (verificationId, resendToken) {
result.complete(verificationId);
},
codeAutoRetrievalTimeout: (_) {},
);
return result.future;
}

When to commit a mongo transaction?

below is a code snippet from https://www.mongodb.com/blog/post/quick-start-nodejs--mongodb--how-to-implement-transactions
It works perfectly but here is what I don't understand:
This function didn't call session.commitTransaction(), how does it commit the transaction?
it aborts the transaction by determining if isListingReservedResults is null or not null, but my implementation is to throw an error in the if block and catches the error then calling the session.abortTransaction() (which result in a "MongoError: Cannot call abortTransaction twice"), I wonder Why would this happen since i only call it onece.
async function createReservation(client, userEmail, nameOfListing, reservationDates, reservationDetails) {
const usersCollection = client.db("sample_airbnb").collection("users");
const listingsAndReviewsCollection = client.db("sample_airbnb").collection("listingsAndReviews");
const reservation = createReservationDocument(nameOfListing, reservationDates, reservationDetails);
const session = client.startSession();
try {
const transactionResults = await session.withTransaction(async () => {
const usersUpdateResults = await usersCollection.updateOne(
{ email: userEmail },
{ $addToSet: { reservations: reservation } },
{ session });
const isListingReservedResults = await listingsAndReviewsCollection.findOne(
{ name: nameOfListing, datesReserved: { $in: reservationDates } },
{ session });
if (isListingReservedResults) {
await session.abortTransaction();
return;
// throw new Error('message'); myi mplementaion, throw error here then abort the transaction in catch block
}
const listingsAndReviewsUpdateResults = await listingsAndReviewsCollection.updateOne(
{ name: nameOfListing },
{ $addToSet: { datesReserved: { $each: reservationDates } } },
{ session });
});
if (transactionResults) {
console.log("The reservation was successfully created.");
} else {
console.log("The transaction was intentionally aborted.");
}
} catch(e){
console.log("The transaction was aborted due to an unexpected error: " + e);
// await session.abortTransaction(); result in a "MongoError: Cannot call abortTransaction twice"
} finally {
await session.endSession();
}
}

Flutter await for try catch [duplicate]

This question already has answers here:
How to Async/await in List.forEach() in Dart
(7 answers)
Closed 2 years ago.
I'm using firebase cloud firestore
inside a Future function I have this
try {
categories.forEach((element) async {
await FirebaseFirestore.instance.collection('Categories').add({
'name': element[0],
'imageUrl': element[1],
});
print('done');
});
print('complete');
} catch (e) {
CoolAlert.show(
context: context,
type: CoolAlertType.error,
content: Text(e),
text: "Upload Failed",
onConfirmBtnTap: () {
Navigator.pop(context);
Navigator.pop(context);
});
}
'completed' printed before 'done'
how to make it the opposite?
how to await for the forEach function to end first then proceed
and even if I moved print('complete'); after the whole try catch block it doesn't work either
so is there a way to wait try catch block?
You can use Future.foreach OR Future.doWhile
Future.doWhile :
int index = 0;
try {
Future.doWhile(() {
if (index < categories.length) {
await FirebaseFirestore.instance.collection('Categories').add({
'name': categories[index][0],
'imageUrl': categories[index][1],
});
print('done');
index++;
return true;
} else {
print('complete');
return false;
}
});
} catch (e) {
CoolAlert.show(
context: context,
type: CoolAlertType.error,
content: Text(e),
text: "Upload Failed",
onConfirmBtnTap: () {
Navigator.pop(context);
Navigator.pop(context);
});
}
Future.foreach:
try {
Future.forEach(categories,(element) async {
await FirebaseFirestore.instance.collection('Categories').add({
'name': element[0],
'imageUrl': element[1],
});
print('done');
});
print('complete');
} catch (e) {
CoolAlert.show(
context: context,
type: CoolAlertType.error,
content: Text(e),
text: "Upload Failed",
onConfirmBtnTap: () {
Navigator.pop(context);
Navigator.pop(context);
});
}

How to use Flutter Bloc with Firebase Phone Auth

I'm trying to implement Firebase phone authorization using Flutter Bloc pattern.
I have the following code
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:firebase_auth/firebase_auth.dart';
import './bloc.dart';
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final FirebaseAuth _auth = FirebaseAuth.instance;
#override
AuthState get initialState => AuthNotStarted();
#override
Stream<AuthState> mapEventToState(
AuthEvent event,
) async* {
if (event is VerifyPhone) {
yield* _mapVerifyPhoneToState(event);
}
}
Stream<AuthState> _mapVerifyPhoneToState(VerifyPhone event) async* {
yield AuthStarted();
_auth.verifyPhoneNumber(
phoneNumber: "+" + event.phoneNumber,
timeout: Duration(seconds: 60),
verificationCompleted: (AuthCredential authCredential) {
print("verification completed: auth credential");
},
verificationFailed: (AuthException authException) {
print("verification failed: auth exception");
print(authException.message);
},
codeSent: (String verificationId, [int forceResendingToken]) {
print("code sent verification id" + verificationId);
},
codeAutoRetrievalTimeout: (String verificationId) {
print("auto time" + verificationId);
});
}
}
But i can't use yield inside verifyPhoneNumber callbacks.
The question is how to yield different states inside of callback functions?
You can add an event from your callback. For example, in verificationCompleted, you can do:
verificationCompleted: (AuthCredential authCredential) {
print("verification completed: auth credential");
add(AuthCompleted());
},
And you can handle the AuthCompleted() event on mapEventToState:
#override
Stream<AuthState> mapEventToState(
AuthEvent event,
) async* {
if (event is VerifyPhone) {
yield* _mapVerifyPhoneToState(event);
}
if (event is AuthCompleted){
//Here you can use yield and whathever you want
}
}
PhoneAuthenticationBloc
class PhoneAuthenticationBloc
extends Bloc<PhoneAuthenticationEvent, PhoneAuthenticationState> {
final AuthRepository _authRepository;
final AuthBloc _authBloc;
#override
Stream<PhoneAuthenticationState> mapEventToState(
PhoneAuthenticationEvent event,
) async* {
if (event is PhoneLoadingEvent) {
yield PhoneLoadingState();
} else if (event is PhoneVerificationFailedEvent) {
yield PhoneOTPFailureState(event.failure);
} else if (event is PhoneSmsCodeSentEvent) {
yield PhoneSmsCodeSentState(
verificationId: event.verificationId, resendCode: event.resendId);
} else if (event is PhoneVerifiedOtpEvent) {
yield* _mapToVerifyOtp(event.smsCode, event.verificationId);
}
}
void verifyPhoneNumber(String phoneNumber) async {
try {
add(PhoneLoadingEvent());
await _authRepository.verifyPhoneNumber(phoneNumber,
onRetrieval: (String retrievalCode) {
print("Time Out Retrieval Code: $retrievalCode");
}, onFailed: (Failure f) {
print("OnFailed: ${f.message}");
add(PhoneVerificationFailedEvent(f));
}, onCompleted: (Map<String, dynamic> data) {
print("verificationCompleted: $data");
}, onCodeSent: (String verificationId, int resendCode) {
print("verificationId:$verificationId & resendCode: $resendCode");
add(PhoneSmsCodeSentEvent(
verificationId: verificationId, resendId: resendCode));
});
} catch (e) {
add(PhoneVerificationFailedEvent(Failure(message: e.toString())));
}
}}
UI Screen
builder: (context, state) {
return AppButton(
isLoading: state is PhoneLoadingState,
onPressed: () async {
if (_formKey.currentState.validate()) {
BlocProvider.of<PhoneAuthenticationBloc>(context)
.verifyPhoneNumber(_phoneController.text);
}
},
title: "Continue",
textColor: Colors.white,
);
}