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];
}
Related
I have an interceptor to send jwt token and to use the refresh_token endpoint when the jwt expires.
With an expired jwt I get
Error: Bad state: Future already completed
error, but the request is processed right anyway. In the console I see one successful response and one with 401 error afterward. How can I solve this issue?
custom_interceptor.dart
class CustomInterceptor extends DefaultInterceptor {
ISecureStorage secureStorageService = ISecureStorage();
#override
void onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
LoginModel loginModel = await secureStorageService.readLoginModel();
options.headers = {
"Content-type": "application/json",
"Authorization": "Bearer ${loginModel.access_token}"
};
return super.onRequest(options, handler);
}
#override
void onError(err, handler) async {
if (err.response?.statusCode == 401) {
final Dio _dio = DioConfig().dio;
LoginModel loginModel = await secureStorageService.readLoginModel();
Uri uri = Uri.https(
"$BASE_URL", "/refresh_token_url");
try {
await _dio.postUri(uri, data: {
"refresh_token": loginModel.refresh_token,
"grant_type": "refresh_token"
}).then((value) async {
if (value?.statusCode == 200) {
await secureStorageService.deleteLoginModel();
LoginModel newLoginData = LoginModel.fromJson(value.data);
await secureStorageService.saveLoginModel(loginModel: newLoginData);
err.requestOptions.headers["Authorization"] =
"Bearer " + newLoginData.refresh_token;
final opts = new Options(
method: err.requestOptions.method,
headers: err.requestOptions.headers);
final cloneReq = await _dio.request(err.requestOptions.path,
options: opts,
data: err.requestOptions.data,
queryParameters: err.requestOptions.queryParameters);
return handler.resolve(cloneReq);
}
return err;
});
return super.onError(err, handler);
} catch (e, st) {
print("ERROR: " + e);
print("STACK: " + st.toString());
return super.onError(err, handler);
}
} else {
return super.onError(err, handler);
}
}
}
class DefaultInterceptor extends Interceptor {
#override
void onRequest(
RequestOptions options, RequestInterceptorHandler handler) async {
print(
'REQUEST[${options.method}] => PATH: ${options.path} | DATA => ${options.data} | JWT => ${options.headers}');
return super.onRequest(options, handler);
}
#override
void onResponse(Response response, ResponseInterceptorHandler handler) {
print(
'RESPONSE[${response.statusCode}] => PATH: ${response.requestOptions.path} | DATA => ${response.data}');
super.onResponse(response, handler);
return;
}
#override
void onError(DioError err, ErrorInterceptorHandler handler) async {
print(
'ERROR[${err.response?.statusCode}] => PATH: ${err.requestOptions.path} | SENT_DATA => ${err.requestOptions.data} | RECEIVED_DATA => ${err.response?.data}');
return super.onError(err, handler);
}
}
dio_config.dart
class DioConfig {
static DioConfig _singletonHttp;
Dio _dio;
get dio => _dio;
factory DioConfig() {
_singletonHttp ??= DioConfig._singleton();
return _singletonHttp;
}
DioConfig._singleton() {
_dio = Dio();
}
dispose() {
_dio.close();
}
}
i_secure_storage.dart
abstract class ISecureStorage {
factory ISecureStorage() => getSecureStorage();
Future<LoginModel> readLoginModel() async => LoginModel.empty;
Future<bool> saveLoginModel({LoginModel loginModel}) async => false;
Future<bool> deleteLoginModel() async => false;
}
web_secure_storage.dart
ISecureStorage getSecureStorage() => WebSecureStorageService();
class WebSecureStorageService implements ISecureStorage {
final String _loginData = 'loginData';
html.Storage webStorage = html.window.localStorage;
#override
Future<LoginModel> readLoginModel() async {
return webStorage[_loginData] == null
? LoginModel.empty
: LoginModel.fromJson(jsonDecode(webStorage[_loginData]));
}
#override
Future<bool> saveLoginModel({ LoginModel loginModel}) async {
webStorage[_loginData] = jsonEncode(loginModel);
return true;
}
#override
Future<bool> deleteLoginModel() async {
webStorage.remove(_loginData);
return true;
}
}
mobile_secure_storage.dart
ISecureStorage getSecureStorage() => MobileSecureStorageService();
class MobileSecureStorageService implements ISecureStorage {
final String _loginModel = 'loginModel';
FlutterSecureStorage storage = const FlutterSecureStorage();
#override
Future<LoginModel> readLoginModel() async {
try {
dynamic _loginData = await storage.read(key: _loginModel);
return _loginData == null ? LoginModel.empty : LoginModel.fromJson(jsonDecode(_loginData));
} on PlatformException catch (ex) {
throw PlatformException(code: ex.code, message: ex.message);
}
}
#override
Future<bool> saveLoginModel({LoginModel loginModel}) async {
try {
await storage.write(key: _loginModel, value: jsonEncode(loginModel));
return true;
} on PlatformException catch (ex) {
throw PlatformException(code: ex.code, message: ex.message);
}
}
#override
Future<bool> deleteLoginModel() async {
try {
await storage.delete(key: _loginModel);
return true;
} on PlatformException catch (ex) {
throw PlatformException(code: ex.code, message: ex.message);
}
}
}
EDIT:
IN MY CASE the problem was in the first
return super.onError(err, handler);
It must be return null;
So I got it working
You are using Dio for the requests. Version 4.0.6 of Dio which is the most recent version as of today has this known issue. Please refer to the same on GitHub here.
Solution
Downgrade your Dio package to the last stable version that was known to not have this issue until a new version is released.
In your pubspec.yaml.
dio: 4.0.4
Then get packages again.
> flutter pub get
For anyone else having this issue and it is not solved by only downgrading dio: Downgrade dio to 4.0.4 AND remove connectTimeout from your BaseOptions.
Update 13/02/23:
dio v5.0.0 finally contains a fix for this issue.
Details: At the end flutter-china has transferred the ownership of the dio repo to CFUG and all the changes from the diox hard fork have been merged into the original dio repo, including the fix for this issue.
Update 15/12/22:
diox is a hard fork of dio made by CFUG group with the aim of keeping dio well maintained. In diox, this issue has already been fixed.
Original answer:
Related issue: https://github.com/flutterchina/dio/issues/1480
There are several open PRs that (try to) tackle this bug:
https://github.com/flutterchina/dio/pull/1470
https://github.com/flutterchina/dio/pull/1496
https://github.com/flutterchina/dio/pull/1550
https://github.com/flutterchina/dio/pull/1565
If you do not want to downgrade to dio 4.0.4 as other answers suggest, you can depend on some of these forks until one of them is merged into the official repository.
In my case, I've reviewed and tested #ipcjs's solution and seems to be working as expected:
dio:
git:
url: https://github.com/ipcjs/dio
path: dio/
ref: b77af132442bf3266ccf11b50ce909711455db3a
class InterceptorsWrapper extends QueuedInterceptorsWrapper {
#override
void onRequest(RequestOptions options,RequestInterceptorHandler handler){
log('send request:${options.baseUrl}${options.path}');
final accessToken = Storage.instance.box.read("accessToken");
options.headers['Authorization'] = 'Bearer $accessToken';
super.onRequest(options, handler);
}
#override
void onError(DioError err, ErrorInterceptorHandler handler) {
switch (err.type) {
case DioErrorType.connectTimeout:
case DioErrorType.sendTimeout:
case DioErrorType.receiveTimeout:
throw DeadlineExceededException(err.requestOptions);
case DioErrorType.response:
switch (err.response?.statusCode) {
case 400:
throw BadRequestException(err.requestOptions);
case 401:
throw UnauthorizedException(err.requestOptions);
case 404:
throw NotFoundException(err.requestOptions);
case 409:
throw ConflictException(err.requestOptions);
case 500:
throw InternalServerErrorException(err.requestOptions);
}
break;
case DioErrorType.cancel:
break;
case DioErrorType.other:
throw NoInternetConnectionException(err.requestOptions);
}
super.onError(err, handler);
}
}
...
...
This is how I done my Dio Interceptor,
you don't have to return anything in your void onRequest() simply call super.onRequest() and don't use handler instance in interceptor class like
return handler.resolve(cloneReq);
that part is already done inside onRequest(). I solved my problem in this way
you can also try.
thank you.
To instantly solve this problem just comment out the "connectTimeOut" field from DioBaseOptions as follows:
connectTimeout: 30000,
To solve this error, I did like that
void onError(DioError err, ErrorInterceptorHandler handler) async {
//Halding refresh token other logic
//Future.delay solve my error.
Future.delayed(const Duration(seconds: 5), () => super.onError(err,handler));
}
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");
}
}
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");
}
I wrote an one-page desktop app to communicate with TCP Server.
In my code, I use Socket.listen() method to receive data and it is OK.
I used single subscription and it was enough for me.
I tried to convert it to StreamProvider[Riverpod] and I failed.
I used StreamController() then I get bad state.
I used StreamController.broadcast() and I couldn't get data from socket
Could you suggest me correct way?
For a side note: I'm not an experienced flutter developer, just try to learn :)
I added code blocks to below and also full code.
For the full code: https://gist.github.com/sphinxlikee/3cbfa47817a5187c7b67905028674041
UI:
Working code;
Future<void> createConnection() async {
try {
_socket = await Socket.connect(serverAddress, serverPort);
_changeConnectionState();
} catch (e) {
print('connection has an error and socket is null.');
print(e);
return;
}
listenSocket();
}
void listenSocket() {
_socket.listen(
(event) {
_getData(String.fromCharCodes(event));
print('received: $receivedData');
if (!_dataReceived) {
_changeDataReceivedState();
}
},
)
..onDone(
() {
_changeConnectionState();
_streamDone();
print('socket is closed');
},
)
..onError(
(error, stackTrace) {
print('$error');
},
);
}
Working code - UI side
class ReceivedData extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
final receivedData = watch(tcpClientProvider).receivedData;
return Text('Received data: $receivedData');
}
}
For the StreamProvider I tried,
Future<void> createConnection() async {
try {
_socket = await Socket.connect(serverAddress, serverPort);
streamController.sink.add(_socket.listen((event) => String.fromCharCodes(event)));
_changeConnectionState();
} catch (e) {
print('connection has an error and socket is null.');
print(e);
return;
}
}
StreamProvider - UI side
final streamProvider = StreamProvider.autoDispose(
(ref) async* {
await for (final value in ref.watch(tcpClientProvider).streamController.stream) {
yield value;
}
},
);
class ReceivedDataWithProvider extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
AsyncValue receivedData = watch(streamProvider);
return receivedData.when(
data: (data) => Text('Received data: $data'),
loading: () => const CircularProgressIndicator(),
error: (err, stack) => Text('error'),
);
}
}
Socket implements Stream, so you could just write:
final streamProvider = StreamProvider.autoDispose<Uint8List>((ref) {
return ref.watch(tcpClientProvider)._socket;
});
If you still want to add a listener, there's no harm in having one if you need:
final streamProvider = StreamProvider.autoDispose<Uint8List>((ref) {
final client = ref.watch(tcpClientProvider);
return client._socket
..listen(
(event) {},
).onDone(
() {
client
.._changeConnectionState()
.._streamDone();
print('socket is closed');
},
);
});
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.