Riverpod
Riverpod 은 상태 관리를 도와주는 패키지(라이브러리) 이며, Provider 패키지를 만든 곳에서 Provider 의 한계를 개선해서 만든 패키지 입니다.
상태 관리
그런데 여기서 상태 관리 라는건 무엇일까요?
상태관리는 애플리케이션에서 데이터나 UI 의 상태(state)를 효과적으로 관리하고 유지하는 방법을 의미합니다. Flutter와 같은 프레임워크에서는 사용자의 상호작용이나 데이터 변경에 따라 화면이 갱신되어야 하기 때문에 상태관리가 매우 중요한 역할을 합니다.
요약하자면, 화면상의 정보에 영향을 미치는 변수를 업데이트, 삭제하는 등의 화면에 표현하는 것을 상태 관리라고 합니다.
1. State 상태
: 애플리케이션에서 상태는 다음을 포함다는 데이터를 의미 합니다.
1. UI 상태 - 텍스트 변경, 버튼 활성화 여부, 스크롤 스위치, 현재 페이지 등...
2. 데이터 - 사용자 이름, 로그인 상태, API 에서 받아온 데이터 등...
2.상태의 종류
- 로컬 상태
: 특정 위젯에서만 필요한 상태 - 입력 필드의 값, 체크박스 상태, 변수가 포함된 텍스트 위젯의 텍스트 등...
아래와 같이 setState 로 간단히 관리가 가능합니다.
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
bool isChecked = false;
@override
Widget build(BuildContext context) {
return Checkbox(
value: isChecked,
onChanged: (value) {
setState(() {
isChecked = value!;
});
},
);
}
}
- 글로벌 상태
: 애플리케이션의 여러 위젯 간에 공유해야하는 상태 - 로그인 정보, 장바구니 데이터 등...
setState 로 관리하기에 비효율적이며, 별도의 상태 관리 패키지를 사용하는 것이 훨씬 적합합니다.
Notifier
Notifier 는 Riverpod 에서 상태를 관리하는 클래스입니다. 이 클래스를 상속받아 상태를 정의하고, 상태를 업데이트하는 함수를 구현할 수 있습니다. Flutter 에서는 상태 관리를 하기위해서 Provider 와 함께 사용되어야 합니다.
import 'package:flutter_riverpod/flutter_riverpod.dart';
class CounterNotifier extends Notifier<int> {
@override
int build() {
return 0;
}
void increment() {
state++;
}
void decrement() {
state--;
}
}
final counterProvider = NotifierProvider<CounterNotifier, int>(
CounterNotifier.new,
);
...
final counter = ref.watch(counterProvider);
ref.read(counterProvider.notifier).increment();
...
AutoDisposeNotifier
Notifier 에 추가적으로, 더 이상 필요하지 않은 상태를 자동으로 정리해줍니다.
- 위젯이 더이상 사용되지 않을 때 상태를 정리합니다. 즉, 특정 화면에서만 필요한 상태를 관리할 때 사용합니다.
Provider
Provider는 데이터를 생성하거나 상태를 관리하고, 이를 Flutter 위젯 트리에서 효율적으로 공유하기 위한 기본 구성 요소입니다.
Provider
- 읽기 전용 상태를 제공하는 데 사용됩니다.
final helloWorldProvider = Provider<String>((ref) {
return 'Hello, World!';
});
// UI에서 사용
final message = ref.watch(helloWorldProvider);
Text(message); // "Hello, World!"
StateProvider
- 간단한 상태 변경이 필요한 경우에 사용됩니다. 내부적으로 상태를 저장하고 업데이트도 할 수 있습니다.
final counterProvider = StateProvider<int>((ref) => 0);
// UI에서 사용
final counter = ref.watch(counterProvider); // 상태 읽기
ref.read(counterProvider.notifier).state++; // 상태 업데이트
FutureProvider
- 비동기 데이터를 관리합니다. 예들 들어서 API호출 결과를 관리할 때 적합합니다.
final dataProvider = FutureProvider<String>((ref) async {
await Future.delayed(Duration(seconds: 2));
return 'Data Loaded';
});
// UI에서 사용
ref.watch(dataProvider).when(
data: (data) => Text(data),
loading: () => CircularProgressIndicator(),
error: (err, stack) => Text('Error: $err'),
);
StreamProvider
- 스트림 데이터를 관리합니다. 실시간 데이터를 처리하는데 적합합니다.
final streamProvider = StreamProvider<int>((ref) async* {
for (int i = 0; i < 5; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
});
// UI에서 사용
ref.watch(streamProvider).when(
data: (data) => Text('$data'),
loading: () => CircularProgressIndicator(),
error: (err, stack) => Text('Error: $err'),
);
Provider 와 함께 사용하는 Notifier
NotifierProvider
- Notifier 클래스를 기반으로 상태를 관리할 때 사용됩니다. Notifier 부분에 언급했듯이 복잡한 상태 관리 로직을 구현할 수 있습니다.
class CounterNotifier extends Notifier<int> {
@override
int build() => 0;
void increment() {
state++;
}
}
final counterNotifierProvider =
NotifierProvider<CounterNotifier, int>(CounterNotifier.new);
// UI에서 사용
final counter = ref.watch(counterNotifierProvider);
ref.read(counterNotifierProvider.notifier).increment();
AsyncNotifierProvider
- AsyncNotifier 와 연결하여 비동기 상태를 관리하기에 적합한 방식입니다. 로딩, 에러, 성공 상태를 구분해서 관리할 수 있습니다.
class DataFetcherNotifier extends AsyncNotifier<String> {
@override
Future<String> build() async {
await Future.delayed(Duration(seconds: 2));
return "Fetched Data";
}
}
final dataFetcherProvider =
AsyncNotifierProvider<DataFetcherNotifier, String>(() => DataFetcherNotifier());
class DataWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final asyncValue = ref.watch(dataFetcherProvider);
return asyncValue.when(
data: (value) => Text('Data: $value'),
loading: () => CircularProgressIndicator(),
error: (e, stack) => Text('Error: $e'),
);
}
}
AutoDisposeNotifierProvider
- 더이상 필요하지 않는 상태를 자동으로 정리해줍니다. 메모리 누수를 방지하거나, 화면을 벗어날 때 상태를 정리할 때 사용됩니다.
class TempCounterNotifier extends Notifier<int> {
@override
int build() => 0;
void increment() => state++;
}
final tempCounterProvider = AutoDisposeNotifierProvider<TempCounterNotifier, int>(
() => TempCounterNotifier(),
);
class TempCounterWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(tempCounterProvider);
final notifier = ref.read(tempCounterProvider.notifier);
return Column(
children: [
Text('Temporary Count: $count'),
ElevatedButton(
onPressed: notifier.increment,
child: Text('Increment'),
),
],
);
}
}
AutoDisposeAsyncNotifierProvider
- AsyncNotifier와 연결된 비동기 상태를 화면 종료 시 자동으로 해제해줍니다.
class TempDataFetcher extends AsyncNotifier<String> {
@override
Future<String> build() async {
await Future.delayed(Duration(seconds: 2));
return "Temporary Fetched Data";
}
}
final tempDataProvider =
AutoDisposeAsyncNotifierProvider<TempDataFetcher, String>(() => TempDataFetcher());
class TempDataWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final asyncValue = ref.watch(tempDataProvider);
return asyncValue.when(
data: (value) => Text('Data: $value'),
loading: () => CircularProgressIndicator(),
error: (e, stack) => Text('Error: $e'),
);
}
}
'Dev > Flutter' 카테고리의 다른 글
[Flutter] Json 데이터를 객체로 변환하는 방법 (0) | 2024.12.03 |
---|---|
함수형 위젯 vs 클래스형 위젯, 어떻게 하는게 좋을까? (2) | 2024.12.02 |
[Flutter] 버튼 Radius 주는 방법은 Style 로!!! (0) | 2024.11.18 |
Flutter iOS 이슈 (0) | 2024.02.04 |
Flutter DropdownButton 글자 중앙정렬 (0) | 2023.12.16 |