Flutter 상태관리에 사용되는 라이브러리인 Riverpod은 provider 종류가 되게 많다. 3일만 지나도 잊어버리는 나를 위해 provider의 용도와 사용방법을 정리해보려고 한다.
provider는 일반적인 값이나 객체를 제공하는 데 사용된다. 주로 의존성 주입(Dependency Injection) 용도로 활용된다.
예를 들어, 데이터베이스 인스턴스, API 클라이언트 등과 같은 싱글톤으로 인스턴스를 제공하는 데 사용된다.
상태를 변경하지 않고 값을 제공하는 데 주로 사용된다.
stateProvider는 상태를 관리하고 업데이트하기 위한 Provider이다. 관리하고 있는 상태가 변경되면 위젯들은 변경을 감지하고 자동으로 다시 그려지게 된다. 그리고 다른 state에 의존하는 다른 provider에도 영향을 준다.
예를 들어, 앱의 로딩 상태, 사용자 인증 상태 등과 같은 변경 가능한 상태를 관리하는 데 사용된다.
위의 두가지 프로바이더를 사용한 코드를 보자.
어떤 상태값을 watch하고 있다가 그 값을 이용하여 어떠한 처리 (계산 등)을 한 값을 저장하는 예시이다.
클릭하면 1씩 증가하는 값의 상태를 저장하는 countStateProvider. 이것은 StateProvider이다.
countStateProvider를 watch하고있는 doubleCountProvider. 이것은 count의 값에 2배를 증가시키는 로직을 갖고있는 Provider이다.
UI처리. MyHomePage 위젯에서 count값과 doubleCount값을 가져와 보여준다. 버튼을 클릭하면 값이 증가하도록 countStateProvider의 state를 증가시켜준다.
전체코드
/// 1
final countStateProvider = StateProvider<int>((ref) => 0); // int 0을 state에 저장
/// 2
final doubleCountProvider = Provider<int>((ref) {
// 다른 상태를 watch하여 해당 상태의 값의 두 배를 반환한다.
final count = ref.watch(countStateProvider);
return count * 2;
});
/// 3
class MyHomePage extends ConsumerWidget {
const MyHomePage({super.key});
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(countStateProvider.notifier).state;
final doubleCount = ref.watch(doubleCountProvider); // count 상태를 watch
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: $count'),
Text('Double Count: $doubleCount'), // count 상태의 값의 두 배가 표시됨
ElevatedButton(
onPressed: () {
ref.read(countStateProvider.notifier).state++; // 카운트를 증가
},
child: Text('Increment Count'),
),
],
),
),
);
}
}
결과

StateNotifierProvider는 상태를 관리 할 때 클래스로 관리한다. 이 클래스는 StateNotifier를 extends한다. StateNotifier는 상태를 변경할 수 있는 메서드를 제공하고, 변경 사항을 감지하고 알릴 수 있다.
StateProvider와 StateNotifierProvider의 차이점을 보면서 위의 설명이 무슨 말인지 이해해보자.
클릭하면 1씩 증가하는 값의 상태를 저장하는 countStateProvider. 이것은 StateProvider이다.
클릭하면 1씩 증가하는 값의 상태를 저장하는doubleCountProvider. 이번에는 StateNotifierProvider로 만들었다. Counter 클래스를 만들어서 상태값을 메서드로 increment 시킨다.
UI처리. MyHomePage 위젯에서 countStateProviderValue countStateNotifierProviderValue 가져와 보여준다.
StateProvider로 카운트 증가
ref.read(countStateProvider.notifier).state++;
StateNotifierProvider로 카운트 증가
ref.read(countStateNotifierProvider.notifier).increment();
전체코드
/// 1
final countStateProvider = StateProvider<int>((ref) => 0); // StateProvider를 사용하여 카운트 값을 관리
/// 2
class Counter extends StateNotifier<int> { // StateNotifier를 extends한 클래스를 사용하여 상태를 관리
Counter() : super(0);
void increment() { // 카운트를 증가시키는 메서드
state++;
}
}
final countStateNotifierProvider = StateNotifierProvider<Counter, int>((ref) => Counter());
/// 3
// StateNotifierProvider를 사용하여 Counter 클래스의 인스턴스를 제공
// 참고 : StateNotifierProvider 옆의 타입은 <State를 관리하는 클래스 , state의 타입> 으로 적어주면 된다.
class MyHomePage extends ConsumerWidget {
const MyHomePage({super.key});
Widget build(BuildContext context, WidgetRef ref) {
final countStateProviderValue = ref.watch(countStateProvider);
final countStateNotifierProviderValue =
ref.watch(countStateNotifierProvider);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('StateProvider vs StateNotifierProvider 예시'),
),
body: Center(
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
// StateProvider로부터 카운트 state를 보여줌
Text('StateProvider 카운트: $countStateProviderValue'),
const SizedBox(height: 20),
// StateNotifierProvider로부터 카운트 state를 보여줌
Text('StateNotifierProvider 카운트: $countStateNotifierProviderValue'),
]),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
// StateProvider로 카운트를 증가시키는 버튼
FloatingActionButton(
onPressed: () {
ref.read(countStateProvider.notifier).state++;
},
child: Icon(Icons.add),
),
const SizedBox(height: 10),
// StateNotifierProvider로 카운트를 증가시키는 버튼
FloatingActionButton(
onPressed: () {
ref.read(countStateNotifierProvider.notifier).increment();
},
child: const Icon(Icons.add),
),
],
),
),
);
}
}
결과

그만 알아보자.
다음시간에는 AsyncNotifierProvider, FutureProvider 등 좀 더 복잡한 Provider를 정리해보겠다..