## 해당 TIL은 주어진 과제를 수행하면서 얻은 학습 내용과, 시행착오 등등을 종합해서 작성한것임
GitHub - Oz-player/oz_player
Contribute to Oz-player/oz_player development by creating an account on GitHub.
github.com
MUOZ 프로젝트를 진행하면서, 새롭게 생각해야 될게 많았고, 너무 정신이 없던 나머지 TIL 작성을 약간 미루었던거 같다..
TIL 작성할 만한 부분은 꽤 있었는데, 시간에 쫓겨서..
약 2~3주간 프로젝트를 진행하면서 있었던 TIL 부분들을 한번에 우다다다 쏟아내 보고자 한다.
1. Riverpod 의 상태관리
이번에 프로젝트를 진행하면서, 다양한 Notifier를 사용할 수 있는 기회를 얻게되어, 꽤나 다양한 Notifier를 사용해 볼 수 있었는데, 각 Notifier의 특징과 차이점에 따라 정리해보고자 한다.
우선 Riverpod에서 상태관리를 사용하기 위해서는 다음과 같이 코드를 작성할 수 있다.
MVVM 패턴이 적용된 코드이다.
- Notifier
Riverpod 에서 가장 기본적으로 사용되는 Notifier 방식이다.
일반적인 상태관리 경우라면 기본 Notifier를 사용하는게 가장 좋다.
// 변수 하나를 상태로써 사용하는 경우
class MainPageViewModel extends Notifier<int> {
@override
int build(){
return 0;
}
// 상태변경(UI화면 갱신하며)
void reflash(){
state = state;
}
}
final mainPageViewModelProvider = NotifierProvider<MainPageViewModel,int> ((ref){
return MainPageViewModel();
});
// 클래스로 상태를 사용하는경우
class SubPageState {
String text;
int index;
SubPageState(this.text, this.index);
}
class SubPageViewModel extends Notifier<SubPageState> {
@override
SubPageState build(){
return SubPageState('', 0);
}
// 상태변경(UI화면 갱신하지 않으며)
void replace(){
state.text = 'a';
state.index = 1;
}
// 상태변경(UI화면 갱신하며)
void reflash(){
state = state;
}
}
final subPageViewModelProvider = NotifierProvider<SubPageViewModel,SubPageState> ((){
return SubPageViewModel();
});
- StateNotifier
변수 하나의 상태를 관리하는데에 사용되는 Notifier이다.
앱 전체에서 공통적으로 사용되는 변수에 주로 사용된다.. 만,
2.x 버전 Riverpod 에서는 그냥 Notifer를 사용하는게 더 나을 수도 있다고 한다.
프로젝트에서는 로그인후 받은 UID를 저장하는데 사용하기 위해 StateNotifier를 사용했었다.
class MainPageViewModel extends StateNotifier<int> {
MainPageVideModel() : super(0);
void increment() {
state = state + 1;
}
void decrement() {
state = state - 1;
}
}
final mainPageProvider = StateNotifierProvider<MainPageViewModel, int>((ref) {
return MainPageViewModel();
});
- AutoDisposeNotifier
사용이 완료되면 자동적으로 Dispose 되는 Notifier이다.
사용이 완료되었다는 뜻은, 해당 State를 사용하는 UI 화면이 없을 경우(Provider가 사용되지 않을 경우) 를 뜻한다.
어느 특정 페이지에서만 사용되는 일시적인 상태값을 다룰때 사용하는게 일반적이며,
반드시 Dispose 되어야 하는 상태가 있는 경우 사용할 때도 있다.
주의할점은, Dispose가 자동적으로 일어나기 때문에, 사용시 dispose 시점을 명확히 생각하며 설계해야 한다는 것이다.
만일 특정한 상황에서 상태값을 유지해야 하는 경우엔, 'ref.keepAlive()' 를 사용하여, provider가 삭제되지 않도록 관리해 상태값을 유지할 수도 있다.
프로젝트에서는 추천페이지를 구상할때 사용되었다.
class MainPageViewModel extends AutoDisposeNotifier<int> {
@override
int build(){
return 0;
}
}
final mainPageProvider = AutoDisposeNotifierProvider<MainPageViewModel, int>((ref) {
return MainPageViewModel();
});
/* provider 삭제를 임의로 막고자 한다면
final mainPageProvider = AutoDisposeNotifierProvider<MainPageViewModel, int>((ref) {
ref.keepAlive();
return MainPageViewModel();
});
*/
- AsyncNotifier
비동기 상태값을 관리하는데 최적화된 Notifier이다.
AsyncNotifier의 가장 큰 특징은, 초기 상태를 비동기적으로 설정할 수 있다는 것과, 상태를 loading 상태와 error 상태로 지정할 수 있다는 것이다.
프로젝트에서는 파이어베이스에 저장된 유저 각각의 플레이리스트 같은 것들을 읽어올때 주로 사용되었다.
class MainPageVidewModel extends AsyncNotifier<int> {
@override
FutureOr<int> build() {
return 0;
}
// 상태를 로딩상태로 설정
// 로딩상태는 상태에 새로운 값이 할당될 경우 자동적으로 풀린다.
Future<void> loading(){
state = const AsyncValue.loading();
try {
final result = await fetchFromApi();
state = AsyncValue.data(result); // 성공하면 데이터 설정 (로딩 해제)
} catch (e, stack) {
state = AsyncValue.error(e, stackTrace: stack); // 에러 발생 시 명시적으로 에러 상태 설정
}
}
// 새로운 값 할당
Future<void> newState() async {
state = AsyncValue.data(1);
}
/* 만약 class 형태의 상태를 다루고 있을때, 일부 값만 변경하도록 하려면?
이런식으로 사용하면 된다.
void changeFocusIndex(int index){
state = state.whenData((value)=>value.copyWith(focusIndex: index));
}
*/
}
final mainPageProvider = AsyncNotifierProvider<MainPageVidewModel, int>((ref) {
return MainPageViewModel();
});
- StreamNotifier
비동기 상태값중 스트림을 관리하는데 최적화된 Notifier이다.
실시간으로 변하는 데이터를 처리하거나 비동기 스트림을 UI와 연동할 때 사용됩니다.
프로젝트에서는 사용되지는 않았지만, 굳이 사용하자면 랭킹페이지에 사용되었을거 같습니다.
// A라는 스트림을 사용했을때의 예시
class MainPageViewModel extends StreamNotifier<int> {
late StreamSubscription<int> _subscription;
@override
Stream<int> build() async* {
final streamA = getStreamA();
_subscription = streamA.listen(
(value) {
state = AsyncValue.data(value);
},
onError: (error, stackTrace) {
state = AsyncValue.error(error, stackTrace: stackTrace);
},
onDone: () {
state = const AsyncValue.data(0); // 종료 시 값
},
);
yield* streamA;
}
Stream<int> getStreamA() async* {
await Future.delayed(Duration(seconds: 1));
yield 1;
await Future.delayed(Duration(seconds: 1));
yield 2;
await Future.delayed(Duration(seconds: 1));
yield 3;
}
// 사용이 끝났을 때 구독을 취소하는 메서드
@override
Future<void> dispose() async {
await _subscription.cancel();
super.dispose();
}
}
final mainPageProvider = StreamNotifierProvider<MainPageVidewModel, int>((ref) {
return MainPageViewModel();
});
'TIL' 카테고리의 다른 글
TIL - MUOZ (6) (IOS 앱 심사 리젝 사유 - 1) (0) | 2025.02.12 |
---|---|
TIL - MUOZ (5) (Sentry) (0) | 2025.02.11 |
TIL - MUOZ (3) (Gemini 음악추천) (0) | 2025.02.08 |
TIL - MUOZ (2) (오디오 플레이어 UI) (0) | 2025.02.08 |
TIL - MUOZ (1) (오디오 플레이어 - Just Audio) (0) | 2025.02.07 |