우선 리버팟을 사용하기 위해 리버팟에 있는 프로바이더들의 종류와 각 프로바이더의 역할을 이해해야한다.
대표적으로 3가지만 예시를 들겠다 (이 3개로 웬만한 건 모두 해결 가능하긴 하다.. 비동기도 커스텀이 가능하기에)
- Provider
- StateProvider
- NotifierProvider
다음 3가지 기준으로 무엇을 선언할지 판단
- 기본 상태 관리
- 상태 변화가 필요한 경우
- → Provider 사용
- 단순 상태 관리
- 상태 변화는 필요하지만 로직이 단순한 경우
- → StateProvider 사용
- 복잡한 상태 관리
- 상태 변화가 필요하고 복잡한 로직이 포함된 경우
- → NotifierProvider 사용
복잡한 로직의 기준
- 실제 사용하면서 판단하는 것을 권장
- 예시:
- 단순: 단일 int 값 관리
- 복잡: int 값을 다른 상태나 API와 연동하여 다양한 기능 수행
setState 대신 StateProvider 사용하기
- 기본 Flutter 카운터 앱을 예시로 설명
- 기존: FloatingButton으로 counter 직접 조작
- 변경: StateProvider를 사용하여 counter 관리
stateProvider 적용 전 코드
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
stateProvider 적용 후 코드
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final counterProvider = StateProvider<int>((ref) => 0);
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Consumer(builder: (context, ref, child) {
final counter = ref.watch(counterProvider);
return Text(
'$counter',
style: Theme.of(context).textTheme.headlineMedium,
);
}),
],
),
),
floatingActionButton: Consumer(builder: (context, ref, child) {
return FloatingActionButton(
onPressed: () {
ref.read(counterProvider.notifier).update((state) => state + 1);
},
tooltip: 'Increment',
child: const Icon(Icons.add),
);
}), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
1. _counter 변수를 지우고 클래스 외부에 counterProvider를 만들어주었다. 여기서 0은 초기값이다. 만약 1부터 시작하고 싶으면 1로 쓰면 된다,.
final counterProvider = StateProvider<int>((ref) => 0);
2. 클래스 내부 지역변수에 _counter변수가 없으니 ref라는 WidgetRef 클래스의 인스턴스를 이용하여 내가 만든 프로바이더를 찾을 수 있다. 위젯에서 ref를 쓸며녀 Cousumer위젯으로 덮어주면 된다.
ref.watch 문법으로 counterProvider를 가져와서 counter변수에 저장했다. counterProvider를 선언할 때 int값을 선언했으므로 이 타입은 int 와 같다
ref.watch / ref.read 두 가지 방식으로 값을 가져올 수 있는데 이건 추 후 설명하겠다.
Consumer(builder: (context, ref, child) {
final int counter = ref.watch(counterProvider);
return Text(
'$counter',
style: Theme.of(context).textTheme.headlineMedium,
);
}),
3. conterProvider의 값을 변경해보자
ref에 접근하기 위해 좀 전에랑 똑같이 Consumer 위젯으로 덮어줬다.
프로바이더의 값을 변경하려면 notifier가 필요하다
ref.read(counterProvider.nonifier) 이렇게 인스턴스를 만들고 . 을 누르면 이 프로바이더가 가지고 있는 메소드 혹은 필드 들이 나온다.
위에서 설명한 stateProvider말고 notifierProvider는 이 메소드들과 필드들을 커스텀하여 개발자 입맛에 맞게 추가할 수 있다. (그래서 복잡한 로직에 사용)
내장되어있는 update 메소드를 이용하여 기존 state값 즉, counter값을 +1 시켜서 저장한다. 이러면 Consumer 위젯 아래에 ref.watch로 counterProvider를 구독하고 있는 모든 필드의 값이 리빌드 된다.
즉 setState없이 자동으로 리빌드 되고 Consumer아래로만 리빌드 되어 위젯의 리빌드를 최소화 할 수 있다.
또한 다른 외부 위젯에서도 이 값에 접근이 가능하여 static 변수처럼 사용가능하다.
floatingActionButton: Consumer(builder: (context, ref, child) {
return FloatingActionButton(
onPressed: () {
ref.read(counterProvider.notifier).update((state) => state + 1);
},
tooltip: 'Increment',
child: const Icon(Icons.add),
);
}),
'Flutter' 카테고리의 다른 글
Flutter[플러터] - Riverpod(리버팟) 상태관리 누구나 쉽게! (1) (2) | 2024.11.07 |
---|---|
Flutter[플러터(고급)] Flutter 최적화하기(성능 개선), 렌더링 속도 Jank의 대하여 (1) | 2024.10.24 |
Flutter[플러터(고급)] Flutter Performance - Widget rebuild stats (0) | 2024.10.24 |
[Dart] 스택(Stack), 힙(Heap), 데이터 영역(Data Segment) 그리고 가비지 컬렉션 (0) | 2024.10.22 |
Flutter[플러터] - IOS 스플래쉬 검은화면 제거하기 feat .flutter_native_splash (0) | 2024.07.24 |