리버팟이란?
Provider는 상태를 관리하는 기본적인 단위입니다. 상태를 읽거나 쓸 수 있는 객체를 생성하며, 앱에서 상태를 제공하고 구독할 수 있게 합니다.
Provider는 상태의 소스를 정의하며, 위젯 트리에서 해당 상태를 구독할 수 있게 해줍니다.
StateProvider: 간단한 상태를 제공하는 Provider입니다. 예를 들어, 카운터나 플래그와 같은 값을 다룰 때 유용합니다.
StateNotifierProvider: 더 복잡한 상태 로직을 처리하는 Provider입니다. 상태를 변경하는 로직을 별도의 클래스로 분리하여 더 세밀하게 제어할 수 있습니다.
FutureProvider: 비동기적인 작업을 처리하는 Provider입니다. 예를 들어, API 요청 후 데이터를 가져올 때 사용됩니다.
StreamProvider: 실시간 데이터 흐름을 다룰 때 사용되는 Provider입니다. 예를 들어, 채팅 메시지나 실시간 알림을 처리할 때 유용합니다.
ProviderScope는 Riverpod의 상태를 관리하는 범위를 정의하는 위젯입니다. 앱 전체에서 상태를 관리할 수 있게 해주는 상위 위젯입니다.
ProviderScope는 앱의 루트 위젯에 감싸서 사용해야 하며, 이를 통해 Riverpod에서 관리하는 상태를 사용할 수 있습니다.
모든 Provider는 ProviderScope 내에서만 작동합니다.
Riverpod에서 상태를 구독하려면 ConsumerWidget을 사용하거나, watch를 통해 상태를 구독하고 변화를 감지할 수 있습니다.
상태가 변경되면 ConsumerWidget은 자동으로 리빌드되며, ScopedReader를 사용해 Provider로부터 값을 읽습니다.
read: 상태를 읽지만, 상태 변화에 따른 리빌드를 하지 않습니다.
watch: 상태를 읽고, 상태가 변경되면 해당 위젯을 리빌드합니다.
RiverPod을 사용하려면 먼저 flutter_riverpod 패키지를 프로젝트에 추가해야 합니다.
flutter pub add flutter_riverpod
ProviderScope로 최상위 위젯 감싸기ProviderScope는 RiverPod에서 상태를 관리하는 상위 컨테이너 역할을 합니다. 앱의 루트 위젯을 ProviderScope로 감싸야 RiverPod에서 제공하는 상태 관리 기능을 사용할 수 있습니다.
void main() {
runApp(const ProviderScope(child: MyApp())); // 앱을 ProviderScope로 감싸기
}
뷰에서는 데이터를 표시하는 역할을 하며, Consumer 위젯을 사용하여 RiverPod의 상태를 구독하고 반영합니다. 이때 ref.watch와 ref.read를 사용하여 상태를 읽거나 업데이트할 수 있습니다.
Model 클래스 만들기API로부터 받아온 데이터를 관리할 Model 클래스를 만듭니다. 예를 들어, 유저 정보를 담을 User 클래스를 정의할 수 있습니다.
class User {
String name;
int age;
User({required this.name, required this.age});
User.fromJson(Map<String, dynamic> json)
: name = json['name'],
age = json['age'];
Map<String, dynamic> toJson() {
return {
"name": name,
"age": age,
};
}
}
Repository 클래스 만들기서버에서 데이터를 가져오는 역할을 하는 Repository 클래스를 만듭니다. 이 클래스는 비동기적으로 데이터를 받아오는 메서드를 구현합니다.
class UserRepository {
Future<User> getUser() async {
await Future.delayed(const Duration(seconds: 1)); // 서버 대기 시간
String serverResponse = """
{
"name": "이지원",
"age": 20
}
""";
Map<String, dynamic> json = jsonDecode(serverResponse);
return User.fromJson(json);
}
}
Widget에서 관리될 상태 클래스 만들기뷰에서 상태를 관리할 클래스를 작성합니다. 여기서는 HomeState를 사용하여 유저 정보와 데이터를 가져온 시간을 관리합니다.
class HomeState {
User? user;
DateTime? fetchTime;
HomeState({required this.user, required this.fetchTime});
}
ViewModel 만들기 (Notifier 상속)상태를 관리하고 변경된 상태를 뷰에 알려주는 ViewModel을 만듭니다. Notifier 클래스를 상속받고, 초기 상태를 설정하는 build 메서드와 데이터를 가져와서 상태를 업데이트하는 메서드를 작성합니다.
class HomeViewModel extends Notifier<HomeState> {
HomeState build() {
return HomeState(user: null, fetchTime: null); // 초기 상태 설정
}
void getUserInfo() async {
UserRepository userRepository = UserRepository();
User user = await userRepository.getUser();
state = HomeState(user: user, fetchTime: DateTime.now()); // 상태 업데이트
}
}
viewModelProvider 만들기NotifierProvider를 사용하여 ViewModel을 공급하는 프로바이더를 생성합니다. 이를 통해 뷰에서 ViewModel을 구독하고 데이터를 관리할 수 있습니다.
final homeViewModelProvider = NotifierProvider<HomeViewModel, HomeState>((ref) {
return HomeViewModel();
});
Widget에서 Consumer 위젯 사용하여 데이터 표시 및 함수 연결Consumer 위젯을 사용하여 ViewModel에 접근하고 상태를 구독합니다. ref.watch를 사용하여 상태가 변경될 때마다 위젯이 리빌드되도록 할 수 있습니다. ref.read를 사용하면 상태를 읽을 수 있지만, 상태가 변경될 때 위젯을 리빌드하지 않습니다.
Consumer(
builder: (context, ref, child) {
HomeState homeState = ref.watch(homeViewModelProvider);
return Column(
children: [
Text('이름: ${homeState.user?.name ?? ""}'),
Text('나이: ${homeState.user?.age ?? ""}'),
Text('데이터 가져온 시간: ${homeState.fetchTime ?? ""}'),
GestureDetector(
onTap: () {
ref.read(homeViewModelProvider.notifier).getUserInfo(); // 상태 업데이트
},
child: Text('정보 가져오기'),
),
],
);
}
)
Notifier 종류NotifierNotifier는 상태가 앱 전역에서 계속 유지될 때 사용합니다. 예를 들어, 로그인 상태나 앱 전체에서 공유되는 상태를 관리할 때 적합합니다.
class HomeViewModel extends Notifier<HomeState> {
HomeState build() {
return HomeState(counter: 0, updateTime: null);
}
}
final homeViewModelProvider = NotifierProvider<HomeViewModel, HomeState>((ref) {
return HomeViewModel();
});
AutoDisposeNotifierAutoDisposeNotifier는 참조가 끝날 때, 즉 위젯이 화면에서 사라지면 자동으로 상태가 제거되는 AutoDisposeNotifier는 화면 간 이동 시 상태를 자동으로 정리하는 데 사용됩니다.
class HomeViewModel extends AutoDisposeNotifier<HomeState> {
HomeState build() {
return HomeState(counter: 0, updateTime: null);
}
}
final homeViewModelProvider = NotifierProvider.autoDispose<HomeViewModel, HomeState>((ref) {
return HomeViewModel();
});
AutoDisposeFamilyNotifierAutoDisposeFamilyNotifier는 AutoDisposeNotifier와 비슷하지만, 위젯에서 값을 넘겨받아 특정 작업을 처리할 때 사용됩니다. 예를 들어, 블로그 포스트 상세 페이지에서 PostId를 넘겨서 해당 포스트의 정보를 받아올 때 사용됩니다.
class HomeViewModel extends AutoDisposeFamilyNotifier<HomeState, int> {
HomeState build(int postId) {
return HomeState(user: null, updateTime: null);
}
void fetchUserInfo(int userId) {
// 사용자 정보 가져오기
}
}
final homeViewModelProvider = NotifierProvider.autoDispose.family<HomeViewModel, HomeState, int>((ref) {
return HomeViewModel();
});
// Widget에서 값 넘겨주기
Consumer(
builder: (context, ref, child) {
HomeState homeState = ref.watch(homeViewModelProvider(1)); // 1은 postId
return Text('');
},
);
PostId를 넘겨서 해당 정보를 불러올 때 사용합니다. 화면을 이동하면서 AutoDisposeFamilyNotifier를 사용하면 기존 상태를 지우고 새로 데이터를 받아올 수 있습니다.RiverPod는 상태 관리뿐만 아니라 API 통신 및 비즈니스 로직을 효율적으로 처리할 수 있는 강력한 라이브러리입니다. Notifier를 통해 상태 관리와 데이터를 쉽게 처리하고, AutoDispose를 활용하여 화면 전환 시 상태를 자동으로 정리할 수 있습니다. AutoDisposeFamilyNotifier는 매개변수를 넘겨받아 유연한 상태 관리를 가능하게 해줍니다. Consumer 위젯을 사용하여 상태 변화를 구독하고, 앱을 효율적으로 관리할 수 있습니다.