Usually while writing my applications in Flutter, I like to mention the return types everywhere. This ensures that I do not return the wrong objects and also checks my code while I'm still writing it.
Let us say that I'm writing a method inside the ApiProvider which on a successful event returns a User object.
i.e.,
Future<UserModel> checkAuth (String username, String password) async {
final result = client.get (.............);
final UserModel model = UserModel.fromJson(result);
return model;
}
Now everything is fine until I introduce try-catch blocks for the error handling. This would modify my code as below :
Future<UserModel> checkAuth (String username, String password) async {
try {
final result = client.get (.............);
final UserModel model = UserModel.fromJson(result);
return model;
} on Exception catch(e) {
return ErrorModel(e.statusCode, e.message);
}
}
Now, as you can see, in case of an error I will have to return an error model or maybe just the string (e.message). However, my function could return only the instances of UserModel. I do NOT want to make the return type dynamic as It makes my code very loosely coupled.
How can I overcome this problem and is there a better way to do this ? Usually after getting the api response or an error response, I would populate the results inside a stream so that I could show them to the user on the UI. I make use of bloc architecture.
Any help would be appreciated. Thanks in advance!
CodePudding user response:
Use Exceptions:
class ApiProviderException implements Exception {
final int? statusCode;
final String? message;
ApiProviderException(this.statusCode, this.message);
@override
String toString() {
return '$statusCode - $message';
}
}
...
Future<UserModel> checkAuth(String username, String password) async {
try {
final result = client.get (.............);
final UserModel model = UserModel.fromJson(result);
return model;
} on Exception catch (e) {
throw ApiProviderException(e.statusCode, e.message);
}
}
To use it:
try {
final model = await checkAuth(username, password);
// some code
} on ApiProviderException catch (e) {
// some code
}
CodePudding user response:
You can use either_dart
package, it handles the "error" L
and "success" R
state of your function return, without the need of explicitly declaring the base class.
Using your function as example:
import 'package:either_dart/either.dart';
Future<Either<ErrorModel, UserModel>> checkAuth(String username, String password) async {
try {
final result = client.get (.............);
final UserModel model = UserModel.fromJson(result);
return Right(model);
} on Exception catch(e) {
return Left(ErrorModel(e.statusCode, e.message));
}
}
And to use it:
final Either<ErrorModel, UserModel> result = await checkAuth(...);
if (result.isLeft) {
// error...
final ErrorModel user = result.left;
} else if (result.isRight) {
// sucess...
final UserModel user = result.right;
}
You can handle it's result in a kind of state manager class like BLoC
, Mobx
Store, Provider
and send to the UI the correct text to be rendered. So:
// services/my_service_class.dart
class MyService {
Either<L, R> callSomeApi() { }
}
// store/my_ui_manager_class.dart
class MyUIStateManagerClass extends BLoC {
final MyService service = MyService();
Future<void> callSomething() async {
final result = await service.callSomeApi();
if (result.isLeft) {
// error...
final ErrorModel user = result.left;
} else if (result.isRight) {
// sucess...
final UserModel user = result.right;
}
}
}
// widgets/my_ui_class.dart
class MyWidget {
MyUIStateManagerClass stateManager;
void initState() {
super.initState();
stateManager = MyUIStateManagerClass();
stateManager.callSomething();
}
Widget build(BuildContext context) {
return ...
}
}