dartz: exception in function is not caught in attempt - flutter

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); });
}

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 unit test: type 'Null' is not a subtype of type 'T' in type cast

I tried to run unit test but i found an issue like below:
type 'Null' is not a subtype of type 'LoginResponse' in type cast
dart:async new Future.value
test/mock_utils.mocks.dart 259:22 MockBaseRepository.call
package:swap_recycling/features/auth/data/repository/login/login_repository_impl.dart 33:42 LoginRepositoryImpl.login
test/features/auth/data/repository/login/login_repository_impl_test.dart 30:38
and these my codes:
test.dart
void main() {
late MockLoginApiService _mockLoginApiService;
late MockSecureStorage _mockSecureStorage;
late MockBaseRepository _mockBaseRepository;
setUp(() {
_mockLoginApiService = MockLoginApiService();
_mockSecureStorage = MockSecureStorage();
_mockBaseRepository = MockBaseRepository();
});
test('Given LoginRepository When login Then return loginResponseSuccess',
() async {
final _repository = LoginRepositoryImpl(
_mockLoginApiService,
_mockSecureStorage,
_mockBaseRepository,
);
final result = await _repository.login(loginRequest);
expect(result, loginResponseSuccess);
verify(_mockLoginApiService.login(loginRequest)).called(1);
});
Login_repository.dart
final loginRepositoryProvider = Provider<LoginRepository>((ref) {
final loginApiService = ref.watch(provideLoginApiService);
final secureStorage = ref.watch(secureStorageProvider);
final baseRepository = ref.watch(baseRepositoryProvider);
return LoginRepositoryImpl(loginApiService, secureStorage, baseRepository);
});
class LoginRepositoryImpl extends LoginRepository {
final LoginApiService _loginApiService;
final SecureStorage _secureStorage;
final BaseRepository _baseRepository;
LoginRepositoryImpl(
this._loginApiService,
this._secureStorage,
this._baseRepository,
);
#override
Future<LoginResponse> login(LoginRequest request) async {
final result = await _baseRepository.call<LoginResponse>(
() => _loginApiService.login(request),
);
return result;
}
base_repository.dart
final baseRepositoryProvider = Provider<BaseRepository>((ref) {
return BaseRepository();
});
class BaseRepository {
Future<T> call<T>(FutureOr<T> Function() call) async {
try {
return await call();
} on DioError catch (e) {
if (e.error is SocketException) {
throw Failure(message: e.message);
}
if (e.response?.statusCode == serverErrorCode) {
throw Failure(
message: 'Server has some issue, please try again',
code: e.response?.statusCode,
);
}
throw Failure(
message: e.response?.statusMessage ?? 'Something went wrong',
code: e.response?.statusCode,
exception: e,
);
}
}
}
I tried to create a reuse function to return HTTP calls or handle errors.
I set generic type and send LoginResponse to tell BaseRepository that I want to return LoginResponse.
I can run it on an emulator but it doesn't work in a unit test.
Why It returns Null type?
I used mockito to generate mock class
mocks.dart
#GenerateNiceMocks([
//Repository
MockSpec<LoginRepository>(),
MockSpec<SettingRepository>(),
//Service
MockSpec<LoginApiService>(),
MockSpec<SecureStorage>(),
//Base
MockSpec<BaseRepository>(),
MockSpec<LoginRepositoryImpl>(),
])

How can I set the order of execution for different future in Flutter?

