I'm new to Flutter & Dart, trying to complete my first app.
I can't catch (with try-catch block) http.get SocketException (which happens when you call API and WiFi turned off)
I tried everything on the internet without luck, I even tried (Dio) package to catch this exception, but no success.
How to reproduce: use bottom code...turn off phone's WiFi...call API...now the app crashes with (SocketException) in your IDE.
Image: https://imgur.com/bA0rKEN
here is my simple code (updated)
RaisedButton(
child: Text("Call API"),
onPressed: () async {
try {
http.Response response = await getLoginResponse();
//do something with response
print(response.body);
} catch (e) {
print("Button onPressed Error: " + e.toString());
}
},
)
//---------------------------------------------------------------
Future<http.Response> getLoginResponse() {
return http.get(loginUrl).timeout(Duration(seconds: 10))
.then((response) {
return response;
}, onError: (e) {
print("onError: " + e.toString());
}).catchError((err) {
print("catchError: " + err.toString());
return null;
});
}
You can catch several types of errors and handle each one separately
Example:
import 'dart:io' as Io;
http.Client client = http.Client();
try {
response = await client.get(url).timeout(new Duration(seconds: 10));
} on Io.SocketException catch (_) {
throw Exception('Not connected. Failed to load data');
} on TimeoutException catch (_) {
throw Exception('Not connected. TimeOut Exception');
} catch (e) {
// Default error handling;
}
if you want to get catch in RaisedButton's try-catch block, instead of return null in getLoginInfo() methods, you must return an Exception like this:
Future<List<LoginObject>> getLoginInfo() async {
try {
List<LoginObject> loginObjectList = List<LoginObject>();
http.Response loginResponse =
await http.get(loginUrl).timeout(Duration(seconds: 10));
if (loginResponse.statusCode == 200) {
loginObjectList = loginObjectFromJson(loginResponse.body);
return loginObjectList;
} else {
throw Exception('Authentication Error');
}
} catch (e) {
print("Error: " + e.toString());
return throw Exception('Connection Error');;
}
}
Note: If you want to handle each one of error response, you can create an custom ErrorModelClass and handle error state with it and finally return your ErrorModelClass.
catch (error) {
print(error);
throw error is HttpResponseError ? error : HttpResponseError(0,"error connection");
HttpResponseError is my custom model class.
Related
the request is just waiting and not resulting an error, i want an error to be produced when there is no internet but it's just waiting forever
i tried
sendTimeout: 600000,
receiveTimeout: 600000,
but same result
vscode screenshot
You need to wrap your logic inside try/catch block.
try {
var response = await _dio.getUri(
Uri.parse(uri));
if (response.statusCode == 200) {
return response.data;
} else {
throw response;
}
} on SocketException catch (e) {
throw SocketException(e.toString());
} on FormatException catch (_) {
throw FormatException("Unable to process the data");
} catch (e) {
throw e;
}
I have class with static Future method that makes http request. This method is encapsulated within try catch. Most of the exception is handled here.
class AuthenticationServices {
static late User users;
static Future<int> loginWithUernameAPI(
String username, String password) async {
try {
Map loginData = {'username': username, 'password': password};
final responseLoginUser = await http.post(
Uri.parse(Constants.userLogin),
headers: {
'Accept': 'application/json'
},
body: loginData,
);
if (responseLoginUser.statusCode == 200) {
return responseLoginUser.statusCode;
} else {
return responseLoginUser.statusCode;
}
} on SocketException catch (e) {
developer.log(e.toString(), name: 'SocketException');
} on TimeoutException catch (e) {
developer.log(e.toString(), name: 'TimeoutException');
} on HttpException catch (e) {
developer.log(e.toString(), name: 'HttpException');
} on Exception catch (e) {
developer.log(e.toString(), name: 'Exception');
} catch (e) {
developer.log(e.toString(), name: 'Exception');
}
}
}
This method is called from another route. Like below
AuthenticationServices.loginWithUernameAPI(
_usernameController.text,
_passwordController.text)
.then((value) {
responseStatusCode = value;
if (responseStatusCode == 200) {
developer.log("autheticated");
context.loaderOverlay.hide();
Navigator.pushAndRemoveUntil<MenuActivity>(
context, MaterialPageRoute(
builder: (BuildContext context) {
return const MenuActivity();
},
), (Route<dynamic> route) => false);
} else if (responseStatusCode == 404) {
context.loaderOverlay.hide();
developer.log("not autheticated");
} else {
developer.log(responseStatusCode.toString());
context.loaderOverlay.hide();
developer.log("not autheticated");
}
}).timeout(const Duration(seconds: 10),
onTimeout: () {
context.loaderOverlay.hide();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(Constants.connectionTimedOut),
duration: Duration(seconds: 3),
),
);
});
When I produce error, app crashes, throws an exception which is not the behavior I want.I am trying here to provide suitable error according to the exception generated.
Note: I have added breakpoint to debug. The app is routed to try catch only after throwing exception which is not handled by the defined try catch.
I suppose you want to show snackbar on errors. So you can rethrow the exceptions and catch them in your method call itself. The other option is to create a model class ExceptionModel where you can pass the message and then return it from the method and then show snackbar.
I have an exception that gets thrown multiple times before getting handled in my code, (the same error object gets thrown 4 times in the tree).
During each throw , flutter appends the word Exception: to my error object, so the final shape of the message is:
Exception: Exception: Exception: Exception: SocketException: OS Error: Connection refused, errno = 111, address = 10.12.7.15, port = 39682
And this is an example of how I handle exceptions:
getSomeData () async {
try {
await myFunction3();
} catch (e) {
throw Exception(e);
}
}
/*****************************************/
Future<void> myFunction3 () async {
try {
await myFunction2();
} catch (e) {
throw Exception(e);
}
}
/*****************************************/
Future<void> myFunction2 () async {
try {
await myFunction1();
} catch (e) {
throw Exception(e);
}
}
/*****************************************/
Future<void> myFunction1() async {
try {
await dio.get(/*some parameters*/);
}
on DioError catch (e) {
throw Exception(e.error);
}
catch (e) {
throw Exception(e);
}
}
I want to throw the Exception() in a right way so the repetition of word Exception: doesn't appear anymore in my error string.
Instead of throwing a new exception, you could rethrow the catched one:
Future<void> myFunction1() async {
try {
await dio.get(/*some parameters*/);
}
on DioError catch (e) {
// do something
rethrow;
}
catch (e) {
// do something
rethrow;
}
}
See more: https://dart.dev/guides/language/effective-dart/usage#do-use-rethrow-to-rethrow-a-caught-exception
I upgraded Flutter from version 2.0.2 to version 2.2.2 and now the custom exceptions that are thrown from a Future function are not being catch.
For example, I got this Future function, where I call another Future that does a server request and returns back the response or throws a custom exception (ApiException) in case of error:
static Future<bool> signUpCustomerRequest(Map<String, dynamic> params) async {
try {
// Here we call this Future function that will do a request to server API.
dynamic _response = await _provider.signUpCustomer(params);
if (_response != null) {
updateUserData(_response);
return true;
}
return false;
} on ApiException catch(ae) {
// This custom exception is not being catch
ae.printDetails();
rethrow;
} catch(e) {
// This catch is working and the print below shows that e is Instance of 'ApiException'
print("ERROR signUpCustomerRequest: $e");
rethrow;
} finally {
}
}
And this is the Future function that does the request to server and throws the ApiException:
Future<User?> signUpCustomer(Map<String, dynamic> params) async {
// POST request to server
var _response = await _requestPOST(
needsAuth: false,
path: routes["signup_client"],
formData: params,
);
// Here we check the response...
var _rc = _response["rc"];
switch(_rc) {
case 0:
if (_response["data"] != null) {
User user = User.fromJson(_response["data"]["user"]);
return user;
}
return null;
default:
print("here default: $_rc");
// And here we have the throw of the custom exception (ApiException)
throw ApiException(getRCMessage(_rc), _rc);
}
}
Before upgrading to Flutter 2.2.2 the catch of custom exceptions worked perfectly. Did something change on this Flutter version? Am I doing something wrong?
Thanks!
I was able to reproduce your bug with the following code:
class ApiException implements Exception {
void printDetails() {
print("ApiException was caught");
}
}
Future<void> doSomething() async {
await Future.delayed(Duration(seconds: 1));
throw ApiException();
}
void main() async {
try {
await doSomething();
} on ApiException catch (ae) {
ae.printDetails();
} catch (e) {
print("Uncaught error: $e"); // This line is printed
}
}
There's an open issue on the dart sdk, which I think might be related, though I'm not sure: https://github.com/dart-lang/sdk/issues/45952.
In any case, I was able to correct the error by returning a Future.error, instead of throwing the error directly:
class ApiException implements Exception {
void printDetails() {
print("ApiException was caught"); // This line is printed
}
}
Future<void> doSomething() async {
await Future.delayed(Duration(seconds: 1));
return Future.error(ApiException());
}
void main() async {
try {
await doSomething();
} on ApiException catch (ae) {
ae.printDetails();
} catch (e) {
print("Uncaught error: $e");
}
}
Using the code from the package I was unable to catch the exception. Note that I would like to catch this specific exception.
// from https://pub.dev/packages/url_launcher
_launchURL() async {
const url = 'myscheme://myurl';
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
// my code
try {
_launchURL();
}
catch (e)
{
// although the exception occurs, this never happens, and I would rather catch the exact canLaunch exception
}
I would try to put the try catch statement inside the function. I believe what is happening is that the try/catch statement is only applying for the function call and although it is async I dont believe that it actually tries and returns exeptions.
So the solution would look somethink like this:
_launchURL() async {
try{
const url = 'myscheme://myurl';
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
catch(e){
//debug e
}
}
// my code
launchURL();
You can use .then() for business logic.
For me, it is used to check if the app can be opened on the device.
Can be solution below,
--> url_launcher: ^6.0.2
--> https://pub.dev/packages/url_launcher
launch(appLink).then(
(bool isLaunch) {
print('isLaunch: $isLaunch');
if (isLaunch) {
// Launch Success
} else {
// Launch Fail
}
},
onError: (e) {
print('onError: $e');
},
).catchError(
(ex) => print('catchError: $ex'),
);
Work for me.
Future<void> _launch(String url) async {
await canLaunch(url)
? await launch(url)
: throw 'Could not launch $url';
}