is it possible to use await in a sync function in dart - flutter

I have a function to get the user login status, now I want to add a logic with silent login when checked user did not login.After the user log, I store the login information about user name and password into local secure storage after the user login for the first time, when invoke the islogin function, my code looks like this:
bool get isLogin {
if(this == null){
// not login, do the automatic login logic
final UserAccount userAccount = UserAccount();
final String? phone = await SecureStorageUtil.getString("username");
final String? password = await SecureStorageUtil.getString("password");
if(phone == null || password == null){
return false;
}
final Result<Map> result = await userAccount.login(phone, password);
if(result != null){
return true;
}
}
return this != null;
}
what make me stuck is that the fetch credential information and login was async and have to using await keywords to wait the return. But the await keyword only allow in async function.
The await expression can only be used in an async function.
If I change the isLogin function to async, many places in this project must change. I was wonder is it possible to using await in the sync function? so I could do the auto login if the user logined for only one time. And did not do any change with the previous code.

Try to replace you function signature with
Future<bool> get isLogin async {

Timer timer;
#override
void initState() {
super.initState();
timer = new Timer.periodic(new Duration(seconds: 1), (Timer timer) async {
await this.getUserVerificationInfo();
});
}
#override
void dispose() {
super.dispose();
timer.cancel();
}
getUserVerificationInfo() async {
await someAsyncFunc();
timer.cancle();
}

Related

Flutter Using async await requires hotreload but using .then doesn't

Can anyone help me understand this piece of code:
String? userName = "";
String? userEmail = "";
AuthService authService = AuthService();
#override
void initState() {
// TODO: implement initState
super.initState();
gettingUserData();
}
while defining gettingUserData(), using async, await needs hotreload to show the email
gettingUserData() async {
setState(() async {
userName = await HelperFunction.getUsername();
userEmail = await HelperFunction.getUseremail();
});
}
But defining it using .then doesn't need hot relaod
gettingUserData() {
HelperFunction.getUseremail().then((value) {
setState(() {
userEmail = value;
});
});
HelperFunction.getUsername().then((value) {
setState(() {
userName = value;
});
});
}
Can anyone help me understand why this is?
The two versions are not equivalent. The Future.then version calls setState after each Future completes.
The await version calls setState with an asynchronous callback, which setState does not expect. Since setState expects a VoidCallback argument, it expects its callback to complete synchronously, and it will not be awaited. setState therefore executes and returns immediately before waiting for either of the Futures complete.
One way to correct your await version is to await the Futures first and to then call setState:
Future<void> gettingUserData() async {
var userName = await HelperFunction.getUsername();
var userEmail = await HelperFunction.getUseremail();
setState(() {
this.userName = userName;
this.userEmail = userEmail;
});
}

How to link phone number to already logged in email user which is using firebase

I want to link the already login user who has login from their email id when starting the website but after completing the signup process I want to add the phone number of the user but I am getting an error as firebase creates a new id every time when creating a new id after the phone OTP verification.
So, after some digging, I found out that there is a way to LINK already logged in the user with an email with a phone number.
But, the function is not working for me
here is my code for adding a phone number and then linking it with current user credentials.
sendOTP(String phoneNumber) async {
this.phoneNumber = phoneNumber;
FirebaseAuth auth = FirebaseAuth.instance;
print('${phoneCode}');
ConfirmationResult confirmationResult =
await auth.signInWithPhoneNumber('+${phoneCode}${phoneNumber}');
if (kDebugMode) {
print("OTP send to +${phoneCode} ${phoneNumber}");
}
return confirmationResult;
}
authenticateMe(ConfirmationResult confirmationResult, String otp) async {
UserCredential userCredential = await confirmationResult.confirm(otp);
signIn(AuthCredential userCredential) async {
//now link these credentials with the existing user
UserCredential? linkauthresult =
await existingUser?.linkWithCredential(userCredential);
print('linked');
}
firebaseOtp = otp;
}
here is my User existingUser = FirebaseAuth.instance.currentUser!; variable which is i am calling in init state
#override
void initState() {
super.initState();
existingUser;
print('this is current user from otp_container ${existingUser}');
}
and here is my button onPressed function
onPressed: () {
authenticateMe(
temp,
otpCodeController.text,
);
Future.delayed(const Duration(seconds: 3))
.then((value) {
if (!mounted) {
const CircularProgressIndicator();
}
setState(() {
if (otpCodeController.text ==
firebaseOtp) {
isAnimatedContainer =
!isAnimatedContainer;
} else {
setState(() {
verifyOtpcheck = !verifyOtpcheck;
});
}
});
});
},
and I am working on flutter web.

Asynchronous method not running in proper order

I have these methods, for some reason fetchItems is being called first before initPosition, how come dart wont wait for it to finish and proceeds to the second method? I've added async/await but it still doesn't work. I've also checked my backend logs to confirm this. Am I doing something wrong?
Future<void> initPosition() async {
if (_latitude != null && _longitude != null) {
await Socket.updatePosition(
lat: 51,
lon: 17,);
}
}
Future<void> initMarkers() async {
await initPosition();
await Provider.of<Items>(context, listen: false)
.fetchItems();
}
void initMapState() async {
await getCurrentLocation().then((_) async {
await initMarkers();
setState(() {
_loaded = true;
});
});
}
#override
void initState() {
super.initState();
_location.enableBackgroundMode(enable: false);
WidgetsBinding.instance?.addPostFrameCallback((_) {
initMapState();
});
}
Future<void> fetchItems() async {
itemList = await repository.getItemList();
notifyListeners();
}
Working with multiple asynchronous functions inside Futures depends on whether one is finished or not, not every single one. For this, you can call the "whenComplete" method so you can assure that your future function have finished running. Like this:
For your initMarkers() function:
Future<void> initMarkers() async {
await initPosition().whenComplete((){
Provider.of<Items>(context, listen: false)
.fetchItems();
});
}
For your initMapState() function:
void initMapState() async {
await getCurrentLocation().whenComplete(() async {
await initMarkers().whenComplete((){
setState(() {
_loaded = true;
});
});
});
}
Keep in mind that, in your code, you are not working with the returning value of your getCurrentLocation() function, so instead of using the "then" method use the "whenComplete" method, assuring that you changed or returned your values with this function. Finally, for the initState(), make the function body with asynchronous:
#override
void initState() {
super.initState();
_location.enableBackgroundMode(enable: false);
WidgetsBinding.instance?.addPostFrameCallback((_) async {
initMapState();
});
}
This should work.

Keep the user logged in flutter (The app has 2 different login and main, one for Client and one for Driver)

I am doing an app in flutter and I am working on the authentication part. I want to know how I can keep my user logged in after I reload the app. Now the thing is that my app has 2 kinds of users (Client and Driver). So each has its own space, like sign in and sign up and main (after logging in).
This is the code that I used for logging.
class Initializer extends StatefulWidget {
// Access to this Screen
static String id = 'initializer';
#override
_InitializerState createState() => _InitializerState();
}
class _InitializerState extends State<Initializer> {
// Firebase Stuff
final _auth = FirebaseAuth.instance;
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
User _user;
// To Check if There's a Driver
bool isDriver = true;
void getCurrentUser() async {
try {
final getCurrentUser = _auth.currentUser;
if (getCurrentUser != null) {
getUserKind();
_user = getCurrentUser;
}
} catch (e) {
print(e);
}
}
getUserKind() async {
try {
// To fetch Database for Driver
final QuerySnapshot checkOfDriver =
await _firestore.collection('driver').where('uid', isEqualTo: _user.uid).get().catchError((error) {
print(error);
});
if (checkOfDriver.docs.isEmpty)
setState(() {
isDriver = false;
});
else
setState(() {
isDriver = true;
});
} catch (e) {
print(e);
return null;
}
}
#override
void setState(fn) {
if (mounted) {
super.setState(fn);
}
}
#override
void initState() {
super.initState();
getCurrentUser();
}
#override
Widget build(BuildContext context) {
getCurrentUser();
SizeConfig().init(context);
return _user == null
? WelcomeScreen()
: isDriver
? DriverMain()
: ClientMain();
}
}
It's actually working but not properly, because when I reload the app while I'm logging in as a Client, the app shows me DriverMain at the beginning for one second then it switches to the right side which is ClientMain and that causes me some errors sometimes, and it's not an efficient work anyway.
So, what I should add to the code or ...
Firebase already persists the users credentials, and restores them automatically when the app restarts.
But this is an asynchronous process, as it requires a call to the server. By the time your getCurrentUser = _auth.currentUser code runs, that asynchronous process hasn't finished yet, so you get null.
To properly respond to the auth state being restored (and other changes), you'll want to use an auth state change listener as shown in the documentation on authentication state:
FirebaseAuth.instance
.authStateChanges()
.listen((User? user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});
If you want to use this in your UI, you'll typically wrap it in a StreamBuilder instead of calling listen yourself.

Flutter : Flutter Shared Preference don't work ,not save username Edit#1

I have an app contain login and register system ,the system is working correctly.
the problem is I want to keep user logged in by using flutter_session package but don't work.
first I have preloading page contain checking if user is logged in by using functions below :
void gotoTabPae() {
print('ok');
Future.delayed(const Duration(milliseconds: 3000), () {
Navigator.of(context).pushReplacementNamed('tabs');
});
}
void gotoLoginPage() {
print('no');
Future.delayed(const Duration(milliseconds: 3000), () {
Navigator.of(context).pushReplacementNamed('login');
});
}
getuser() async {
var loginedUser;
SharedPreferences preferences = await SharedPreferences.getInstance();
setState(() {
loginedUser= preferences.getString('username');
});
loginedUser != null ? gotoTabPae() : gotoLoginPage();
}
#override
void initState() {
getuser();
super.initState();
}
when I run the app and login then when I restart the app it must go to 'tabs' page ,but the value of username is always null therefore it load login page ,the login function is :
login() async {
var formdata = formLoginKey.currentState;
if (formdata.validate()) {
formdata.save();
var data = {'username': username.text, 'password': password.text};
var url =xxxx/api/controller/users/login_user.php";
var response = await http.post(url, body: data);
var responsebody = jsonDecode(response.body);
if (responsebody['status'] == 'success') {
SharedPreferences pref = await SharedPreferences.getInstance();
pref.setString('username', username.text);
Navigator.of(context).pushReplacementNamed('tabs');
} else {
_showDialog(context, responsebody['status']);
}
} else {
}
}
But in tabs page it is load the session username corret :
getuser() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
var logineduser = preferences.getString('username');
}
#override
void initState() {
getuser();
super.initState();
}
How can I solve this? where is my mistake ?
Your code is running the getuser() method in the initSate() method that too even before the super.initState();. That is the reason the value is no able to load which makes it null. You should use it in the build function.
Your code might look like this:
#override
Widget build(BuildContext context) {
var loginedUser;
loginedUser = await FlutterSession().get('username');
loginedUser != null ? return ClassName()(tabs.dart) : return ClassName()(login.dart);
}
I think that in your login function, pref.setString('username', username.text) is run before the response is received. Can you try this:
http.post(url, body: data).then((response) {
var responsebody = jsonDecode(response.body);
if (responsebody['status'] == 'success') {
SharedPreferences pref = await SharedPreferences.getInstance();
pref.setString('username', username.text);
Navigator.of(context).pushReplacementNamed('tabs');
}
});
and let me know the result?