| Provider ํ์ | ์ํ ๋ณ๊ฒฝ ๊ฐ๋ฅ? | ์ฌ์ฉ ์์ |
| Provider (@riverpod ๊ธฐ๋ณธ ํจ์) | โ ์ฝ๊ธฐ๋ง ๊ฐ๋ฅ | ref.watch(isLoadingProvider) |
| StateProvider | โ ๊ฐ๋ฅ | ref.read(isLoadingProvider.notifier).state = true; |
| @riverpod class (StateNotifier) | โ ๊ฐ๋ฅ | ref.read(isLoadingProvider.notifier).set(true); |
provider๋ฅผ ์ ์ํ๋ ๊ตฌ๋ฌธ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
(์ํ๋ฅผ ์ฝ๊ธฐ๋ง ํ ์ ์๋ ๋ถ๋ณ ๊ฐ)
@riverpod
Result myFunction(Ref ref) {
<your logic here>
}
์ด๋ ธํ ์ด์ ๋ ํจ์(annotated function)
์ด๋
ธํ
์ด์
๋ ํจ์์ ์ด๋ฆ์ ๋ฐ๋ผ provider์ ์ํธ์์ฉํ๋ ๋ฐฉ์์ด ๊ฒฐ์ ๋ฉ๋๋ค.
์ฃผ์ด์ง ํจ์ myFunction์ ๋ํด ์์ฑ๋ myFunctionProvider ๋ณ์๊ฐ ์์ฑ๋ฉ๋๋ค.
์ด๋
ธํ
์ด์
๋ ํจ์๋ ์ฒซ ๋ฒ์งธ ๋งค๊ฐ๋ณ์๋ก "ref"๋ฅผ ์ง์ ํด์ผ ํฉ๋๋ค.
๊ทธ ์ธ์๋ ํจ์๋ ์ ๋ค๋ฆญ์ ํฌํจํ์ฌ ์ฌ๋ฌ ๊ฐ์ ๋งค๊ฐ๋ณ์๋ฅผ ๊ฐ์ง ์ ์์ต๋๋ค. ์ด ํจ์๋ ์ํ ๊ฒฝ์ฐ Future/Stream์ ๋ฐํํ ์๋ ์์ต๋๋ค.
์ด ํจ์๋ provider๋ฅผ ์ฒ์ ์ฝ์ ๋ ํธ์ถ๋ฉ๋๋ค.
์ดํ ์ฝ๊ธฐ๋ ํจ์๋ฅผ ๋ค์ ํธ์ถํ์ง ์๊ณ ๋์ ์บ์๋ ๊ฐ์ ๋ฐํํฉ๋๋ค.
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'activity.dart';
// ์ฝ๋ ์์ฑ์ด ์๋ํ๋ ๋ฐ ํ์ํฉ๋๋ค.
part 'provider.g.dart';
/// ๊ทธ๋ฌ๋ฉด ์ด ํจ์์ ๊ฒฐ๊ณผ๋ฅผ ์บ์ํ
/// `activityProvider`๋ผ๋ provider๊ฐ ์์ฑ๋ฉ๋๋ค.
@riverpod
Future<Activity> activity(Ref ref) async {
// package:http๋ฅผ ์ฌ์ฉํ์ฌ Bored API์์ ์์์ activity๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
final response = await http.get(Uri.https('boredapi.com', '/api/activity'));
// ๊ทธ๋ฐ ๋ค์ dart:convert๋ฅผ ์ฌ์ฉํ์ฌ JSON ํ์ด๋ก๋๋ฅผ ๋งต ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ก ๋์ฝ๋ฉํฉ๋๋ค.
final json = jsonDecode(response.body) as Map<String, dynamic>;
// ๋ง์ง๋ง์ผ๋ก ๋งต์ Activity ์ธ์คํด์ค๋ก ๋ณํํฉ๋๋ค.
return Activity.fromJson(json);
}
โ ์์ : ์ผ๋ฐ Provider vs @riverpod ๋ฒ์
1. ๊ธฐ์กด ๋ฐฉ์
final nameProvider = Provider<String>((ref) => 'ํ๊ธธ๋');
2. @riverpod ๋ฐฉ์ (codegen)
import 'package:flutter_riverpod/flutter_riverpod.dart';
part 'my_provider.g.dart'; // ์ค์!
@riverpod
String name(NameRef ref) {
return 'ํ๊ธธ๋';
}
์ด๋ ๊ฒ ํ๋ฉด ์๋์ผ๋ก nameProvider๊ฐ ์์ฑ!
โ ์ฐจ์ด์ ์ ๋ฆฌ
| ํญ๋ชฉ | @riverpod ๋ฐฉ์ | ๊ธฐ์กด ๋ฐฉ์ |
| ์ฝ๋ ๊ธธ์ด | ๋ ์งง๊ณ ๊ฐ๊ฒฐํจ | ์ข ๋ ๊ธธ๊ณ ๊ตฌ์กฐ์ |
| ์ฝ๋ ์์ฑ | ํ์ (build_runner) | ํ์ ์์ |
| ํ์ ์์ ์ฑ, ์๋์์ฑ | ๋งค์ฐ ์ข์ | ๊ด์ฐฎ์ |
| ์ ์ง๋ณด์ | ๋๊ท๋ชจ ํ๋ก์ ํธ์ ์ ๋ฆฌ | ์ด๋ฐ์ ๋จ์ํจ |
โ ํ ์ค ์์ฝ
@riverpod์ผ๋ก ๋ง๋ counterProvider๋ ๊ธฐ์กด์ StateNotifierProvider์ StateNotifier๋ฅผ ๋ ๊น๋ํ๊ฒ ์๋์ผ๋ก ๋ง๋ค์ด์ฃผ๋ ๋ฐฉ์์ด์์.
Notifiers๋ providers์ "์ํ์ ์ฅ ์์ ฏ(stateful widget)"์
๋๋ค. provider๋ฅผ ์ ์ํ๋ ๋ฌธ๋ฒ์ ์ฝ๊ฐ ์์ ํด์ผ ํฉ๋๋ค.
์ด ์๋ก์ด ๋ฌธ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
@riverpod
class MyNotifier extends _$MyNotifier {
@override
Result build() {
<your logic here>
}
<your methods here>
}
@riverpod
class TodoList extends _$TodoList {
@override
Future<List<Todo>> build() async {
// ์ด์ ์ FutureProvider์ ์๋ ๋ก์ง์ด ์ด์ build ๋ฉ์๋์ ์์ต๋๋ค.
return [
Todo(description: 'Learn Flutter', completed: true),
Todo(description: 'Learn Riverpod'),
];
}
}
์ด๋ ธํ ์ด์ (annotation)
๋ชจ๋ providers๋ @riverpod ๋๋ @Riverpod()๋ก ์ด๋
ธํ
์ด์
ํด์ผ ํฉ๋๋ค. ์ด ์ด๋
ธํ
์ด์
์ ์ ์ญ ํจ์๋ ํด๋์ค์ ๋ฐฐ์นํ ์ ์์ต๋๋ค.
์ด ์ด๋
ธํ
์ด์
์ ํตํด provider๋ฅผ ์ค์ (config)ํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, @Riverpod(keepAlive: true)๋ฅผ ์์ฑํ์ฌ "auto-dispose"(๋์ค์ ์ดํด๋ณผ ๊ฒ์)๋ฅผ ๋นํ์ฑํํ ์ ์์ต๋๋ค.
Notifier
@riverpod ์ด๋
ธํ
์ด์
์ด ํด๋์ค์ ๋ฐฐ์น๋๋ฉด ํด๋น ํด๋์ค๋ฅผ "Notifier"๋ผ๊ณ ๋ถ๋ฆ
๋๋ค.
ํด๋์ค๋ _$NotifierName์ ํ์ฅํด์ผ ํ๋ฉฐ, ์ฌ๊ธฐ์ NotifierName์ ํด๋์ค ์ด๋ฆ์
๋๋ค.
Notifiers๋ provider์ ์ํ(state)๋ฅผ ์์ ํ๋ ๋ฉ์๋๋ฅผ ๋
ธ์ถํ ์ฑ
์์ด ์์ต๋๋ค.
์ด ํด๋์ค์ ๊ณต๊ฐ ๋ฉ์๋๋ ref.read(yourProvider.notifier).yourMethod()๋ฅผ ์ฌ์ฉํ์ฌ consumer๊ฐ ์ก์ธ์คํ ์ ์์ต๋๋ค.
UI์์ ์ํ๊ฐ ๋ณ๊ฒฝ๋์์์ ์ ์ ์๋ ์๋จ์ด ์๊ธฐ ๋๋ฌธ์, Notifiers์๋ ๊ธฐ๋ณธ ์ ๊ณต 'state' ์ธ์ ๊ณต๊ฐ ์์ฑ์ด ์์ด์ผ ํฉ๋๋ค.
โ ์ง๊ธ Riverpod 2.0์์ ์ค์ ๋ก "ํต์ฌ์ ์ผ๋ก ์ฐ๋ ๊ฒ"
โ 1. @riverpod + ํจ์
@riverpod
String name(NameRef ref) => 'ํ๊ธธ๋';
๐ ๊ธฐ์กด์ Provider ๊ธฐ๋ฅ์ ์๋ ์์ฑ
โ 2. @riverpod + Notifier ํด๋์ค
@riverpod
class Counter extends _$Counter {
@override
int build() => 0;
void increment() => state++;
}
๐ ๊ธฐ์กด์ StateNotifierProvider + StateNotifier` ์กฐํฉ์ ๋์ฒด
| ๊ณผ๊ฑฐ ๋ฐฉ์ | 2.0 ๋ฐฉ์ ๋ฐฉ์ |
| Provider | @riverpod ํจ์ |
| StateNotifierProvider | @riverpod class extends _$... |
| StateProvider | ๊ฑฐ์ ๊ทธ๋๋ก ์ฌ์ฉ ๊ฐ๋ฅํ์ง๋ง ์ฝ๋ ์์ฑ์ ์์ |
โ ์ค์ ๋ก ๋๋ถ๋ถ์ ๊ฒฝ์ฐ, Riverpod 2.0์์๋ @riverpod ๊ธฐ๋ฐ์ Provider๋ Notifier ๋ ๊ฐ์ง๋ง์ผ๋ก ์ถฉ๋ถํฉ๋๋ค.
ํํ ์ญํ ๋์ฒด๋๋ ์ ํต์ Provid| ํํ | ์ญํ | ๋์ฒด๋๋ ์ ํต์ Provider |
| @riverpod ํจ์ | ์ฝ๊ธฐ ์ ์ฉ ๊ฐ, ๊ณ์ฐ๋ ๊ฐ | Provider, FutureProvider, StreamProvider |
| @riverpod class extends _$Notifier<T> | ๋๊ธฐ ์ํ, ๋ก์ง ํฌํจ | StateNotifierProvider |
| @riverpod class extends _$AsyncNotifier<T> | ๋น๋๊ธฐ ์ํ (API ๋ฑ) | FutureProvider, AsyncNotifier, StreamProvider |
โ ๊ทธ๋์ ์ค์ ๋ก ํ์ํ ๊ฑด
- @riverpod ํจ์
→ Provider, FutureProvider, StreamProvider ๋์ฒด ๊ฐ๋ฅ - @riverpod class extends _$Notifier<T>
→ StateNotifierProvider ๋์ฒด - (์ต์
) @riverpod class extends _$AsyncNotifier<T>
→ ๋น๋๊ธฐ ์์ ์ด ํ์ํ ๊ฒฝ์ฐ๋ง ์ฌ์ฉ
โ๊ทธ๋ผ StateProvider๋?
- ์ฌ์ ํ ์์ฃผ ๊ฐ๋จํ ์ํ (ํ ๊ธ, ์ซ์ ๋ฑ) ์๋ ํธํด์ ์ธ ์ ์์ด์.
- ํ์ง๋ง ๊ตฌ์กฐ์ ์ผ๋ก ๋ ๊น๋ํ๊ฒ ๊ด๋ฆฌํ๋ ค๋ฉด Notifier๋ก ์ ํํ๋ ๊ฒ ์ข์์.
โ ํ ์ค ์ ๋ฆฌ
์ง๊ธ Riverpod 2.0์์๋ @riverpod ๊ธฐ๋ฐ์ Provider์ Notifier (ํ์ ์ AsyncNotifier)๋ง์ผ๋ก ๋๋ถ๋ถ์ ์ํ ๊ด๋ฆฌ๊ฐ ๋๋์.
์ง๊ธ Riverpod 2.0์์๋ @riverpod ์ฝ๋ ์์ฑ ๊ธฐ๋ฐ ๊ตฌ์กฐ๊ฐ ๋๋ถ๋ถ์ ๋์ฒดํ์ง๋ง,
StateProvider๋งํผ ๊ฐ๋จํ ๊ธฐ๋ฅ์ ์ ํํ ๋์ฒดํ๋ ๊ตฌ์กฐ๋ ์์ต๋๋ค.
โ ์ StateProvider๋ ๋์ฒด๋์ง ์์์๊น?
| ์ด์ | ์ค๋ช |
| ๋๋ฌด ๋จ์ํจ | ์ซ์ ํ๋, bool ํ๋ ์ํ ๊ด๋ฆฌํ ๋ ๋ฑ ์ข์ |
| ๋ณ๋ ํด๋์ค ๋ถํ์ | Notifier์ฒ๋ผ ํด๋์ค ์ ๋ง๋ค๊ณ ํ ์ค๋ก ๋๋ |
| ๋น ๋ฅด๊ฒ ํ ์คํธ์ฉ ์ํ ๋ง๋ค๊ธฐ ์ข์ | UI ํ ์คํธ๋ ์์ ์ํ์ ์ ๋ฆฌ |
โ ์ ๋ฆฌ: ํ์ฌ ๊ตฌ์กฐ์์์ ์ญํ ๋ถ๋ด
| ๋ชฉ์ | ์ถ์ฒ ๋ฐฉ์ | ๋น๊ณ |
| ๋จ์ํ ๊ฐ (int, bool ๋ฑ) | โ StateProvider | ์ฌ์ ํ ์ ์ฉํจ |
| ์ฝ๊ธฐ ์ ์ฉ ๊ฐ | โ @riverpod ํจ์ | ๊ธฐ์กด Provider ๋์ฒด |
| ๋ก์ง ํฌํจ ๋๊ธฐ ์ํ | โ @riverpod + Notifier | ๊ธฐ์กด StateNotifier ๋์ฒด |
| ๋น๋๊ธฐ ์ํ | โ @riverpod + AsyncNotifier | ๊ธฐ์กด FutureProvider ๋ฑ ๋์ฒด |
โ ํ ์ค ์์ฝ
StateProvider๋ ํ์ฌ๋ ๋์ฒด๋์ง ์์๊ณ , Riverpod 2.0์์๋ ์ฌ์ ํ ์ด์ ์๋ "์ฌํํ ์ํ ๊ด๋ฆฌ ๋๊ตฌ"์์.
ํ์ํ๋ค๋ฉด ์๋์ฒ๋ผ Notifier๋ก StateProvider์ฒ๋ผ ํ๋ด๋ด๋ ๊ฒ๋ ๊ฐ๋ฅํ์ง๋ง…
์์งํ ๋๋ฌด ์ค๋ฒ์์ง๋์ด๋ง์ด์์ ๐
@riverpod
class SimpleInt extends _$SimpleInt {
@override
int build() => 0;
void set(int value) => state = value;
void increment() => state++;
}
StatelessWidget/StatefulWidget์ด Consumer๋ฅผ ๋ฐํํ๋ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋์ ConsumerWidget/ConsumerStatefulWidget์ ์ ์ํ ์ ์์ต๋๋ค.
ConsumerWidget๊ณผ ConsumerStatefulWidget์ ์ฌ์ค์ StatelessWidget/StatefulWidget๊ณผ Consumer๋ฅผ ๊ฒฐํฉํ ๊ฒ์
๋๋ค. ์ด๋ค์ ์๋์ ์ง๊ณผ ๋์ผํ๊ฒ ๋์ํ์ง๋ง "ref"๋ฅผ ์ ๊ณตํ๋ค๋ ์ถ๊ฐ์ ์ธ ์ด์ ์ด ์์ต๋๋ค.
ConsumerWidget
/// "StatelessWidget" ๋์ "ConsumerWidget"์ ์๋ธํด๋์คํํ์ต๋๋ค.
/// ์ด๋ "StatelessWidget"์ ๋ง๋ค๊ณ "Consumer"๋ฅผ ์ฌ์กฐ์ ํ๋ ๊ฒ๊ณผ ๊ฐ์ต๋๋ค.
class Home extends ConsumerWidget {
const Home({super.key});
@override
// ์ด์ "build"๊ฐ ์ถ๊ฐ ๋งค๊ฐ๋ณ์ "ref"๋ฅผ ๋ฐ๋ ๋ฐฉ์์ ์ฃผ๋ชฉํ์ธ์
Widget build(BuildContext context, WidgetRef ref) {
// "Consumer"๋ฅผ ์ฌ์ฉํ์ ๋์ ๋ง์ฐฌ๊ฐ์ง๋ก ์์ ฏ ๋ด๋ถ์์ "ref.watch"๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
final AsyncValue<Activity> activity = ref.watch(activityProvider);
// ๋ ๋๋ง ๋ก์ง์ ๋์ผํ๊ฒ ์ ์ง๋ฉ๋๋ค.
return Center(/* ... */);
}
}
ConsumerStatefulWidget
// ConsumerStatefulWidget์ ํ์ฅํฉ๋๋ค.
// ์ด๊ฒ์ "Consumer" + "StatefulWidget"๊ณผ ๋์ผํฉ๋๋ค.
class Home extends ConsumerStatefulWidget {
const Home({super.key});
@override
ConsumerState<ConsumerStatefulWidget> createState() => _HomeState();
}
// "State" ๋์ "ConsumerState"๋ฅผ ํ์ฅํ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
// ์ด๊ฒ์ "ConsumerWidget" ๋ "StatelessWidget"๊ณผ ๋์ผํ ์๋ฆฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
class _HomeState extends ConsumerState<Home> {
@override
void initState() {
super.initState();
// ์ํ ์๋ช
์ฃผ๊ธฐ์๋ "ref"์ ์ก์ธ์คํ ์ ์์ต๋๋ค.
// ์ด๋ฅผ ํตํด ํน์ provider์ ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํ์ฌ ๋ํ ์์/์ค๋ต๋ฐ๋ฅผ ํ์ํ๋ ๋ฑ์ ์์
์ ์ํํ ์ ์์ต๋๋ค.
ref.listenManual(activityProvider, (previous, next) {
// TODO ์ค๋ต๋ฐ/๋ํ ์์ ํ์
});
}
@override
Widget build(BuildContext context) {
// "ref"๋ ๋ ์ด์ ๋งค๊ฐ๋ณ์๋ก ์ ๋ฌ๋์ง ์๊ณ ๋์ "ConsumerState"์ ํ๋กํผํฐ๊ฐ ๋ฉ๋๋ค.
// ๋ฐ๋ผ์ "build" ๋ด์์ "ref.watch"๋ฅผ ๊ณ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
final AsyncValue<Activity> activity = ref.watch(activityProvider);
return Center(/* ... */);
}
}'Mobile > Flutter' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [Flutter] Erros.. (0) | 2023.03.08 |
|---|---|
| [Flutter] Write your first Flutter app, part 1 (1) | 2022.06.07 |
| [Flutter] (0) | 2022.05.20 |
| ์ฃผ์ (2) | 2020.05.18 |
| Dart cheatsheet codelab (0) | 2020.01.02 |