I am trying to get some information from a database which I do get eventually, but my if conditions are checked first before getting the data and prints the data after completing the checking of the if conditions, even though I have used await to wait for the data to arrive and then continue.
Future reg() async {
getData().then((value) async {
print(value["serverIP"]);
print(value["port"]);
print(value["passwordMain"]);
Dio dio = Dio();
Response response = await dio.get(
'http://${value["serverIP"]}:${value["port"]}/${value["passwordMain"]}/reg/${controllerEmail.text}/${controllerPassword.text}/${controllerUsername.text}');
print(response.data);
return response;
});
ElevatedButton(
onPressed: () async {
if (!controllerEmail.text.endsWith("#gmail.com") &
!controllerEmail.text.endsWith("#gmail.com ") &
!controllerEmail.text.endsWith("#email.com") &
!controllerEmail.text.endsWith("#email.com ") &
!controllerEmail.text.endsWith("#hotmail.com") &
!controllerEmail.text.endsWith("#hotmail.com ")) {
if (controllerEmail.text.endsWith(" ")) {
controllerEmail.text =
controllerEmail.text.replaceAll(" ", "");
}
showErrorDialog(context, 'Unknown Email Address',
'Try Changing the Email to one of the Providers we Support.');
} else if ((controllerPassword.text !=
controllerRePassword.text) |
controllerPassword.text.isEmpty) {
showErrorDialog(context, 'Passwords Do not Match/Empty',
'Please Re-Type your Passwords as they do not Match, or are Empty');
} else {
var response = await reg();
if (response != null) {
if (response.data == "done") {
showErrorDialog(context, "Done",
"Your Account has been Created, please Log in");
} else if (response.data == "key") {
showErrorDialog(
context,
"Incorrect API Key/Main Server Password",
"The API Key (Main Server Password) is Incorrect. Kindly, Ensure the Key.");
} else if (response.data == "email") {
showErrorDialog(context, "Account Already Exists",
"An Account already exists with this Email");
} else if (response.data == "username") {
showErrorDialog(context, "Account Already Exists",
"An Account already exists with this Username");
}
}
}
},
child: const Text("Sign Up"),
),
You're missing a return in your reg() function. Add one before your getData() call like this:
Future reg() async {
try {
return getData().then((value) async {
Dio dio = Dio();
Response response = await dio.get(
'http://${value["serverIP"]}:${value["port"]}/${value["passwordMain"]}/reg/${controllerEmail.text}/${controllerPassword.text}/${controllerUsername.text}');
return response;
});
} catch (e) {}
}
Now the function should be properly awaited because it is now returning a promise instead of nothing.
Alternatively, you might prefer to rewrite it using more async/await for easier comprehension, like this:
Future reg() async {
try {
const value = await getData();
Dio dio = Dio();
Response response = await dio.get(
'http://${value["serverIP"]}:${value["port"]}/${value["passwordMain"]}/reg/${controllerEmail.text}/${controllerPassword.text}/${controllerUsername.text}');
return response;
} catch (e) {}
}
Credit: https://stackoverflow.com/a/74238420/13909069
Related
hello I have a case where when the user token expires the user does not switch to the loginPage page, even though I have set it here.
how do i solve this problem thanks.
i set it on splashscreen if token is not null then go to main page and if token is null then go to login page.
but when the token expires it still remains on the main page
Future<void> toLogin() async {
Timer(
const Duration(seconds: 3),
() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? token = prefs.getString(Constant.token);
Navigator.pushReplacementNamed(
context,
token != null ? AppRoute.mainRoute : AppRoute.loginRoute,
arguments: token,
);
},
);
}
and function when user login
CustomButtonFilled(
title: 'Login',
onPressed: () async {
final prefs =
await SharedPreferences.getInstance();
prefs.setString(Constant.token, '');
if (nimController.text.isEmpty ||
passwordController.text.isEmpty) {
showError('NIM/Password harus diisi');
} else {
setState(() {
isLoading = true;
});
User? user = await userProvider.login(
nimController.text,
passwordController.text);
setState(() {
isLoading = false;
});
if (user == null) {
showError('NIM/Password tidak sesuai!');
} else {
userProvider.user = user;
Navigator.pushNamedAndRemoveUntil(
context,
'/main',
(route) => false,
);
}
}
},
),
and this call api
Future<User?> login(String nim, String password) async {
String url = Constant.baseURL;
try {
var body = {
'username': nim,
'password': password,
};
var response = await http.post(
Uri.parse(
'$url/login_mhs',
),
body: body,
);
if (response.statusCode == 200) {
final token = jsonDecode(response.body)['data']['access_token'];
//Ini mulai nyimpen token
await UtilSharedPreferences.setToken(token);
print(token);
// print(await UtilSharedPreferences.getToken());
return User.fromJson(jsonDecode(response.body));
} else {
return null;
}
} catch (e) {
print(e);
throw Exception();
}
}
you can just make your own HTTP client using Dio and add Interceptor to automatically regenerate idToken if expired using the refreshToken given.
Http client gives an error if the refreshToken also gets expired.
In that case, just navigate to the login screen.
Full code for adding interceptor and making own HTTP client is given below
import 'package:dio/dio.dart';
import '../utils/shared_preference.dart';
class Api {
static Dio? _client;
static Dio clientInstance() {
if (_client == null) {
_client = Dio();
_client!.interceptors
.add(InterceptorsWrapper(onRequest: (options, handler) async {
if (!options.path.contains('http')) {
options.path = 'your-server' + options.path;
}
options.headers['Authorization'] =
'Bearer ${PreferenceUtils.getString('IdToken')}';
return handler.next(options);
}, onError: (DioError error, handler) async {
if ((error.response?.statusCode == 401 &&
error.response?.data['message'] == "Invalid JWT")) {
if (PreferenceUtils.exists('refreshToken')) {
await _refreshToken();
return handler.resolve(await _retry(error.requestOptions));
}
}
return handler.next(error);
}));
}
return _client!;
}
static Future<void> _refreshToken() async {
final refreshToken = PreferenceUtils.getString('refreshToken');
final response = await _client!
.post('/auth/refresh', data: {'refreshToken': refreshToken});
if (response.statusCode == 201) {
// successfully got the new access token
PreferenceUtils.setString('accessToken', response.data);
} else {
// refresh token is wrong so log out user.
PreferenceUtils.deleteAll();
}
}
static Future<Response<dynamic>> _retry(RequestOptions requestOptions) async {
final options = Options(
method: requestOptions.method,
headers: requestOptions.headers,
);
return _client!.request<dynamic>(requestOptions.path,
data: requestOptions.data,
queryParameters: requestOptions.queryParameters,
options: options);
}
}
Dio client = Api.clientInstance();
var resposne = (hit any request);
if(error in response is 401){
//it is sure that 401 is because of expired refresh token as we
//already handled idTokoen expiry case in 401 error while
//adding interceptor.
navigate to login screen for logging in again.
}
Please accept the solution if it solves your problem.
If your session expire feature has some predefine interval or logic than you have to implement it in splash screen and based on that you can navigate user further. Otherwise you want to handle it in API response only you have add condition for statusCode 401.
checkSessionExpire(BuildContext context)
if (response.statusCode == 200) {
//SuccessWork
} else if (response.statusCode == 401) {
//SessionExpire
} else {
return null
}
}
I have this funtion which gets data from a database.
Future reg() async {
try {
getData().then((value) async {
Dio dio = Dio();
Response response = await dio.get(
'http://${value["serverIP"]}:${value["port"]}/${value["passwordMain"]}/reg/${controllerEmail.text}/${controllerPassword.text}/${controllerUsername.text}');
return response;
});
} catch (e) {}
}
Now I request the data and check for some conditions but, the if conditions are ran first before getting the data from the database and the data arrives after the complete execution of code (Which I know because it can print the correct data after checking the IF conditions).
else {
var response = await reg();
if (response != null) {
if (response.data == "done") {
showErrorDialog(context, "Done",
"Your Account has been Created, please Log in");
} else if (response.data == "key") {
showErrorDialog(
context,
"Incorrect API Key/Main Server Password",
"The API Key (Main Server Password) is Incorrect. Kindly, Ensure the Key.");
} else if (response.data == "email") {
showErrorDialog(context, "Account Already Exists",
"An Account already exists with this Email");
} else if (response.data == "username") {
showErrorDialog(context, "Account Already Exists",
"An Account already exists with this Username");
}
}
}
How can I run these IF conditions after getting the data from the database?
You're missing a return in your reg() function. Add one before your getData() call like this:
Future reg() async {
try {
return getData().then((value) async {
Dio dio = Dio();
Response response = await dio.get(
'http://${value["serverIP"]}:${value["port"]}/${value["passwordMain"]}/reg/${controllerEmail.text}/${controllerPassword.text}/${controllerUsername.text}');
return response;
});
} catch (e) {}
}
Now the function should be properly awaited because it is now returning a promise instead of nothing.
Alternatively, you might prefer to rewrite it using more async/await for easier comprehension, like this:
Future reg() async {
try {
const value = await getData();
Dio dio = Dio();
Response response = await dio.get(
'http://${value["serverIP"]}:${value["port"]}/${value["passwordMain"]}/reg/${controllerEmail.text}/${controllerPassword.text}/${controllerUsername.text}');
return response;
} catch (e) {}
}
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);
}
i have a problem with hate. I'm trying to login using dio, the login method works perfectly, but when I put invalid credentials dio gives me this error:
DioError
Error in execution
I created a boolean function that would return true or false if the statuscode was 200 it would return true and if not it would return false, but when logging in with the right credentials everything is ok, everything happens as it should, but when logging in with invalid credentials this error above causes it. I'm using shared preferences to store the tolken in the app, and the logic would be simple, if it was 200 I would log into the app, otherwise it would show me a snackbar I made in another file, this is my code:
loginFinal() async {
if (formKey.currentState!.validate()) {
bool loginIsOk = await loginConect();
if (loginIsOk) {
Get.offAllNamed("/home");
await Future.delayed(const Duration(seconds: 1));
message(MessageModel.info(
title: "Sucesso",
message: "Seja bem vindo(a) influenciador(a)",
));
} else {
loaderRx(false); //LOADER
message(MessageModel.error(
title: "Erro",
message: "Erro ao realizar login",
));
}
}
}
//LOGICA DE ENTRAR NO APP
Future<bool> loginConect() async {
final dio = Dio();
String baseUrl = "https://soller-api-staging.herokuapp.com";
loaderRx(true); //LOADER
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
final response = await dio.post(
baseUrl + "/auth",
data: jsonEncode(
{
"login": emailController.text,
"senha": passWordController.text,
},
),
options: Options(
headers: {'Content-Type': 'application/json; charset=UTF-8'},
method: "post",
),
);
if (response.statusCode == 200) {
await sharedPreferences.setString(
"token",
"string: ${response.data["string"]}",
);
print("Resposta: ${response.data["string"]}");
loaderRx(false);
return true;
} else {
print("RESPOSTA: ${response.data}");
return false;
}
}
}
Dio always throw an exception if the status code in the header is not 200,
you will need to catch the exception using try catch.
In the catch method, you can check if the type of the error is DioError and then handle that exception,
Here is a code snippet of a login process that I use in my code to handle this behavior.
Future<SignInApiResponse> signInUser(String _email,String _password) async {
try {
final dio = Dio(ApiConstants.headers());
final Response response = await dio.post(
ApiConstants.baseUrl + ApiConstants.signInUrl,
data: {"email": _email,
"password": _password,
},
);
if (response.statusCode == 200) {
return SignInApiResponse.fromJson(response.data);
} else {
return SignInApiResponse(message: response.toString());
}
} catch (e) {
if (e is DioError) {
if (e.response?.data == null) {
return SignInApiResponse(message: Messages.loginFailed);
}
return SignInApiResponse.fromJson(e.response?.data);
} else {
return SignInApiResponse(message: e.toString());
}
}
}
hopefully, this will help
if not you can always use http package that does not throw an exception in similer case
In my flutter app, I have a future that handles http requests and returns the decoded data. But I want to be able to send an error if the status code != 200 that can be gotten with the .catchError() handler.
Heres the future:
Future<List> getEvents(String customerID) async {
var response = await http.get(
Uri.encodeFull(...)
);
if (response.statusCode == 200){
return jsonDecode(response.body);
}else{
// I want to return error here
}
}
and when I call this function, I want to be able to get the error like:
getEvents(customerID)
.then(
...
).catchError(
(error) => print(error)
);
Throwing an error/exception:
You can use either return or throw to throw an error or an exception.
Using return:
Future<void> foo() async {
if (someCondition) {
return Future.error('FooError');
}
}
Using throw:
Future<void> bar() async {
if (someCondition) {
throw Exception('BarException');
}
}
Catching the error/exception:
You can use either catchError or try-catch block to catch the error or the exception.
Using catchError:
foo().catchError(print);
Using try-catch:
try {
await bar();
} catch (e) {
print(e);
}
You can use throw :
Future<List> getEvents(String customerID) async {
var response = await http.get(
Uri.encodeFull(...)
);
if (response.statusCode == 200){
return jsonDecode(response.body);
}else{
// I want to return error here
throw("some arbitrary error"); // error thrown
}
}
Another way to solve this is by using the dartz package.
An example of how to use it would look something similar like this
import 'package:dartz/dartz.dart';
abstract class Failure {}
class ServerFailure extends Failure {}
class ResultFailure extends Failure {
final int statusCode;
const ResultFailure({required this.statusCode});
}
FutureOr<Either<Failure, List>> getEvents(String customerID) async {
try {
final response = await http.get(
Uri.encodeFull(...)
);
if (response.statusCode == 200) {
return Right(jsonDecode(response.body));
} else {
return Left(ResultFailure(statusCode: response.statusCode));
}
}
catch (e) {
return Left(ServerFailure());
}
}
main() async {
final result = await getEvents('customerId');
result.fold(
(l) => print('Some failure occurred'),
(r) => print('Success')
);
}
You can return the error data like this if you want to read the error object:
response = await dio.post(endPoint, data: data).catchError((error) {
return error.response;
});
return response;
//POST
Future<String> post_firebase_async({String? path , required Product product}) async {
final Uri _url = path == null ? currentUrl: Uri.https(_baseUrl, '/$path');
print('Sending a POST request at $_url');
final response = await http.post(_url, body: jsonEncode(product.toJson()));
if(response.statusCode == 200){
final result = jsonDecode(response.body) as Map<String,dynamic>;
return result['name'];
}
else{
//throw HttpException(message: 'Failed with ${response.statusCode}');
return Future.error("This is the error", StackTrace.fromString("This is its trace"));
}
}
Here is how to call:
final result = await _firebase.post_firebase_async(product: dummyProduct).
catchError((err){
print('huhu $err');
});