In my flutter project I need to show some illustration images when socket exception occurs when API was called. How can I do that ?
Thanks in advance
This will help on socket exception and format exception.
Create model class for httpresponse
class HTTPResponse<T> {
bool isSuccessful;
T data;
String message;
int responseCode;
HTTPResponse(this.isSuccessful, this.data, {this.message, this.responseCode});
}
Then use this model in api response like this
Future<HTTPResponse<List<Post>>> getPosts(
{int limit = 20, int page = 1}) async {
String url =
'https://jsonplaceholder.typicode.com/posts?_limit=$limit&_page=$page';
Uri uri = Uri.parse(url);
try {
var response = await http.get(uri);
if (response.statusCode == 200) {
var body = json.decode(response.body);
List<Post> postsList = [];
body.forEach((e) {
Post post = Post.fromJson(e);
postsList.add(post);
});
return HTTPResponse(
true,
postsList,
responseCode: response.statusCode,
);
} else {
return HTTPResponse(false, null,
message: 'Invalid response from server',
responseCode: response.statusCode);
}
} on SocketException {
return HTTPResponse(false, [], message: 'Unable to reach the internet');
} on FormatException {
return HTTPResponse(false, [], message: 'Invalid response from server');
} catch (e) {
return HTTPResponse(false, [],
message: "Something went wrong please try in a minute or two");
}
}
It depends on where you want to show it in the widget tree. One simple example would be to push a new screen on to the navigation stack. You will need the BuildContext in your function where the exception might occur.
void someMethod(BuildContext context) {
try {
//some code that might throw an exception
} on Exception catch (_) {
Navigator.pushNamed(context, "Your illustration view");
}
}
Another example would be to add it to your widget tree depending on a bool. You set that bool to true when an exception is thrown.
void someOtherMethod() {
try {
//some code that might throw an exception
} on Exception catch (_) {
setState(() {
hasThrownError = true;
});
}
}
Use it in your widget tree like so:
bool hasThrownError = false;
Widget buildWidgetTree() {
return hasThrownError
? Text("This is where you can show your error illustration")
: Text("This is wher you can show your regular view");
}
Related
I have a flutter application that uses graphql: ^5.0.0 to perform mutations and queries on my database and I'm trying to handle invalid token exceptions I get. When I get an invalid token error from my server, an error is thrown here.
Here is the error making its way up into my code
here is my code:
try {
final QueryResult result = await client.query(options);
List<dynamic> taskList = result.data!['userTasksConnections']['tasks'];
List<Task> tasks = [];
for(int i = 0; i < taskList.length; i++) {
tasks.add(Task.fromJson(taskList[i]));
}
return tasks;
} on HttpLinkServerException catch(e) {
if(e.parsedResponse?.errors?[0] == 'Invalid Token'){
await UserRepo().getAccessToken();
return getTasks(page: page, keyword: keyword);
}
else{
return [];
}
}
since the error is clearly of type HttpLinkServerException I have an on HttpLinkServerException catch(). However, when the code runs the exception is not caught in the catch block and the code continues after the result await as if nothing happened, causing a null data exception on this line
List<dynamic> taskList = result.data!['userTasksConnections']['tasks'];
You need to write your own parser to fix this issue. You do so with something like this
import 'package:graphql/client.dart';
class CustomResponseParser extends ResponseParser {
#override
Response parseResponse(Map<String, dynamic> body) {
Map<String, String> errors = new Map();
if(body["errors"] != null) {
errors['message'] = body["errors"][0];
}
Response res = Response(
errors: (body["errors"] as List?)
?.map(
(dynamic error) => parseError(errors),
)
.toList(),
data: body["data"] as Map<String, dynamic>?,
context: Context().withEntry(
ResponseExtensions(
body["extensions"],
),
),
);
return res;
}
#override
GraphQLError parseError(Map<String, dynamic> error) {
return GraphQLError(
message: error['message'],
);
}
}
And then you use it when initializing your graphqlClient like this
final GraphQLClient client = GraphQLClient(
cache: GraphQLCache(),
link: AuthLink(getToken: () {
if (store.state.auth.accessToken == '') {
return "";
} else {
return "Bearer ${store.state.auth.accessToken}";
}
}).concat(
HttpLink(
Environment().config.apiHost,
parser: CustomResponseParser()
)
)
);
I working on error handling of api's. i want if api is crashed then it display a message of "Server is down" something like this, in UI.
I created a class where i'm creating methods of api, here in getBooks method if i modify the api url then it is printing this Exception, and i want it in UI. The problem is getBooks return type is List<Book>> so we can't return this Exception, any solution how to do this?
Exception
E/flutter (12924): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: Exception
here is my api code
class BooksApi {
static Future<List<Book>> getBooks(String query) async {
try {
final url = Uri.parse(
'https://gist.githubusercontent.com/JohannesMilke/d53fbbe9a1b7e7ca2645db13b995dc6f/raw/eace0e20f86cdde3352b2d92f699b6e9dedd8c70/books.json');
final response = await http.get(url);
if (response.statusCode == 200) {
final List books = json.decode(response.body);
return books.map((json) => Book.fromJson(json)).where((book) {
final titleLower = book.title.toLowerCase();
final authorLower = book.author.toLowerCase();
final searchLower = query.toLowerCase();
return titleLower.contains(searchLower) ||
authorLower.contains(searchLower);
}).toList();
} else {
throw Exception;
}
} catch (e) {
print("e");
print(e);
}
throw Exception;
}
}
and calling it like
Future init() async {
setState(() {
isLoading = true;
});
var books = await BooksApi.getBooks(query); //this
var response = await obj.getProduct();
print(response);
setState(() => this.books = books);
setState(() {
isLoading = false;
});
}
You could handle errors with then and onError :
await BooksApi.getBooks(query).then((books) async {
setState(() => {
this.books = books;
this.isLoading = false;
})
}, onError: (error) {
// do something with error
});
or a simple try-catch (you can write try-catch clauses the same way you would in synchronous code).
See handling errors.
You can also use catchError id you don't use async/await :
BooksApi.getBooks(query).then((books) {
setState(() => {
this.books = books;
this.isLoading = false;
})
}).catchError((error, stackTrace) {
print("error is: $error");
});
See futures error handling.
Try to wrap 'var books = await BooksApi.getBooks(query)' with try and catch.
...
try {
var books = await BooksApi.getBooks(query);
} catch (e) {
// To do for UI
}
...
For api, you need to make something like this:
APIModel{
final int code;
// or a success flag
// final bool success;
final String message;
final List<Book> data;
APIModel({this.code,this.message,this.data});
}
It means, every api have its own code,message,and data filed.
When you request, you can check your code or success:
var response = await request(params);
isLoading = false;
if(response.code == 0){}
// or
if(response.success){
// do what you want
}
else {
Toast.show(response.message);
}
You can use build_runner and json_serializable.
I tried using Catcher, here is my code
CatcherOptions debugOptions = CatcherOptions(SilentReportMode(), [
ConsoleHandler(),
HttpHandler(HttpRequestType.post,
Uri.parse("https://api.telegram.org/bot<TOKEN>/sendMessage?chat_id=-469322015&text="),
printLogs: true,
),
]);
Catcher(MyApp(), debugConfig: debugOptions, releaseConfig: releaseOptions);
Everything is fine, but I must enter the error message into this parameter /sendMessage?chat_id=-469322015&text="Here Error Message"
Please help me how to do that
SOLVED by create own ReportMode 🙏🏻
class SilentReportMode extends ReportMode {
#override
void requestAction(Report report, BuildContext context) {
// no action needed, request is automatically accepted
print("HEREEEEE ======= ${report.error}");
try {
sendError(report);
} catch (e) {
}
super.onActionConfirmed(report);
}
Future sendError(Report report) async {
try {
Response response = await Dio().post('https://api.telegram.org/bot<TOKEN>/sendMessage?chat_id=-469322015&text=message: $report',
);
print("RESPONSE TELEGErammmmm ====== ${response.data}");
} catch (e) {
throw e;
}
}
#override
List<PlatformType> getSupportedPlatforms() =>
[PlatformType.Web, PlatformType.Android, PlatformType.iOS];
}
I want to handle http request flutter with some error message but I got so many errors here. I just make it based on the suggestion but it didn't work for me. Please, anyone, help me
Here is my function to call API
getData(data, apiUrl) async {
var tempUrl = _url + apiUrl + await _getToken();
Uri uri = Uri.parse(tempUrl);
var fullUrl = uri.replace(queryParameters: data);
var res;
try {
var response = await http.get(fullUrl, headers: _setHeaders()).timeout(
const Duration(seconds: 60));
print(response.statusCode);
if (response.statusCode != 200) {
res = {
"success": false,
"status": response.statusCode,
"message": _returnResponse(response)
};
}
else {
res = response;
}
}
on SocketException {
throw FetchDataException('No Internet connection');
}
on TimeoutException catch (e) {
res = {
"success": false,
"status": response.statusCode,
"message": "Connection timeout"
};
} on Error catch (e) {
print('Error: $e');
}
return res;
}
This is my return response for the others except 200
dynamic _returnResponse(http.Response response) {
switch (response.statusCode) {
case 400:
throw BadRequestException(response.body.toString());
case 401:
case 403:
throw UnauthorisedException(response.body.toString());
case 500:
default:
throw FetchDataException(
'Error occured while Communication with Server with StatusCode : ${response
.statusCode}');
}
}
and here is my app_exception.dart I got from StackOverflow and other forums
class AppException implements Exception {
final _message;
final _prefix;
AppException([this._message, this._prefix]);
String toString() {
return "$_prefix$_message";
}
}
class FetchDataException extends AppException {
FetchDataException([String message])
: super(message, "Error During Communication: ");
}
class BadRequestException extends AppException {
BadRequestException([message]) : super(message, "Invalid Request: ");
}
class UnauthorisedException extends AppException {
UnauthorisedException([message]) : super(message, "Unauthorised: ");
}
class InvalidInputException extends AppException {
InvalidInputException([String message]) : super(message, "Invalid Input: ");
}
I have tried so many suggestions but it didn't work at all
I got this error
Error: 'SocketException' isn't a type.
on SocketException {
^^^^^^^^^^^^^^^
Error: 'TimeoutException' isn't a type.
on TimeoutException catch (e) {
^^^^^^^^^^^^^^^^
I used dio package. That's more easier and bug-less than i make it
https://pub.dev/packages/dio
If the error occurring is on SocketException and Timeout exception ensure you have imported both dart.io and dart.async respectively in that file. As pertains to you code I was able to successfully run it but you can refer to the answer by Paresh Mangukiya for a step by step or refer here for more clarification on how to handle Network calls and exceptions with custom error responses in flutter.
This is my exception class. Exception class has been implemented by the abstract exception class of flutter. Am I missing something?
class FetchDataException implements Exception {
final _message;
FetchDataException([this._message]);
String toString() {
if (_message == null) return "Exception";
return "Exception: $_message";
}
}
void loginUser(String email, String password) {
_data
.userLogin(email, password)
.then((user) => _view.onLoginComplete(user))
.catchError((onError) => {
print('error caught');
_view.onLoginError();
});
}
Future < User > userLogin(email, password) async {
Map body = {
'username': email,
'password': password
};
http.Response response = await http.post(apiUrl, body: body);
final responseBody = json.decode(response.body);
final statusCode = response.statusCode;
if (statusCode != HTTP_200_OK || responseBody == null) {
throw new FetchDataException(
"An error occured : [Status Code : $statusCode]");
}
return new User.fromMap(responseBody);
}
CatchError doesn't catch the error when the status is not 200. In short error caught is not printed.
Try
void loginUser(String email, String password) async {
try {
var user = await _data
.userLogin(email, password);
_view.onLoginComplete(user);
});
} on FetchDataException catch(e) {
print('error caught: $e');
_view.onLoginError();
}
}
catchError is sometimes a bit tricky to get right.
With async/await you can use try/catch like with sync code and it is usually much easier to get right.
Let's say this is your function which throws an exception:
Future<void> foo() async {
throw Exception('FooException');
}
You can either use try-catch block or catchError on the Future since both do the same thing.
Using try-catch
try {
await foo();
} on Exception catch (e) {
print(e); // Only catches an exception of type `Exception`.
} catch (e) {
print(e); // Catches all types of `Exception` and `Error`.
}
Use catchError
await foo().catchError(print);
I was trying to find this answer when got to this page, hope it helps: https://stackoverflow.com/a/57736915/12647239
Basicly i was just trying to catch an error message from a method, but i was calling
throw Exception("message")
And in "catchError" i was getting "Exception: message" instead of "message".
catchError(
(error) => print(error)
);
fixed with the return in the above reference
Future < User > userLogin(email, password) async { try {
Map body = {
'username': email,
'password': password
};
http.Response response = await http.post(apiUrl, body: body);
final responseBody = json.decode(response.body);
final statusCode = response.statusCode;
if (statusCode != HTTP_200_OK || responseBody == null) {
throw new FetchDataException(
"An error occured : [Status Code : $statusCode]");
}
return new User.fromMap(responseBody); }
catch (e){
print(e.toString());
}