dart is rethrow exception to the calling function - flutter

I have two classes the first is AppModel and AppViewModel
AppModel have a method that do some api operation and return the value according to the state.
AppViewModel has a method that calling the appModel object method to do the api request.
AppModel's method code is:
try {
final response = await apicall();
if (response.statusCode != 200)
return Resource.error(jsonDecode(response.error)['message']);
return Resource.success(
ProductDetails.fromJson(jsonDecode(response.body)).productData);
} catch (error, stackTrace) {
print('Exception occurred: $error stackTrace: $stackTrace');
return Resource.error(error);
}
The problem is when an error has raised the catch method is called as expected and it should return a value to the calling but instead the error is also thrown to the AppViewModel function and that cause an exception is not handled error.
How to prevent the AppModel function from throwing the error to the AppViewModel function and instead return the value specified.
The Resource class code is :
class Resource<T> {
Resource({#required this.status, this.data, this.message});
String message;
Status status;
T data;
factory Resource.success(T data) =>
Resource(status: Status.SUCCESS, data: data);
factory Resource.error(String msg) =>
Resource(status: Status.ERROR, message: msg);
factory Resource.loading() =>
Resource(status: Status.LOADING);
}

Related

Flutter future return type issue with async functions

I have a weird error with my Flutter code involving a Future<T> return type. I have a fairly simple piece of code that makes a get request to the backend, and a .then clause to handle the return. Everything's fine, and as soon as I add onError to handle possible back error (namely 403/404 errors), I have an issue regarding the return type, quoting that Future<dynamic> can't be returned when I expect a Future<String?>, and that's in spite of onError always returning null.
Any idea how I can fix that behavior? Thanks in advance !
Code:
Future<String?> getUserStatus(String id) async {
return requestManager.get("/users/$id/status")
.then((response) {
final dynamic userStatus =
(response as Map<String, dynamic>)["status"];
if (unsubStatus == null) {
return Future.value();
}
return Future.value(userStatus.toString());
}, onError: (error) {
print("An error occured when reading response : $error");
return null;
}).onError((error, stackTrace) => Future.value("NoStatus")); // I also tried to return null
}
Error:
A value of type 'Future<dynamic>' can't be returned from an async function with return type 'Future<String?>'.
- 'Future' is from 'dart:async'.
}).onError((error, stackTrace) => Future.value("NoStatus"));
I recommended using try bloc and await instead of using then and onError:
Future<String?> getUserStatus(String id) async {
try {
var response = await requestManager.get("/users/$id/status");
final dynamic unsubStatus = (response as Map<String, dynamic>)["status"];
if (unsubStatus == null) {
return null;
} else {
return unsubStatus.toString();
}
} catch (e) {
print("An error occured when reading response : $e");
return null;
}
}
Future<String?> getUserStatus(String id) async {
final result =await requestManager.get("/users/$id/status");
final dynamic userStatus = (response as Map<String, dynamic>)["status"];
if (unsubStatus == null) {
return Future.value();
}
return Future.value(userStatus.toString());
}

The argument type 'List<HospitalListModel>?' can't be assigned to the parameter type 'HospitalListModel'

i have bloc class and it throw an error with a message The argument type 'List<HospitalListModel>?' can't be assigned to the parameter type 'HospitalListModel'.
this is the bloc class:
class HospitalListBloc extends Bloc<HospitalListEvent, HospitalListState> {
HospitalListBloc() : super(HospitalListInitial()) {
final ApiRepository _apiRepository = ApiRepository();
on<GetCovidList>((event, emit) async {
try {
emit(HospitalListLoading());
final mList = await _apiRepository.fetchHospitalList();
emit(HospitalListLoaded(mList));
} on NetworkError {
emit(HospitalListError("Failed to fetch data. is your device online?"));
}
});
}
}
and the error is on emit(HospitalListLoaded(mList));, and in case if you want to know the API provider:
class ApiProvider {
final Dio _dio = Dio();
final String _url = 'http://lovemonster.my.id/hospital';
Future<List<HospitalListModel>?> fetchHospitalList() async {
try {
Response response = await _dio.get(_url);
return hospitalListModelFromJson(response.data);
} catch (error, stacktrace) {
print("Exception occurred: $error stackTrace: $stacktrace");
return Future.error("");
}
}
}
your HospitalListLoaded function should be
HospitalListLoaded(List<HospitalListModel> mList)
The argument type 'List<HospitalListModel>?' can't be assigned to the parameter type 'HospitalListModel'.
Your HospitalListLoaded function is declared as this:
void HospitalListLoaded(HospitalListModel model){
....
}
Here the parameter type is a single HospitalListModel, not a list of them. So, you can either pass a single HospitalListModel or you can change the parameter type to List<HospitalListModel>. In that case, you must change your logic inside that function.
Plus, notice the ? null operator. If the List you pass can be null, then the parameter type must be nullable. In that case,
void HospitalListLoaded(List<HospitalListModel>? models){
....
}
You are returning an Object of HospitalListModel but your Bloc class having method which accept list of HospitalListModel
You need to return list not an object
Check below code which will be useful
class ApiProvider {
final Dio _dio = Dio();
final String _url = 'http://lovemonster.my.id/hospital';
Future<List<HospitalListModel>?> fetchHospitalList() async {
try {
List<HospitalListModel> hospitalList = [];
Response response = await _dio.get(_url);
var mData = responseData.data as List;
hospitalList = mData.
.map<HospitalListModel?>((e) => hospitalListModelFromJson(e)
.toList();
return hospitalList;//return List not object
} catch (error, stacktrace) {
print("Exception occurred: $error stackTrace: $stacktrace");
return Future.error("");
}
}
}

Flutter how to show a custom exception message in a widget with custom error message

I want to show my custom error message with a custom exception right now, I only get Instance of 'CustomException' it's not showing the custom message.
I have the following
try {
batch.set(usersRef, user.toJson());
batch.set(accountsRef, account.toJson());
return await batch.commit();
} on FirebaseException catch (error) {
throw CustomException(
message: 'Future Error createUser',
subMessage: error.message.toString(),
);
}
My custom exception class
class CustomException implements Exception {
int? codeNumber;
String? codeString;
String message;
String subMessage;
CustomException({
this.codeNumber,
this.codeString,
required this.message,
required this.subMessage,
});
}
And my widget
}).catchError((error) {
setState(() {
_loader = false;
_errorMessage = error.toString();
});
});
You should override the toString() method in your CustomException class and return the message you want to show on an exception if you wish to show your custom message.
Add this to your CustomException class:
class CustomException implements Exception {
...
#override
String toString() {
return 'Exception: $message ($subMessage)';
}
}
Also, you can add a public method to your CustomException class. Then you can call that method on the CustomException object's instance to print the message:
class CustomException implements Exception {
...
String printStack() {
return 'Exception: $message ($subMessage)';
}
}
then:
throw CustomException(message: 'Exception title', subMessage: 'Exception description').printStack();
PS: You don't need to implement the Exception class. (Please correct me if I am wrong. )

