반응형
Overview
Provider 패키지를 사용하여 StatefulWidget의 상태 관리를 편리하게 할 수 있습니다.
아래의 예시는 플러터 provider 패키지의 공식 가이드 문서를 참고하여 작성하였습니다.
또한, 소스 코드는 글쓴이의 취향에 맞게 수정되어 작성되었음을 알려드립니다.
Provider 패키지란?
- 상속된 위젯들을 감싸는 래퍼
- 애플리케이션에서 데이터의 상태를 관리하고 공유하기 위한 기능을 제공하는 패키지
- 상태를 사용하는 위젯을 자동으로 재빌드
Provider 패키지 추가하기
특정 버전으로 추가
- (프로젝트 디렉토리 내) pubspec.yaml 파일의 dependencies 아래 코드 추가
- 컨트롤 + S (파일 저장)을 눌러 의존성 주입
dependencies:
flutter:
sdk: flutter
provider: ^6.0.5 # 버전 입력
최신 안정 버전으로 추가
flutter pub add provider
프로바이더의 값 읽고 불러오기
<T> 객체란?
- 특정한 타입을 일반화하여 나타내는 용어
- T는 Type의 약자로 실제로 어떤 타입인지는 사용하는 시점에서 결정
- 예시) int의 경우 T타입 객체는 int 타입의 값을 가지는 변수나 객체를 의미
context.watch<T>()
- 객체의 상태가 변경될 때 위젯을 다시 빌드하는 데 사용
- ChangeNotifier를 사용하여 상태가 변경될 때 위젯을 다시 렌더링하여 새로운 상태 반영
context.read<T>()
- 객체의 데이터를 읽어옴
- 개체의 상태 변경을 감지하지 않고, 현재 상태를 한번만 읽어오는 데 사용
- 상태 변경에 따라 위젯을 다시 빌드할 필요가 없는 경우 유용
- 초기 설정, 특정 작업에서 한 번만 객체의 값을 읽어올 때 등
context.select<T, R>(R cb(T value))
- 객체의 일부분만 관리할 경우 사용
- 'cb'라는 콜백 함수를 사용하여 객체 특정 속성을 선택하고 해당 속성만을 관리
- 전체 객체를 읽어올 필요 없이 선택한 속성에만 의존할 수 있으며, 성능 최적화에 유용
- 객체가 많은 속성을 가질 경우, 필요한 속성만 확인하여 해당 속성이 변경될 때에만 위젯을 다시 렌더링 함
Provider.of<T>(context, listen: false)
- Provider로부터 값을 읽어오는 메서드
- listen: false를 사용하면 위젯은 한 번 값을 읽고나면 다시 렌더링 하지 않음
- 위젯은 값을 감지하지 않기 떄문에 상태 변경시 값을 업데이트하려면 수동으로 setStaete 함수를 호출하여야 함
사용 예시
메인 함수에 프로바이더 설정
- CountUpdator은 사용자가 지정한 프로바이더 클래스명임
- 상태 변경을 위한 로직을 사용자가 정의하고 사용하겠다는 의미
void main() {
runApp(
MaterialApp(
// 멀티 프로바이더 정의
home: MultiProvider(
providers: [
// CountUpdator : 카운트를 증가시키고 업데이트할 Provider Class
ChangeNotifierProvider(create: (_) => CountUpdator()),
],
// 프로바이더를 제공 받을 페이지
child: const ProviderTest(),
),
),
);
}
Provider 상태 변경 클래스 정의 (CountUpdator)
- CountUpdator 클래스는 사용자가 임의로 지정한 이름임
- 해당 클래스의 함수가 호출되면 해당 변수는 자동으로 리빌드되어 상태 변경 됨
// 상태 변경을 위한 Provider 클래스 정의
class CountUpdator extends ChangeNotifier {
// 숫자 카운트를 위한 변수 초기화
int _cnt = 0;
// 카운트 된 값을 cnt 변수에 저장
int get cnt => _cnt;
void updateCount(int number) {
// 함수가 호출되면 카운트 + 1
_cnt = number + 1;
// 상태가 변경 업데이트
notifyListeners();
}
}
메인 페이지(ProviderTest)
- ProviderTest 클래스는 사용자가 임의로 지정한 이름임
- 플로팅 액션 버튼을 클릭시 프로바이더 클래스의 CountUpdator 함수를 통해 카운트 +1
- 원래 구문 : context.read<CountUpdator>().updateCount(param)
- onPressed 핸들러를 위한 변경 구문 : Provider.of<CountUpdator><context, listen: false).updateCount(param)
- 텍스트 위젯에서 카운트된 값 출력
- 구문 : context.watch<CountUpdator>().cnt
class ProviderTest extends StatefulWidget {
const ProviderTest({super.key});
@override
State<ProviderTest> createState() => _ProviderState();
}
class _ProviderState extends State<ProviderTest> {
// 카운트 초기 값 설정
int countNumber = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(builder: (context) {
countNumber = context.watch<CountUpdator>().cnt;
return Center(
child: Text(
//'${context.watch<CountUpdator>().cnt}',
countNumber.toString(),
style: const TextStyle(
fontSize: 50,
fontWeight: FontWeight.bold,
),
),
);
}),
// 클릭시 카운트가 +1씩 되는 플로팅 액션 버튼
floatingActionButton: FloatingActionButton(
// Provider 데이터 저장
// 기존 구문 : context.read<클래스명>().함수명(파라미터명)
// onPressed 핸들러는 위젯 트리 외부에서 실행되므로 Provider 사용을 위한 값 설정 (listen:false)
onPressed: () => Provider.of<CountUpdator>(context, listen: false)
.updateCount(countNumber),
tooltip: '카운트+1',
child: const Icon(Icons.add),
),
);
}
}
전체 소스 코드
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
MaterialApp(
// 멀티 프로바이더 정의
home: MultiProvider(
providers: [
// CountUpdator : 카운트를 증가시키고 업데이트할 Provider Class
ChangeNotifierProvider(create: (_) => CountUpdator()),
],
// 프로바이더를 제공 받을 페이지
child: const ProviderTest(),
),
),
);
}
// 상태 변경을 위한 Provider 클래스 정의
class CountUpdator extends ChangeNotifier {
// 숫자 카운트를 위한 변수 초기화
int _cnt = 0;
// 카운트 된 값을 cnt 변수에 저장
int get cnt => _cnt;
void updateCount(int number) {
// 함수가 호출되면 카운트 + 1
_cnt = number + 1;
// 상태가 변경 업데이트
notifyListeners();
}
}
class ProviderTest extends StatefulWidget {
const ProviderTest({super.key});
@override
State<ProviderTest> createState() => _ProviderState();
}
class _ProviderState extends State<ProviderTest> {
// 카운트 초기 값 설정
int countNumber = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(builder: (context) {
countNumber = context.watch<CountUpdator>().cnt;
return Center(
child: Text(
//'${context.watch<CountUpdator>().cnt}',
countNumber.toString(),
style: const TextStyle(
fontSize: 50,
fontWeight: FontWeight.bold,
),
),
);
}),
// 클릭시 카운트가 +1씩 되는 플로팅 액션 버튼
floatingActionButton: FloatingActionButton(
// Provider 데이터 저장
// 기존 구문 : context.read<클래스명>().함수명(파라미터명)
// onPressed 핸들러는 위젯 트리 외부에서 실행되므로 Provider 사용을 위한 값 설정 (listen:false)
onPressed: () => Provider.of<CountUpdator>(context, listen: false)
.updateCount(countNumber),
tooltip: '카운트+1',
child: const Icon(Icons.add),
),
);
}
}
참고
반응형