해결된 질문
작성
·
306
0
안녕하세요.
Riverpod 상태관리에서 StateNotiferProvider 대신에 NotifierProvider를 사용하는 것을 권장하신다고 하셨는데요.
StateNotifierProvider 로 작성한 코드를 NotiferProvider 로 변경하고 싶은데 잘 안되어 문의 드립니다.
contactStateProvier 코드로는 정상 동작됩니다.
이 코드를 NotifierProvider 로 변경해보려고 하는데 잘 안되네요.
final contactStateProvider =
StateNotifierProvider<ContactStateNotifier, ContactResultBase>(
(ref) {
final repository = ref.watch(restClientProvider);
final notifier = ContactStateNotifier(restClient: repository);
return notifier;
},
);
class ContactStateNotifier extends StateNotifier<ContactResultBase> {
final RestClient restClient;
ContactStateNotifier({required this.restClient})
: super(ContactResultLoading()) {
postContactList();
}
postContactList() async {
final resp = await restClient.postContactList(
Crypto.AES_encrypt(Crypto.URLkey()), '');
state = resp;
}
}
@RestApi(baseUrl: RetrofitURL.baseUrl)
abstract class RestClient {
factory RestClient(Dio dio, {String baseUrl}) = _RestClient;
@POST(RetrofitURL.mLogin)
@FormUrlEncoded()
Future<LoginResponse> userLogin(
@Field() String keyword,
@Field() String userID,
@Field() String password,
@Field() String uID,
@Field() String mfoneNO,
);
@POST(RetrofitURL.contactData)
@FormUrlEncoded()
Future<ContactResult> postContactList(
@Field() String keyword,
@Field() String search,
);
}
final secureStorageProvider =
Provider<FlutterSecureStorage>((ref) => const FlutterSecureStorage());
final dioProvider = Provider<Dio>((ref) {
final dio = Dio();
final storage = ref.watch(secureStorageProvider);
dio.interceptors.add(LogInterceptor());
dio.interceptors.add(
CustLogInterceptor(storage: storage,),
);
return dio;
});
final restClientProvider = Provider<RestClient>((ref) {
final dio = ref.watch(dioProvider);
final repository = RestClient(dio);
return repository;
});
contactStateProvider 를 변경시도 해보다가 에러가 발생한 코드
final contactProvider =
NotifierProvider<ContactNotifier, ContactResultBase>(
() {
final repository = ref.watch(restClientProvider);
final notifier = ContactNotifier(restClient: repository);
return notifier;
},
);
class ContactNotifier extends Notifier<ContactResultBase> {
@override
ContactResultBase build() => ContactResultLoading();
final RestClient restClient;
ContactStateNotifier({required this.restClient}) {
postContactList();
}
postContactList() async {
final resp = await restClient.postContactList(
Crypto.AES_encrypt(Crypto.URLkey()), '');
state = resp;
}
}
답변 1
0
안녕하세요
첨부해 주신 코드상에서 확인할 수 있는 내용에 대해 피드백을 드립니다.
상태 변경을 청취하여 새로고침을 할 필요가 없는 경우 ref.watch
가 아니라 ref.read
로 사용하시는게 좋습니다.
final secureStorageProvider =
Provider<FlutterSecureStorage>((ref) => const FlutterSecureStorage());
final dioProvider = Provider<Dio>((ref) {
final dio = Dio();
final storage = ref.read(secureStorageProvider);
dio.interceptors.add(LogInterceptor());
dio.interceptors.add(
CustLogInterceptor(storage: storage,),
);
return dio;
});
final restClientProvider = Provider<RestClient>((ref) {
final dio = ref.read(dioProvider);
final repository = RestClient(dio);
return repository;
});
Notifier의 경우 ref.watch 코드를 build()
함수 안에서 다룬다는 점이 StateNotifier와 다릅니다. 자세한 예제는 관련 공식 문서 링크를 참고해 주세요.
final contactProvider =
NotifierProvider<ContactNotifier, ContactResultBase>(ContactNotifier.new);
class ContactNotifier extends Notifier<ContactResultBase> {
@override
ContactResultBase build() {
return ContactResultLoading();
}
postContactList() async {
final resp = await ref.read(restClientProvider).postContactList(
Crypto.AES_encrypt(Crypto.URLkey()), '');
state = resp;
}
}
새로운 코드를 실행했을 때 발생하는 에러 코드 메세지 및 관련 코드를 공유해 주셔야 정확한 원인 분석 및 피드백을 드릴 수 있을 것 같습니다.
먼저 에러가 발생하는 이유는 postContactList()
함수는 아무것도 반환하지 않는데, postdata
라는 변수에 결과를 할당한 뒤 뒤쪽에서 ContactResult
타입으로 캐스팅하려 했기 때문에 에러가 발생합니다.
postContactList() async {
final resp = await ref.read(restClientProvider).postContactList(
Crypto.AES_encrypt(Crypto.URLkey()), '');
state = resp;
}
final postdata = ref.watch(contactProvider.notifier).postContactList();
그 외에도 postContactList()
함수를 build()
함수 내에서 watch로 접근하여 호출하고 있는데, 그렇게 되면 ContactNotifier
에서 상태가 변경될 때 마다 build()
함수가 다시 실행되기 때문에 꼬리에 꼬리를 무는 형태로 postContactList()
함수가 무한 반복 호출될 수 있습니다.
ContactResultProviderPage
클래스를 StatefulWidget
으로 변경한 뒤 initState()
함수에서 postContactList()
함수를 호출하도록 변경하시는 것을 권장드립니다.
추가로 ContactNotifier
의 상태 클래스에 Dart sealed 키워드를 활용하면 상태 클래스를 열거 가능한 하위 클래스들로 다루어 switch 구문에서 default 값 없이 사용할 수 있습니다.
위 문제들을 해결한 샘플 코드를 다음 DartPad 링크에서 확인해 주세요.
감사합니다 :)
정말 감사드립니다.
구글 검색으로 찾은 간단한 예제들로는 이해가 잘 안되었습니다.
덕분에 상태관리에 대해 조금 이해하게 되었습니다.
알려주신 코드 덕분에 아래와 같이 수정하여 해결하였습니다.
final contactProvider =
NotifierProvider<ContactNotifier, ContactResultBase>(ContactNotifier.new);
class ContactNotifier extends Notifier<ContactResultBase> {
@override
ContactResultBase build() {
return ContactResultLoading();
// 초기화 시 state는 로딩 상태
}
void postContactList() async {
ContactResult resp = await ref
.read(restClientProvider)
.postContactList(Crypto.AES_encrypt(Crypto.URLkey()), '');
state = resp;
// ConsumerStatefullWidget에서 초기화시 호출하여 state를 ContactResult 로 만듬
}
}
class ContactResultProviderPage extends ConsumerStatefulWidget {
const ContactResultProviderPage({super.key});
@override
ConsumerState<ContactResultProviderPage> createState() =>
_ContactResultProviderPageState();
}
class _ContactResultProviderPageState extends ConsumerState<ContactResultProviderPage> {
@override
void initState() {
super.initState();
ref.read(contactProvider.notifier).postContactList();
}
@override
Widget build(BuildContext context) {
final data = ref.watch(contactProvider);
if (data is ContactResultLoading) {
return Center(
child: CircularProgressIndicator(),
);
}
final ids = data as ContactResult;
if (ids.status.contains("success")) {
final addrinfo = ids.addrinfo as List<ContactItem>;
return _ListViewSubPage(posts: addrinfo);
} else {
return _ErrorSubPage(
status: ids.status,
message: ids.message,
);
}
}
}
답변 감사드립니다.
알려주신 코드로 변경하여 시도했더니 아래와 같은 에러메시지가 나옵니다.
여기서 Casting 에러가 발생합니다.
The following TypeError was thrown building ContactResultProviderPage(dirty, dependencies: [UncontrolledProviderScope], state: ConsumerState#4877c):
type 'Future<dynamic>' is not a subtype of type 'ContactResult' in type cast