Riverpod을 사용해 필요한 값들을 Provider 안에 집어넣어보자!
이 과정을 통해서 코드가 더 깔끔해지는 걸 볼 수 있을 것이다... ㅎㅎㅎ
먼저 사용하고 있는 여러 가지 변수들, Dependency들이 있음.
대표적으로 dio를 중복 사용 중임...
그리고 dio에서 CustomInterceptor를 사용할 때에도 storage도 똑같이 쓰고 있음.
dio와 storage를 같은 인스턴스로 사용할 수 있다면???
매번 dio 안에 interceptors를 생성해야하는 반복 작업을 하지 않아도 됨!
👉 이러한 반복적인 작업 방지를 위해 Riverpod을 사용한다.
❓ 어떻게 사용하면 될까?
👉 일반적으로 dio를 선언하는 방식을 그냥 provider 안에 선언하면 된다.
① dio.dart에 Provider 생성
dio.dart 코드로 가서 provider를 생성해준다.
①-1. dio 선언
Provider를 불러오고 ref를 받고서 값을 반환해줄 수 있었음.
나는 dio를 반환해줄거임.
Interceptor를 넣을거니깐 일반함수로다가 만들어주고, dio 선언해준다.
①-2. CustomInterceptor 넣어주기
원래 다른 곳에다 했던 것처럼 CustomInterceptor를 넣어준다.
그런데 이때 문제는 storage를 또 가져와야 함!
그리고 혹시 모르니깐 하나의 인스턴스를 사용해 storage를 관리해주면 좋을 듯!
② common/secure_storage/secure_storage.dart 폴더 및 파일 생성
storage 관리를 위해 common/secure_storage/secure_storage.dart
폴더와 파일을 생성해준다.
②-1. Provider 불러오기
FlutterSecureStorage( )값이 한 번 secureStorageProvider가 생성될 때 반환되면,
하나의 FlutterSecureStorage( )값을 가지고 계속 프로젝트 안에서 돌려 사용!
②-2. main에 ProviderScope 넣어주기
Provider를 선언한 후, main에 코드 추가해줘야겠지?
바로 ProviderScope!
최상위에 ProviderScope를 넣어준다.
dio로 돌아와서 코드를 다시 봐보자.
이 storage를 가지고 있는 곳은 const/data.dart임!
③ storage 불러오는 코드 삭제
const/data.dart로 가보자.
근데 이제 여기서 안 쓸거라서 지워준다!
글로벌한 변수들은 무조건 Provider를 넣어서 어디서든 똑같은 값을 유연하게 가질 수 있게 구현할거임!
storage 불러오는 코드를 삭제해서 storage 키워드에 에러 발생함.
이걸 해결해보자.
④ secureStorageProvider를 ref.watch 해준다.
그러면 빨간 에러가 사라지는 걸 볼 수 있다.
storage가 secureStorageProvider 안에서 반환해주는 FlutterSecureStorage임!
(⭐ ref를 사용하면 또 다른 Provider를 가져올 수 있음~)
⑤ secureStorageProvider 제너릭 추가
secure_storage.dart에서 secureStorageProvider Provider에 제너릭을 추가해준다.
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
final secureStorageProvider =
Provider<FlutterSecureStorage>((ref) => const FlutterSecureStorage());
그러면 dio에서 storage를 받아서 그대로 CustomInterceptor(storage: storage)에 넣어주면,
dio가 항상 CustomInterceptor를 secureStorageProvider 안에서 생성한
똑같은 Provider를 가지고 생성될거임!
⑥ dio 리턴해주기
그러면 final dioProvider = Provider 이 Provider는 dio를 리턴해줌!
그럼 반환되는 return dio;는 하나의 dio 인스턴스(final dio = Dio( );)를 가지고
storage도 secureStorageProvider 안에서 제공해주는
하나의 인스턴스의 storage를 사용해서
// dio를 생성해서
dio.interceptors.add(
CustomInterceptor(storage: storage),
);
dio를 생성해서 dioProvider를 쓰면 똑같은 dio로 매번 반환해줄 수 있게되는 것임!
그럼 이 dio를 dio.dart에다가...
미리 생성해 놓은
dioProvider를 써서 변경해주면 되겠다는 생각이 들 수 있음!
틀린 건 아니지만, 일차원적인 생각일 수 있음....
이게 무슨 말인지... 차근차근 봐보자!
⑦-1. RestaurantDetailScreen 코드 수정
RestaurantDetailScreen에서 StatelessWidget을 ConsumerWidget으로 바꾸고,
build 함수에 WidgetRef ref 파라미터 추가
⑦-2. Future에서 WidgetRef ref를 직접 받아보기
먄약에 Future에서 WidgetRef ref를 직접 받는다면 (아래 future에도 ref를 넣어줘야겠지?)
그럼 이 ref를 써서 dio를 매번 생성하는 게 아니라
dio.dart 파일에 생성해 놓은...
그러니까 dioProvider 안에 생성된 interceptors까지 적용된 dio(드래그된)를 불러올 수 있음.
⑦-3. RestaurantDetailScreen에 작성한 dio 코드 삭제
⑦-4. dioProvider를 ref.watch
RestaurantDetailScreen에 작성한 dio 코드를 삭제하고, dioProvider를 ref.watch 해준다.
그리고 이 dio의 경우엔 어디서 불러와도 ref.watch 또는 ref.read 한 다음에
(dioProvider) 이렇게 파라미터 안에 넣고 반환값을 받으면
이 반환받은 dio는 무조건 dioProvider에서 앱이 빌드됐을 때,
맨 처음 한번 생성된 final dio = Dio( );
항상 똑같은 인스턴스의 dio 이 CustomInterceptor가 적용된 dio를 무조건 받을 수 있음.
그럼 이것도 확인해보자!
⑧ RestaurantScreen 코드 수정
RestaurantScreen 코드로 가서 StatelessWidget을 ConsumerWidget으로 바꾸고,
build 함수에 WidgetRef ref 파라미터를 추가해준다.
⑧-1. paginateRestaurant에 ref 넣어주기
여기에는 WidgetRef ref 넣어주면 됨.
⑧-2. RestaurantScreen dio 코드 삭제
그러면 CustomInterceptor를 적용한 함수 안에서 생성한 dio가 아니라...
ref.watch(dioProvider); 를 해준다.
LoginScreen 오류 해결
storage를 이제 가져올 수 없어서 난 오류임...
잠깐만 위로 올라가서 StatefulWidget을 ConsumerStatefulWidget으로 바꿔준다.
State도 ConsumerState로 바꿔준다.
그럼 StatefulWidget을 riverpot을 불러올 수 있는 위젯으로 변경할 수 있다.
그러면 이제 storage가 이게 아니라...
secureStorageProvider를 가져오면 이제 storage를 그대로 쓸 수 있다.
오류 사라짐!
근데 다만 글로벌하게 data.dart 안에 선언한 게 아니라
상태관리 툴 안에다 넣은(Provider 안에 넣은) 똑같은 인스턴스의 storage를 사용할 수 있음!
SplashScreen 쪽에서도 에러가 있는데, 같은 에러임...!
아래에도 이렇게 오류가 있는데...
여기에다가도 이렇게 불러주면 에러 사라짐!
그리고 RestaurantScreen 코드를 보면 dio를 그냥 dioProvider 안에서 받아서
RestaurantRepository 안에다 넣어줌. 로그도 잘 찍혀있음!
이렇게 잘 찍히는 이유는?
dioProvider 안을 보면 dio에다가 CustomInterceptor를 적용했는데
그 적용이 된 똑같은 인스턴스에 dio를 집어넣었기 때문!
그래서 이 dio를 RestaurantScreen과 RestaurantDetailScreen에서
똑같은 인스턴스를 사용했기 때문에 상세페이지에 들어가도 로그가 찍힘!