Flutter : Provider 와 Riverpod
Flutter에서 상태관리는 상태변화를 감지해서 UI를 업데이트하고 데이터를 동기화하는 등 많은 부분을 관리하는데 필수적입니다. 상태관리를 하는데 여러가지 방법이 있는데, 오늘은 많이 사용되는 setState, Provider, RiverPod에 대해서 알아보겠습니다.
1. setState
setState는 Flutter에서 기본으로 제공하는 상태 관리 방법으로 StateFulWidget을 통해 사용할 수 있습니다.
import 'package:flutter/material.dart';
class CounterApp extends StatefulWidget {
@override
_CounterAppState createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('setState 예제'),
),
body: Center(
child: Text('버튼이 눌린 횟수: $_counter'),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
void main() => runApp(MaterialApp(home: CounterApp()));
위의 코드는 setState를 사용한 예시입니다. setState는 변화를 감지하면 flutter에게 변화를 알려서 업데이트합니다.
보시면 statefulWidget를 사용하는 상태에서 변화가 있는 _counter를 setState안에서 관리하면서 counter값을 UI에 적용하고 있습니다. 이렇게 상태를 관리할 수 있습니다.
단점
하지만 flutter의 기본 상태관리 기능이기 때문에 몇가지 단점이 있습니다.
- setState는 상태가 감지되면 build함수를 호출하게 됩니다. 그렇기 때문에 불필요하게 많은 랜더링이 되어 앱의 성능이 느려질 수 있습니다.
- setState는 위젯안에서만 국한되어, 상태를 여러 위젯이 공유해야하는 상태에서는 구조가 복잡해집니다.
- 비동기 처리를 할때 여러가지 setState가 있을경우 상태가 중첩되어 이상이 생길 수 있다.
이러한 문제들을 해결하기 위해서 사용되는 상태관리 패키지들이 provider와 riverpod 입니다.
Provider
provider는 의존성 주입을 통해서 상태를 관리하는 라이브러리입니다.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 상태 변경 시 알림
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider 예제')),
body: Center(
child: Consumer<Counter>(
builder: (context, counter, child) {
return Text('버튼이 눌린 횟수: ${counter.count}');
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Provider.of<Counter>(context, listen: false).increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
),
);
}
}
ChangeNotifier를 통해서 setState처럼 상태를 변화하는 클래스를 생성해줍니다. 이를 ChangeNotifierProvider를 통해서 앱이 상태를 받게 됩니다. 그리고 나서 Provider.of() 혹은 Consumer를 상용해서 상태를 위젯에 전달하여 UI를 업데이트 합니다.
단점
- provider도 changeNotifier를 사용할때 해당하는 상태를 확인하는 모든 위젯이 다시 렌더링하게는 문제가 있습니다.
- 테스트가 불가능해서 독립적인 기능을 확인하기 어렵습니다.
- 위젯의 생명주기를 관리하지 않기 때문에 따로 관리를 해주어야하는 문제가 있습니다.
Riverpod
Riverpod은 provider의 발전되 형태의 상태관리 라이브러이입니다. provider의 기능을 사용하면서 더 안전하고 유연하면서 많은 기능을 지원합니다.
특징
- Null Safety: Riverpod는 Dart의 null safety를 완벽하게 지원하여, 런타임 오류를 줄이고 코드의 안정성을 높입니다.
- 전역 상태 관리: 애플리케이션의 어느 곳에서든 상태를 쉽게 접근하고 관리할 수 있습니다.
- 테스트 용이성: 상태를 독립적으로 관리할 수 있어, 테스트가 용이합니다. Provider의 상태를 Mocking하기 쉬워, 유닛 테스트가 간편합니다.
- 비동기 처리 지원: 비동기 작업을 쉽게 처리할 수 있는 FutureProvider와 StreamProvider를 제공합니다.
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
사용할때는 가장먼저 MyApp()을 ProviderScope로 감쌉니다.
class Counter extends StateNotifier<int> {
Counter() : super(0); // 초기 상태
void increment() {
state++; // 상태 변경
}
}
final counterProvider = StateNotifierProvider<Counter, int>((ref) {
return Counter();
});
class CounterScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider); // 상태 구독
return Scaffold(
appBar: AppBar(
title: Text('Riverpod 카운터 예제'),
),
body: Center(
child: Text('버튼이 눌린 횟수: $count'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
ref.read(counterProvider.notifier).increment(); // 상태 변경
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
상태를 관리하는StateNotifier를 상속 받는 클래스를 만들고 이를 StateNotifierProvider로 정의 합니다.
그리고 상태를 ConsumerWidget안에서 ref.watch나 ref.read를 통해서 상태를 구독하고 상태를 변경합니다.
단점
- 학습 곡선 : riverpod을 사용하기 위해서는 상태관리, provider 등 여러가지 알아야하는 것이 있어서 학습 곡선이 있다는 것이 단점입니다.
- Boilerplate 코드 : 사용하기 위해서 설정해야하는 코드들이 다수 있어서 간단한 프로젝트에서는 불필요하게 코드가 많아질 수 있습니다.
오늘은 이렇게 상태를 하는 방법과 상태관리를 위해서 사용하는 라이브러리 몇가지를 알아보았습니다. 상태관리는 필수는 아니지만 상태가 변화가 발생할 경우 반드시 필요해집니다. 그렇기 때문에 위의 방법을 알아두시면 도움이 될 것입니다. 또한 다른 방법들도 공부해보시면 도움이 될 것입니다.
'Flutter' 카테고리의 다른 글
폰트 적용 (0) | 2025.02.06 |
---|---|
flutter Statefulwigets 생명주기 (0) | 2025.01.15 |
Clean Architecture (0) | 2025.01.13 |
Firebase 쉽게 연결하기 (1) | 2024.12.20 |
Navigation VS GoRouter (0) | 2024.12.20 |