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를 사용하는 것이 좋습니다. 🚀
