User gets internaly loged in, but UI does not change - flutter

So I have a problem with my app's login. When a user enters correct login data, he gets recognized on the firebase console, debug console also shows the user now exists, but the screen (which is controlled by StreamBuilder) does not change.
home: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, AsyncSnapshot<User?> userSnapshot) {
if (userSnapshot.connectionState ==
ConnectionState.waiting) {
return LoadingSpinner();
}
if (userSnapshot.hasData) {
return MainScreen();
}
return AuthScreen();
}),
//.............
void authUser(String email, String password, bool isLogin,
String username) async {
setState(() {
isLoading = true;
});
UserCredential userCreadencial;
FocusScope.of(context).unfocus();
if (isLogin == false) {
userCreadencial = await firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
);
} else {
userCreadencial = await firebaseAuth.signInWithEmailAndPassword(
email: email,
password: password,
);
}
FirebaseFirestore.instance
.collection('users_info')
.orderBy(userCreadencial.user!.uid, descending: true);
await FirebaseFirestore.instance
.collection('users_info')
.doc(userCreadencial.user!.uid)
.set({
'email': userCreadencial.user!.email,
'username': username,
});
setState(() {
isLoading = false;
});
Debug console log:
W/System (14293): Ignoring header X-Firebase-Locale because its value
was null. 2 I/System.out(14293): (HTTPLog)-Static: isSBSettingEnabled
false D/FirebaseAuth(14293): Notifying id token listeners about user (
55epaBG5sGYJ7YWq1QZosJqrajT2 ).

I faced a simillar problem recently. I have found a workaround for this problem. You can use "whenComplete" function & a condition if the current user uid is not empty to navigate to the route after the signIn or login is successful. This is how your code might look like:
void authUser(String email, String password, bool isLogin,
String username) async {
setState(() {
isLoading = true;
});
UserCredential userCreadencial;
FocusScope.of(context).unfocus();
//to get current uid
String get currentUserUid => FirebaseAuth.instance.currentUser?.uid ?? '';
if (isLogin == false) {
//add whenComplete & check if the current uid is not empty.
userCreadencial = await firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
).whenComplete(
() => currentUserUid.isNotEmpty
? Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => MainScreen(),
),
(route) => false,
):null,
);;
} else {
//add whenComplete.
userCreadencial = await firebaseAuth.signInWithEmailAndPassword(
email: email,
password: password,
).whenComplete(
() => currentUserUid.isNotEmpty
? Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => MainScreen(),
),
(route) => false,
): null,
);;
}
FirebaseFirestore.instance
.collection('users_info')
.orderBy(userCreadencial.user!.uid, descending: true);
await FirebaseFirestore.instance
.collection('users_info')
.doc(userCreadencial.user!.uid)
.set({
'email': userCreadencial.user!.email,
'username': username,
});
setState(() {
isLoading = false;
});
you should also wrap your signIn and createUser function with try catch blocks

Related

Flutter unable to login after create new account