Value of type 'Resource<dynamic>' can't be returned as T where T extends Resource

Here's my program (you can run it as a dart file):
/// Fetches data from TestRepository and shows result
main () async {
final repository = TestRepository();
final resource = await repository.fetchString();
if (resource.isError) {
print("| Error: ${resource.error}");
} else {
print("| Success: ${resource.data}");
}
}
/// A class to get Success or Error responses
class Resource<S> {
final S? _data;
final String? _message;
Resource.success(S data)
: _data = data,
_message = null;
Resource.error(String message)
: _data = null,
_message = message;
bool get isError => _message != null;
S get data => _data!;
String get error => _message!;
}
/// A sample repository class to use API and handle errors
class TestRepository {
Future<Resource<String>> fetchString() {
return _dummyApiCall().useErrorHandler();
}
/// Dummy API Call mimicking
Future<Resource<String>> _dummyApiCall() async {
await Future.delayed(const Duration(seconds: 1));
if (true) {
// to mimic an exception so that we can catch it
throw Exception("Error Occurred");
}
return Resource.success("Hello World");
}
}
/// An extension to wrap Resource Futures with error handler
extension FutureResourceExt<T extends Resource> on Future<T> {
Future<T> useErrorHandler() {
return onError((error, stacktrace) => handleError(error));
}
}
/// A function to handle errors thrown by Future.onError
T handleError<T extends Resource>(dynamic error) {
return Resource.error("Error: $error");
}
I have a code error on following line:
return Resource.error("Error: $error");
The error says:
A value of type 'Resource<dynamic>' can't be returned from the function 'handleError' because it has a return type of 'T'
If I change the implementation to add as T to above statement, code error disappears and gets thrown on runtime.
Unhandled Exception: type 'Resource<dynamic>' is not a subtype of type 'Resource<SomeDataType>' in type cast
I don't know why can I not assign Resource to T extends Resource return type.
How should I implement Resource class such that I can do this without knowing S type of Resource?
Here's the line with error code:
return Resource.error("Error: $error");
Here's the solution:
return Resource<Never>.error("Error: $error") as T;
I received a hint from this nice answer. Never knew something like Never existed.

dartz: exception in function is not caught in attempt

I am using dartz with flutter. I want to use Either type with all API calls as suggested in this article
Below is the way I have wrapped API to Task
Future<Either<ApiException, SendOtpDto>> receiveOtp(
String phoneNumber) async {
return Task<SendOtpDto>(() async {
final String url = 'AuthenticationApi/sendOtp/$phoneNumber';
final Map<String, dynamic> response = await apiWrapper.get(url);
final SendOtpDto dto = SendOtpDto.fromJson(response);
if (!dto.success) {
throw ServerErrorException(dto.error); //Exception is thrown here
}
return dto;
}).attempt().mapLeftToFailure().run();
}
I expect attempt() should catch all throw as ApiException. Currently it gives error type 'Future<Either<ApiException, dynamic>>' is not a subtype of type 'FutureOr<Either<ApiException,
SendOtpDto>>
Below is how I am using the Api
if (event is GetOtpButtonPressed) {
yield LoginApiLoading();
final Either<ApiException, SendOtpDto> result = await apiManager.authentication(context).receiveOtp(event.username);
yield result.fold<LoginState>(
(ApiException exception) { return LoginFailure(error: exception.toString()); },
(SendOtpDto dto) { return LoginOtpSent(dto: dto); });
}