I'm playing with Flutter coming from a Xamarin.Forms experience and I'm trying to implement some common scenarios I dealt with in my previous applications.
I have a RestClient
service class containing all the low-level Rest calls performed with Dio
.
I want to logout from the application when one of the calls returns 401 - Unauthorized
(I know it can be handled better... I'm simplifying).
I'm using Riverpod
to manage my business logic and I was struggling to try to communicate with an authenticationProvider
, because inside RestClient
there is no Ref
to interact with providers.
The only way I found was to pass a Ref
in RestClient
constructor, so that I can update authenticationProvider
state.
While it's working fine, I'm doubtful about mixing things and creating a deep link between two components that maybe should not communicate in this way.
Here's my code.
rest_client.dart
class RestClient {
final Dio dio;
final Ref ref;
RestClient._({required this.dio, required this.ref});
factory RestClient({required Ref ref}) {
var dio = Dio(BaseOptions(
baseUrl: "https://myurl.com/",
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
));
dio.interceptors.add(InterceptorsWrapper(
onError: (e, handler) {
if (e.response != null && e.response!.statusCode == 401) {
ref.read(authenticationProvider.notifier).invalidateToken();
}
},
));
return RestClient._(dio: dio, ref: ref);
}
/**** ... *****/
/**** Methods to call api endpoints *****/
/**** ... *****/
}
authentication_provider.dart
class AutenthicationNotifier extends StateNotifier<String?> {
// *** Constructor and other methods ***
void invalidateToken() {
// body
}
}
final authenticationProvider = StateNotifierProvider<AutenthicationNotifier, String?>((ref) {
return AutenthicationNotifier();
});
Finally, I have a simple Provider
that gives access to RestClient
.
final restClientProvider = Provider((ref) => RestClient(ref: ref));
This is the exact way that you should do it. If the class used has its own provider function using ref is the intended way to communicate. After all from the perspective of riverpod the only difference between the two is one having a state.
A syntax suggestion: You can make your RestClient much simpler by just having the constructor
RestClient({required this.dio, required this.ref});
and doing all the initialization in the provider function:
final restClientProvider = Provider((ref) {
var dio = Dio(BaseOptions(
baseUrl: "https://myurl.com/",
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
));
dio.interceptors.add(InterceptorsWrapper(
onError: (e, handler) {
if (e.response != null && e.response!.statusCode == 401) {
ref.read(authenticationProvider.notifier).invalidateToken();
}
},
));
return RestClient(ref: ref, dio: dio);
});