After building the app, if I try to login it's working fine, but if I create a new account then try to login again, login is not working. I have seen after debugging that I'm getting token in _authenticate method. I think notifyListeners not working here but I don't know why?
I'm using the provider package in my flutter app. I have an Auth provider class where I'm saving data in firebase and also login by firebase. Below it's my provider class.
class Auth with ChangeNotifier {
String? _token;
late DateTime _expiryDate = DateTime.now();
late String _userId;
bool get isAuth {
return token != null;
}
String? get token {
if ((_expiryDate).isAfter(DateTime.now())) {
return _token;
}
return null;
}
Future _authenticate(String email, String password, String urlSegment) async {
var urlParse = Uri.parse(urlSegment);
try {
final response = await http.post(urlParse,
body: jsonEncode({
'email': email,
'password': password,
'returnSecureToken': true
})
);
final responseData = jsonDecode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
// set token and user id from firebase response
_token = responseData['idToken'];
_userId = responseData['localId'];
_expiryDate = DateTime.now()
.add(Duration(seconds: int.parse(responseData['expiresIn'])));
notifyListeners();
return responseData['idToken'];
} catch (error) {
rethrow;
}
}
Future login(String email, String password) async {
print(email);
String url = Constants.firebaseLoginUrl;
return _authenticate(email, password, url);
}
Future signup(String email, String password) async {
String url = Constants.firebaseSignupUrl;
return _authenticate(email, password, url);
}
}
In signUp page I have tried below code to create a new user
Future<void> signUpSubmit() async {
if (_formKey.currentState!.validate()) {
await Provider.of<Auth>(context, listen: false).signup(_email.text, _pass.text);
}
I have checked new data is saving perfectly.
In signUp page there has a login button, after click on login button I have redirect in login page,
TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const LoginPage()
),
);
},
child: const Text('Log in to access'),
)
After click on login I have redirect in login page, then tried login again but it's not working. In debug I'm getting token if I print(token) in _authenticate method.
In main.dart my consumer is looks like
child: Consumer<Auth>(
builder: (ctx,auth, _) => MaterialApp(
home: auth.isAuth ? const HomePage():const LoginPage(),
)
After create account if I rebuild app again then login is working? How I will solve this problem?
See you are redirecting directly in loginPage rather than via the main page ! Just redirect the login button to main page, in your main page there has condition
home: auth.isAuth ? const HomePage(): const LoginPage()
So, if it is auth false it will always redirect to the login page.
Change
MaterialPageRoute( builder: (context) => const LoginPage() ),
To
MaterialPageRoute( builder: (context) => const MainPage() ),

Flutter field has not been initialized

i have a bool that i set to true if the account datas is right but my bool is set to late and it doesnt works
My Model:
bool _result = false;
Future login(String username, String password) async {
var url = "http://192.168.178.75/flutterconn/login.php";
var response = await http.post(Uri.parse(url), body: {
"username": username,
"password": password,
});
var data = await json.decode(response.body);
if (data == "success") {
setResult(true);
Fluttertoast.showToast(msg: "Erfolgreich Eingeloggt",toastLength: Toast.LENGTH_SHORT,gravity: ToastGravity.CENTER,fontSize: 16.0);
}else {
Fluttertoast.showToast(msg: "Nicht Eingeloggt",toastLength: Toast.LENGTH_SHORT,gravity: ToastGravity.CENTER,fontSize: 16.0);
}
//print(_result);
notifyListeners();
}
Future setResult(bool rs) async{
_result = await rs;
notifyListeners();
}
bool getResult(){
return _result;
}
My onPressed Method:
context.read<LoginModel>().login(
usernameController.text, passwordController.text);
print(context.read<LoginModel>().getResult());
if (context.read<LoginModel>().getResult()) {
context.read<FoodSelectionModel>().loadGalleryLinks();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FoodSelectionScreen()),
);
}
My Error:
The following LateError was thrown while handling a gesture:
LateInitializationError: Field '_result#21188420' has not been initialized.
The Bool is turned to late to true how can i set it faster.
i tried with putting async on my onPressed method and await it works thank you guys.
onPressed: () async {
await context.read<LoginModel>().login(
usernameController.text, passwordController.text);
print(context.read<LoginModel>().getResult());
if (context.read<LoginModel>().getResult()) {
context.read<FoodSelectionModel>().loadGalleryLinks();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FoodSelectionScreen()),
);
}

onTap keeps getting called again and again

I had an onTap function for an InkWell with the following code:
onTap: () async {
setState(() {
hasPressedLogIn = true;
print(hasPressedLogIn);
});
var loginData = await lib.login(username, password);
print('got loginData');
setState(
() {
if (password == '' && username == '' ||
password == '' ||
username == '') {
loginText = 'username or password empty';
} else {
utils.saveLoginData(username, password, loginData!.token.session,
loginData.token.refresh);
print('saved login data');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomePage(
token: loginData.token.session,
),
),
);
}
},
);
},
Now, the code executes perfectly for the first time and the app successfully navigates to the next page without any error, however the onTap function doesn't stop despite having done it's job resulting in the rate limit being exceeded. The function seems to loop around the line
var loginData = await lib.login(username, password);
which later on exceeds the login attempt limit. How am I supposed to stop these unnecessary calls?
Change it to this:
onTap: () async {
if (password == '' && username == '' || password == '' || username == '') {
setState(() {
loginText = 'username or password empty';
});
} else {
setState(() {
hasPressedLogIn = true;
});
print(hasPressedLogIn);
utils.saveLoginData(
username,
password,
loginData!.token.session,
loginData.token.refresh);
print('saved login data');
var loginData = await lib.login(username, password);
print('got loginData');
Navigator.push(context, MaterialPageRoute(
builder: (context) => HomePage( token: loginData.token.session,),),);}
},
onTap: () async {
if(hasPressedLogIn == false){
setState(() {
hasPressedLogIn = true;
print(hasPressedLogIn);
});
var loginData =
await lib.login(username, password);
print('got loginData');
setState(
() {
if (password == '' && username == '' ||
password == '' ||
username == '') {
loginText = 'username or password empty';
} else {
utils.saveLoginData(
username,
password,
loginData!.token.session,
loginData.token.refresh);
print('saved login data');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomePage(
token: loginData.token.session,
),
),
);
}
},
);}
else{
Future.delayed(const Duration(milliseconds: 1500), () {
//after a limited duration you will be able to tap it again
setState(() {
hasPressedLogIn = false;
print(hasPressedLogIn);
});
});
}
},

