Flutter Provider 상세 소개 (Flutter Provider Detailed Introduction)
1. Provider란? (What is Provider?)
Provider는 Flutter에서 공식적으로 권장하는 상태 관리 패턴입니다.
- 상태 관리: UI가 데이터 변경을 감지하고 자동으로 업데이트됨.
- 의존성 주입: 객체를 효율적으로 공유할 수 있음.
- Flutter의 기본 철학과 잘 맞음:
InheritedWidget
을 기반으로 하여 성능이 우수함.
💡 Bloc과 비교하면 코드가 간결하고, GetX보다 공식적인 지원을 받는 것이 장점입니다.
2. Provider 설치 (Installing Provider)
pubspec.yaml 파일에 provider
패키지를 추가합니다.
dependencies: provider: latest_version # 최신 버전 사용
➡ 최신 버전 확인: pub.dev Provider
설치 후 flutter pub get
실행!
3. Provider 기본 사용법 (Basic Usage of Provider)
3.1 ChangeNotifier를 이용한 상태 관리 (Using ChangeNotifier)
1️⃣ 상태 클래스 생성
import 'package:flutter/material.dart'; class CounterProvider extends ChangeNotifier { int _counter = 0; int get counter => _counter; // Getter void increment() { _counter++; notifyListeners(); // UI 업데이트 요청 } }
💡 notifyListeners()
를 호출하면 Provider를 구독하는 모든 UI가 업데이트됩니다.
2️⃣ Provider 등록 (main.dart
)
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'counter_provider.dart'; // 위에서 만든 Provider void main() { runApp( ChangeNotifierProvider( create: (context) => CounterProvider(), child: MyApp(), ), ); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HomeScreen(), ); } }
💡 ChangeNotifierProvider
를 사용하여 CounterProvider
를 앱 전역에서 사용할 수 있도록 등록합니다.
3️⃣ UI에서 Provider 사용
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'counter_provider.dart'; class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { final counterProvider = Provider.of<CounterProvider>(context); return Scaffold( appBar: AppBar(title: Text('Provider 예제')), body: Center( child: Text('카운터: ${counterProvider.counter}', style: TextStyle(fontSize: 24)), ), floatingActionButton: FloatingActionButton( onPressed: counterProvider.increment, child: Icon(Icons.add), ), ); } }
💡 Provider.of<CounterProvider>(context)
를 사용하여 상태를 가져오고, 버튼을 클릭하면 상태가 업데이트됩니다.
✅ 장점: notifyListeners()
덕분에 UI가 자동으로 업데이트됨.
❌ 단점: context
가 필요하여 코드가 길어질 수 있음.
3.2 Consumer를 이용한 상태 관리 (Using Consumer)
Consumer를 사용하면 Provider.of
보다 효율적으로 UI를 빌드할 수 있습니다.
class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Consumer 예제')), body: Center( child: Consumer<CounterProvider>( builder: (context, counterProvider, child) { return Text('카운터: ${counterProvider.counter}', style: TextStyle(fontSize: 24)); }, ), ), floatingActionButton: FloatingActionButton( onPressed: () => context.read<CounterProvider>().increment(), child: Icon(Icons.add), ), ); } }
✅ 장점: Consumer
를 사용하면 필요한 부분만 리빌드되어 성능이 향상됨.
❌ 단점: Consumer
가 많아지면 코드 가독성이 떨어질 수 있음.
3.3 Selector를 이용한 최적화 (Using Selector for Optimization)
Selector는 특정 값이 변경될 때만 UI를 업데이트하도록 최적화할 수 있습니다.
class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Selector 예제')), body: Center( child: Selector<CounterProvider, int>( selector: (context, provider) => provider.counter, // 특정 값만 감지 builder: (context, counter, child) { return Text('카운터: $counter', style: TextStyle(fontSize: 24)); }, ), ), floatingActionButton: FloatingActionButton( onPressed: () => context.read<CounterProvider>().increment(), child: Icon(Icons.add), ), ); } }
✅ 장점: 불필요한 UI 리빌드를 방지하여 성능 향상.
❌ 단점: 코드가 다소 길어질 수 있음.
4. MultiProvider 사용 (Using MultiProvider)
여러 개의 Provider
를 동시에 사용해야 할 경우 MultiProvider
를 사용하면 됩니다.
void main() { runApp( MultiProvider( providers: [ ChangeNotifierProvider(create: (context) => CounterProvider()), ChangeNotifierProvider(create: (context) => AnotherProvider()), ], child: MyApp(), ), ); }
✅ 장점: 여러 개의 Provider를 쉽게 관리 가능.
✅ 단점: MultiProvider
가 많아지면 코드가 길어질 수 있음.
5. 라우팅과 Provider (Navigation with Provider)
새로운 화면으로 이동할 때 Provider
의 상태를 유지하려면 ChangeNotifierProvider.value
를 사용합니다.
Navigator.push( context, MaterialPageRoute( builder: (context) => ChangeNotifierProvider.value( value: Provider.of<CounterProvider>(context, listen: false), child: NextScreen(), ), ), );
✅ 장점: 새로운 화면에서도 기존 Provider 상태를 유지할 수 있음.
❌ 단점: listen: false
를 설정해야 성능이 저하되지 않음.
6. 의존성 관리 (Dependency Injection with Provider)
Provider는 ProxyProvider
를 이용하여 다른 Provider의 데이터를 의존성으로 주입할 수 있습니다.
ChangeNotifierProvider(create: (context) => UserProvider()), ProxyProvider<UserProvider, AuthProvider>( update: (context, userProvider, authProvider) => AuthProvider(userProvider.user), )
✅ 장점: Provider 간의 의존 관계를 설정할 수 있음.
7. 결론 (Conclusion)
기능 | Provider | GetX | Bloc |
---|---|---|---|
사용법 | 보통 ⭕ | 쉬움 ✅ | 어려움 ❌ |
성능 | 우수 ✅ | 빠름 ✅ | 느림 ❌ |
공식 지원 | 있음 ✅ | 없음 ❌ | 있음 ✅ |
의존성 관리 | 있음 ✅ | 있음 ✅ | 있음 ✅ |
Provider는 공식적으로 지원되는 상태 관리 패턴이며, GetX보다 코드가 정형화되어 있어 유지보수에 유리합니다.
하지만, 코드가 길어질 수 있어 소규모 프로젝트에서는 GetX, 대규모 프로젝트에서는 Provider를 사용하는 것이 좋습니다. 🚀