Riverpod은 Flutter의 상태관리 패키지 중 하나로, 다른 유명한 상태관리 패키지는 Bloc, GetX, Provider 등이 있습니다. 이 중에서 Provider를 만든 개발자가 Provider를 보완하기 위해서 만든 패키지가 Riverpod입니다.
따라서 앞으로는 Provider의 상위 버전인 Riverpod에 기능이 더 추가되고 버전업이 될 것 같습니다. 저도 이번에 진행하는 사이드 프로젝트에 Riverpod 적용해 볼 예정이라 Riverpod에 대해 정리해보려고 합니다.
목차
3. ref 객체의 watch, read, listen 메서드
Riverpod 이란?
Riverpod (Provider의 철자를 바꾼 말)는 Flutter/Dart를 위한 반응형 캐싱 프레임워크(Reactive caching framework)입니다.
Riverpod는 선언적 프로그래밍과 반응형 프로그래밍을 사용하여 애플리케이션 로직의 상당 부분을 처리합니다. 내장된 오류 처리 및 캐싱을 통해 네트워크 요청을 수행하는 동시에 필요할 때 자동으로 데이터를 다시 가져올 수 있습니다.
Riverpod는 Flutter 위젯에서 영감을 받아 비즈니스 로직을 작성하는 새롭고 독특한 방법을 제공함으로써 이러한 문제를 해결하려고 합니다. 여러 면에서 Riverpod는 위젯과 비슷하지만 상태에 대해서는 비슷합니다.
Provider 종류
Provider
final valueProvider = Provider<int>((ref) {
return 0;
});
가장 간단한 기본 형태의 Provider입니다. Provider는 읽기만 가능하며 값을 변경할 수 없습니다.
Provider는 일반적으로 다음과 같은 상황에 사용됩니다.
- 캐싱 계산
- 다른 공급자(저장소, HttpClient)에 값 노출
- 테스트나 위젯이 값을 재정의 하는 방법을 제공
- select 함수를 사용하지 않고 rebuild 횟수 감소
StateProvider
final counterStateProvider = StateProvider<int>((ref) {
return 0;
});
StateProvider는 상태를 변경할 수 있는 Provider입니다. 내부 상태는 state로 접근이 가능한데, 사용하고자 하는 위젯에서 state 값을 직접 변경할 수 있습니다.
StateProvider는 주로 UserInterface에 의해 단순 변수를 수정할 수 있도록 존재합니다. StateProvider의 상태는 일반적으로 다음 중 하나입니다:
- 필터 타입과 같은 enum
- 문자열(일반적으로 텍스트 필드의 raw content)
- bool(체크박스용)
- 숫자
StateNotifierProvider
class Counter extends StateNotifier<int> {
Counter() : super(0);
void increment() => state++;
void decrement() => state--;
}
final counterStateNotifierProvider = StateNotifierProvider<Counter, int>((ref) {
return Counter();
});
StateNotifierProvider는 상태 뿐만 아니라 일부 로직을 함께 저장할 때 사용됩니다. 예를 들어 다른 Provider와 결합을 하거나, 내부에서 사용할 로직을 정의할 수 있습니다.
이외에도 NotifierProvider, FutureProvider, StreamProvider, ChangeNotifierProvider가 있지만 이 Provider들은 아직 스터디가 부족하여 다음 기회에 정리해서 추가하도록 하겠습니다.
ref 객체의 watch, read, listen 메서드
Provider를 읽기 위해선 먼저 ref 객체를 얻어야 합니다. 이 객체를 통해 위젯과 provider과 연결되어 특정 위젯에서 특정 Provider에 접근이 가능하게 됩니다.
ref.watch
- 반응형으로 Provider의 값이 변경 되면 GetX의 Rx 자료형과 같이 자체적으로 다시 build 됩니다.
- 다른 Provider와 결합할 때 유용하게 쓰입니다.
※watch 메서드는 Button의 콜백 메서드와 같이 비동기적으로 호출되는 함수나, initState 및 기타 State 생명주기 함수 내에서도 사용해서는 안 됩니다. 이러한 경우에는 개발팀에서는 ref.read 사용을 권고하고 있습니다.
ref.listen
- Provider의 값이 변경되면 선언한 콜백 메서드를 실행합니다.
- 오류가 발생할 때 스낵바를 표시하는 등 특정 변경이 발생할 때 이벤트를 처리하는데 유용할 수 있습니다.
- ref.listen 함수에는 Provider와 콜백 함수를 인수로 들어갑니다.
- 콜백 함수에는 이전 상태 값과 새 상태 값 2개의 파라미터가 전달됩니다.
※listen 메서드 또한 Button의 콜백 메서드와 같이 비동기적으로 호출되는 함수나, initState 및 기타 State 생명주기 함수 내에서도 사용해서는 안 됩니다.
ref.read
- Provider의 값을 읽어오기만 합니다. 값이 변경되어도 별다른 동작을 하지 않습니다.
- 보통 값을 읽어오기보단 빌드없이 Provider의 함수를 참조하기 위해 사용됩니다.
개발팀에서는 ref.read나 ref.listen보다 ref.watch를 사용하여 기능을 구현하는 것을 권고하고 있습니다. ref.watch를 사용하면 애플리케이션이 반응적이고 선언적이 되어 유지관리가 쉬워진다 것이 그 이유입니다.
실제 사용 방법
StateNotifierProvider를 사용한 예시로 실제 사용 방법을 설명하겠습니다.
void main() {
runApp(
// Riverpod을 설치하려면 다른 모든 위젯 위에 이 위젯을 추가해야 합니다.
// 이 위젯은 "MyApp" 내부가 아니라 "runApp"에 직접 파라미터로 추가해야 합니다.
ProviderScope(
child: MyApp(),
),
);
}
우선 Provider를 사용할 위젯트리의 상단에 위치한 위젯을 ProviderScope 위젯으로 감싸야합니다.
class MyCounter extends StateNotifier<int> {
MyCounter() : super(0);
void increment() => state++;
void decrement() => state--;
void initCount() => state = 0;
}
final myCounterStateNotifierProvider =
StateNotifierProvider<MyCounter, int>((ref) {
return MyCounter();
});
StateNotifier를 상속하는 클래스를 만들어서 안에 사용할 자료와 데이터 처리 로직을 작성합니다.
그리고 StateNotifier를 상속한 클래스를 제네릭 타입으로 갖는 Provider를 전역변수로 선언합니다.
class PracticePage extends ConsumerWidget {
PracticePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final counterRead = ref.read(myCounterStateNotifierProvider.notifier);
final counterState = ref.watch(myCounterStateNotifierProvider);
ref.listen(
myCounterStateNotifierProvider,
((int num) {
print('바뀔때마다 동작');
print('ref.listen: $num');
}),
);
return Scaffold(
appBar: AppBar(
title: const Text('Riverpod Practice'),
),
body: Center(
child: Text(
'Value: $counterState',
style: const TextStyle(
fontSize: 48,
fontWeight: FontWeight.bold,
),
),
),
floatingActionButton: Align(
alignment: Alignment.bottomRight,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
heroTag: '111',
onPressed: () => counterRead.increment(),
child: const Icon(
Icons.add,
),
),
const SizedBox(width: 10.0),
FloatingActionButton(
heroTag: '222',
onPressed: () => counterRead.decrement(),
child: const Icon(
Icons.remove,
),
),
const SizedBox(width: 10.0),
FloatingActionButton(
heroTag: '333',
onPressed: () => counterRead.initCount(),
child: const Icon(
Icons.refresh,
),
),
],
),
),
);
}
}
GetX에서는 OBX를 통해 바뀌는 부분의 위젯을 감싸주었다면 Riverpod에서는 StatelessWidget, StatefulWidget, Sate를 각각 ConsumerWidget, ConsumerStatefulWidget, ConsumerState로 대체해주어야 합니다.
build 메서드에는 WidgetRef ref를 추가해 주면 됩니다.
그러고 나서는 ref 객체의 메서드를 이용하면 됩니다. 메서드를 호출할 때 파라미터로 provider.notifier를 전달하면 provider객체가 반환되고, provider를 전달하면 State 즉 값만 반환됩니다.
'개발 > FLUTTER' 카테고리의 다른 글
flutter unit test 하는 방법 (0) | 2024.01.30 |
---|---|
[Flutter] 행에서 위젯을 자동 줄바꿈 해주는 wrap 사용방법 (0) | 2024.01.30 |
FLUTTER 개념 정리- WIDGET, ELEMENT, RENER Tree, Context (0) | 2024.01.27 |
Flutter 개발을 위한 Android studio 단축키 정리 (0) | 2024.01.27 |
Flutter WidgetsFlutterBinding.ensureInitialized(); 사용이유 (0) | 2024.01.26 |