I'm implementing a flutter project using Getx library.
My current project has a format in which the response from the backend is recombined at the front end to the ui model and show on the screen. (Because the project's d-day is very close, so I could not change backend. I should just use a existing backend response).
Meanwhile, I was writing a code that some api call futures must keep the order.
To put it briefly, I receive a survey's result list and make a string list with survey's id (it is in the survey's result object).
After that, I receive a survey list and compare it to survey's result id list.
Through these courses, I will be able to know whether the survey has been completed or not from the survey list.
To implement this action, I declared two future, and I thought that the future was guaranteed the order by executing it with wait.
But nothing is changed in my view...
Below things are my codes.
class ExampleController extends GetxController {
final PreferenceManager _preferenceManager =
Get.find(tag: (PreferenceManager).toString());
/// ------------> related to survey values
final RxList<SurveyListUiModel> _rxSurveyListUiModelList = RxList.empty();
List<SurveyListUiModel> get surveyListUiModelList =>
_rxSurveyListUiModelList.toList();
final List<String> _surveyResultIdList = [];
void getSurveyConfigListWithSurveyResult() async{
Future<String> surveyResultListFuture =
_preferenceManager.getString('survey_result_list');
callDataService(
surveyResultListFuture,
onSuccess: _handleSurveyResultListResponseSuccess,
);
Future<String> surveyListFuture =
_preferenceManager.getString('survey_list');
await callDataService(
surveyConfigListFuture,
onSuccess: _handleSurveyListResponseSuccess,
);
setListLoading(false);
}
void _handleSurveyResultListResponseSuccess(String response) {
List<dynamic> list = jsonDecode(response) as List<dynamic>;
for (var element in list) {
SurveyConfigResponse surveyConfigResponse = SurveyConfigResponse.fromJson(
element['survey_config'] as Map<String, dynamic>);
_surveyResultIdList.add(surveyConfigResponse.id);
}
}
void _handleSurveyListResponseSuccess(String response) {
List<dynamic> list = jsonDecode(response) as List<dynamic>;
for (var element in list) {
SurveyConfigResponse surveyConfigResponse =
SurveyConfigResponse.fromJson(element as Map<String, dynamic>);
surveyListUiModelList.add(SurveyListUiModel(
surveyConfigId: surveyConfigResponse.id,
surveyConfigTitle: surveyConfigResponse.title,
isDiagnosed: _surveyResultIdList.contains(surveyConfigResponse.id),
));
_rxSurveyListUiModelList.refresh();
}
}
/// ------------> related to survey values
#override
void onInit() {
getSurveyConfigListWithSurveyResult();
super.onInit();
}
}
// callDataService method
dynamic callDataService<T>(
Future<T> future, {
Function(Exception exception)? onError,
Function(T response)? onSuccess,
Function? onStart,
Function? onComplete,
}) async {
Exception? _exception;
onStart == null ? showLoading() : onStart();
try {
final T response = await future;
if (onSuccess != null) onSuccess(response);
onComplete == null ? hideLoading() : onComplete();
return response;
} on ServiceUnavailableException catch (exception) {
_exception = exception;
showErrorMessage(exception.message);
} on UnauthorizedException catch (exception) {
_exception = exception;
showErrorMessage(exception.message);
} on TimeoutException catch (exception) {
_exception = exception;
showErrorMessage(exception.message);
} on NetworkException catch (exception) {
_exception = exception;
showErrorMessage(exception.message);
} on JsonFormatException catch (exception) {
_exception = exception;
showErrorMessage(exception.message);
} on NotFoundException catch (exception) {
_exception = exception;
showErrorMessage(exception.message);
} on ApiException catch (exception) {
_exception = exception;
} on AppException catch (exception) {
_exception = exception;
showErrorMessage(exception.message);
} catch (error) {
_exception = AppException(message: "$error");
logger.e("Controller>>>>>> error $error");
}
if (onError != null) onError(_exception);
onComplete == null ? hideLoading() : onComplete();
}
// example view
class ExampleView extends GetView<ExampleController> {
#override
Widget body(BuildContext context) {
return Obx(() => Text(controller.surveyListUiModelList.length.toString()));
}
}
What is the missing point in my codes..?
Edit 1
With obove code, the lists in the _handle~~~Success method has a right value.
A bit tricky to follow your code, but I think you should be awaiting the first call to callDataService(...)
I.e. this:
callDataService(
surveyLifeResultListFuture,
onSuccess: _handleSurveyResultListResponseSuccess,
);
should be:
await callDataService(
surveyLifeResultListFuture,
onSuccess: _handleSurveyResultListResponseSuccess,
);
Oh It's my mistake. The method callDataService is an already synchronized method...
So I did not have to consider asynchronization.
The order in which the code was written was applied in the order of execution.
A real problem was RxList.
In my question code, I writed a getter method of RxList like this.
List<SurveyListUiModel> get surveyListUiModelList => _rxSurveyListUiModelList.toList();
The toList() method is just extracting a Growable List from RxList.
it's code is here.
List<E> toList({bool growable = true}) {
if (this.isEmpty) return List<E>.empty(growable: growable);
var first = this[0];
var result = List<E>.filled(this.length, first, growable: growable);
for (int i = 1; i < this.length; i++) {
result[i] = this[i];
}
return result;
}
And, with that extracted list(copy the past state of RxList), I tried adding new items...
So, the obx widget in exampleView that is observing RxList did not response the past extracted list.
To solve this question, I changed my add items code with RxList keeping others in same.
before
void _handleSurveyConfigListResponseSuccess(String response) {
List<dynamic> list = jsonDecode(response) as List<dynamic>;
for (var element in list) {
SurveyConfigResponse surveyConfigResponse =
SurveyConfigResponse.fromJson(element as Map<String, dynamic>);
surveyListUiModelList.add(SurveyListUiModel(
surveyConfigId: surveyConfigResponse.id,
surveyConfigTitle: surveyConfigResponse.title,
isDiagnosed: _surveyResultIdList.contains(surveyConfigResponse.id),
));
}
_rxSurveyListUiModelList.refresh();
}
after
void _handleSurveyConfigListResponseSuccess(String response) {
List<dynamic> list = jsonDecode(response) as List<dynamic>;
for (var element in list) {
SurveyConfigResponse surveyConfigResponse =
SurveyConfigResponse.fromJson(element as Map<String, dynamic>);
_rxSurveyListUiModelList.add(SurveyListUiModel(
surveyConfigId: surveyConfigResponse.id,
surveyConfigTitle: surveyConfigResponse.title,
isDiagnosed: _surveyResultIdList.contains(surveyConfigResponse.id),
));
}
_rxSurveyListUiModelList.refresh();
}

flutter return future list in a var to use outside the loop

Hello I'm trying to recuperate the list value of a database.
i can but what i want is to export the result in a var so i can use in all my code just by calling "print(myList);"
this is my code :
static const URL =
'https://xxxhost/employee_actions3.php';
static Future<List<Employee>> getEmployees() async {
try {
final response = await http.post(Uri.parse(
URL,
));
print("getEmployees >> Response:: ${response.body}");
if (response.statusCode == 200) {
List<Employee> list = parsePhotos(response.body);
return list;
} else {
throw <Employee>[];
}
} catch (e) {
return <Employee>[];
}
}
and my classe Employee
class Employee {
String id;
String firstName;
String lastName;
Employee({required this.id, required this.firstName, required this.lastName});
factory Employee.fromJson(Map<String, dynamic> json) {
return Employee(
id: json['id'] as String,
firstName: json['lat'] as String,
lastName: json['lng'] as String,
);
}
}
can i have help please ?
There are two ways to access async data in most modern languages, including dart, they are:
1. By providing a callback then
2. By using the function in an async context and awaiting the result
I've wrapped the code above in a class called API so the examples below are easier to follow,
class API {
static const URL = 'https://xxxhost/employee_actions3.php';
static Future<List<Employee>> getEmployees() async {
try {
final response = await http.post(Uri.parse(URL));
print("getEmployees >> Response:: ${response.body}");
if (response.statusCode == 200) {
List<Employee> list = parsePhotos(response.body);
return list;
} else {
throw("${response.statusCode} Failed to parse photos");
}
} catch (e) {
throw e;
}
}
}
Method 1: Providing a callback to .then, this method will allow you to work with async actions in a synchronous context, but be aware it will not halt the execution flow.
void main() {
API.getEmployees().then((resp) => print(resp)).catchError(e) => print(e);
}
Method 2: Async/Await, this method will allow you to access the data inline, that is var x = await myAsyncFunc() remember the await keyword requires the function to be called within an async context. And the await keyword will halt the execution flow till the future completes.
void main() async {
try {
final list = await API.getEmployees();
print(list);
} catch (e) {
print(e);
}
}
Using either one of the two methods outlined above will allow you to access the data of the list later on.
Additional Reading:
Async programming in dart
Futures and error handling