Riverpod
는 여러 종류의 패키지가 있습니다.
각 패키지마다 사용 목적이 다르며, 어떤 패키지를 설치 할 것인지는 만드려는 앱에 따라 다릅니다.
provider 의 값을 얻어서 변화를 모니터링 할 때 사용합니다.
값이 변경되면, widget 을 re_build 하거나 값을 구독하고 있는 위치에 상태 값을 전달합니다.
상태 변화를 화면에 즉각 반영해야 할 때 사용합니다.
final counterProvider = StateProvider((ref) => 0);
class HomeView extends ConsumerWidget {
const HomeView({Key? key}) : super(key: key);
Widget build(BuildContext context, WidgetRef ref) {
// ref를 사용하여 provider 구독
final counter = ref.watch(counterProvider);
return Text('$counter');
}
}
watch 와 동일하게 변화를 모니터링 하지만, widget 을 re_build 하거나 다른 곳에 상태값을 전달하지는 않습니다.
상태 변경이 있을 때, 커스텀한 어떠한 행위를 취해야 할 경우 사용합니다.
아래와 같이 상태 변화 시, SnackBar 등을 호출 할 때, 사용할 수 있습니다.
final counterProvider = StateNotifierProvider<Counter, int>((ref) => Counter(ref));
class HomeView extends ConsumerWidget {
const HomeView({Key? key}): super(key: key);
Widget build(BuildContext context, WidgetRef ref) {
ref.listen<int>(counterProvider, (int? previousCount, int newCount) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Value is ${current.state}')),
);
});
return Scaffold(...);
}
}
어떤 부가적인 효과 없이 provider 의 상태 값을 가지고 올 때 사용합니다.
read 는 일반적으로 사용자 상호작용으로 발생가능한 트리거 함수 내부에서 주로 사용합니다.
주로, provider 내부의 callback
함수에 접근할 때 사용합니다.
final counterProvider =
StateNotifierProvider<Counter, int>((ref) => Counter(ref));
class HomeView extends ConsumerWidget {
const HomeView({Key? key}) : super(key: key);
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
// counterProvider의 increment() 메소드를 호출합니다.
ref.read(counterProvider.notifier).increment();
},
),
);
}
}
https://pub.dev/packages/hooks_riverpod
hooks_riverpod
flutter pub add hooks_riverpod
g.dart를 생성하기 위한 패키지
flutter pub add riverpod_generator
generator를 사용하기 위한 패키지
flutter pub add build_runner
annotation 패키지
flutter pub add riverpod_annotation
적용
flutter pub get
generator commaned
dart run build_runner build --delete-conflicting-outputs
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class HooksRiverpodEx extends HookConsumerWidget {
const HooksRiverpodEx({super.key});
Widget build(BuildContext context, WidgetRef ref) {
return Container();
}
}
void main() {
runApp(const ProviderScope(child: MyApp()));
}
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'counter_provider.g.dart';
class Counter extends _$Counter {
// state로 관리
int fetchItem(int value) {
// 초기화 당시 실행될 로직
return state = value;
}
int build() {
return fetchItem(0);
}
// state값 0으로 초기화
void reset() {
state = 0;
}
// state값 1 증가
void increase() {
state++;
}
}
변수를 생성하여 사용하는 것이 아닌, provider를 통해 상태를 생성하고,
이 상태를 앱 전반에 걸쳐 읽거나 변경할 수 있습니다.
class HooksRiverpodEx extends HookConsumerWidget {
const HooksRiverpodEx({super.key});
Widget build(BuildContext context, WidgetRef ref) {
var viewModel = ref.watch(counterProvider);
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(viewModel.toString()),
ElevatedButton(
onPressed: () {
ref.read(counterProvider.notifier).reset();
},
child: const Text('초기화')),
],
),
)),
floatingActionButton: FloatingActionButton(
onPressed: () {
ref.read(counterProvider.notifier).increase();
},
child: const Icon(Icons.add),
),
);
}
}
비동기인 경우, 아래와 같이 데이터 결과, 로딩, 에러를 한번에 보여주는 위젯을 제공합니다.
viewModel.when(
data: (data) {
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
var item = data[index];
return ListTile(
dense: true,
title: Text('item1'),
shape: Border(bottom: BorderSide(width: 1)),
);
},
);
},
error: (error, stack) => Center(
child: CustomErrorWidget(
error: error,
),
),
loading: () => const LoadingIndicator(),
)