What to do after login but before essential user data gets returned in flutter

I have a flutter app that uses firebase for authentication.
return StreamBuilder<FirebaseUser>(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
return HomeScreen();
} else {
return LoginScreen();
}
},
);
so basically as soon as user authenticates, this will take to the home screen. but i dont want that, i want to wait on another piece of data from my api, say onboarded, if onboarded == true then HomeScreen otherwise OnboardingScreen.
So the tricky part is before that data comes in, i want to stay on the login screen. how do i have the user stay on the LoginScreen? it seems the best way is to have another stream listen to the onboardedLoading and combine these 2 streams?
Make a dart file auth.dart, in that, paste this line of code,
final FirebaseAuth auth = FirebaseAuth.instance;
Future<FirebaseUser> handleSignInEmail(String email, String password) async {
AuthResult result = await auth.signInWithEmailAndPassword(email: email, password: password);
final FirebaseUser user = result.user;
assert(user != null);
assert(await user.getIdToken() != null);
final FirebaseUser currentUser = await auth.currentUser();
assert(user.uid == currentUser.uid);
print('signInEmail succeeded: $user');
return user;
}
Future<FirebaseUser> handleSignUp(email, password) async {
AuthResult result = await auth.createUserWithEmailAndPassword(email: email, password: password);
final FirebaseUser user = result.user;
assert (user != null);
assert (await user.getIdToken() != null);
return user;
}
In your login/ Sigup page, create an instance of my auth class:
var authHandler = new Auth();
In the onPressed () of your button
onPressed: () {
if(onboardedLoading==true){
authHandler.handleSignInEmail(emailController.text, passwordController.text)
.then((FirebaseUser user) {
Navigator.push(context, new MaterialPageRoute(builder: (context) => HomeScreen()));
}).catchError((e) => print(e));
}
}else{
//Show An Animation, such as CirclularProgressIndicator.
}
You can design a simple loading screen, then use Navigator.pushAndRemoveUntil() to whichever screen you need after getting AuthState.

Store multiple values in shared preference in flutter failed

Basically i am creating an online product app, when ever a user select any product and click on checkout button it checks whether user the is login or not. Here i did the same thing, i select a product and went cart page and clicks on checkout button (Here i am not loggedin) so it went to login page and saved the variables and then i go to the cart page and click on checkout button again it goes to login page. I have already saved the variables in login page but why it is not verifying.
Loginpage.dart
Future<UserRegister> getLogin(UserLogin userLogin) async{
final String apiUrl="http://cashback.us-east-2.elasticbeanstalk.com/user/login";
var userLoginJson = userLogin.toJson();
final response=await http.post(apiUrl,body: json.encode(userLoginJson),headers: {"content-type" : "application/json",
"Accept": "application/json",}
);
print(response);
if(response.statusCode >= 200 && response.statusCode < 300){
final responseString=response.body;
print(responseString);
var result = userRegisterFromJson(responseString);
print(result.user.userId);
print(result.user.name);
print(result.user.token);
print(result.user.mobile);
print(result.user.isDriver);
print(result.driver.driverId);
prefs.setBool('login', true);
prefs.setString('userid', result.user.userId.toString());
prefs.setString('name', result.user.name);
prefs.setString('mobile', result.user.mobile); //Here i am storing the values
prefs.setString('isDriver', result.user.isDriver.toString());
prefs.setString('driverId', result.driver.driverId.toString());
prefs.setString('token', result.user.token);
return result;
}else{
final responseString=response.body;
var result = json.decode(responseString);
print(result);
return null;
}
}
RaisedButton(
onPressed(){
getLogin(login);
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => MainPage()));
}
)
Cartpage.dart
SharedPreferences logindata;
bool newuser;
PlatformButton(
onPressed: (){
check_if_already_login();
},
child: Text('Checkout',style: TextStyle(color: Colors.white),),
androidFlat: (_) =>
MaterialFlatButtonData(color: Colors.cyan),
ios: (_) =>
CupertinoButtonData(color: Colors.cyan)
),
Future<void> check_if_already_login() async {
logindata = await SharedPreferences.getInstance();
newuser = (logindata.getBool('login') ?? true);
Navigator.pushReplacement(
context, new MaterialPageRoute(builder: (context) => Login()));
if (newuser == false) {
print(false);
}
}
Because you put Navigator.pushReplacement before if statement
So it will directly go to Login page
You can move Navigator.pushReplacement inside if
You can change from
Navigator.pushReplacement(
context, new MaterialPageRoute(builder: (context) => Login()));
if (newuser == false) {
to
if (newuser == false) {
Navigator.pushReplacement(
context, new MaterialPageRoute(builder: (context) => Login()));
print(false);
}