티스토리 뷰
오늘도 강의를 통해 배운 내용을 정리해보고자 함!
오늘 주제는 ProxyProvider
ProxyProvider란 다른 Providerd의 값에 의존하는 Provider라고 함.
만약 A Provider에 의존해 B Provider의 값 바뀔 일이 있다면 그럴 때 써주면 됨!
생김새는 아래처럼 생겼음
생성자를 보면 create는 옵셔널 값이고, update는 required 값이라는 걸 알 수 있음.
create는 R타입의 객체를 생성하는데, 위에서 말한 것처럼 이 놈은 남에게 의존하는 놈이기 때문에 굳이 create가 필요없어서 옵셔널 값이라고 함
반대로 update는 의존하는 값이 바뀔 때마다 호출되는 함수로, 저 함수 덕분에 다른 값에 의존하여 계속 값이 업데이트 될 수 있는 것!
update 함수를 자세히 보면, 의존하는 T타입의 값을 인자로 줌! 그 인자를 가지고 R타입의 객체를 리턴하면 되는 것!
종류에는 ProxyProvider와 ChangeProxyProvider가 있다고 함
오늘은 간단한 예제와 함께 ProxyProvider에 대해 알아보겠음!
아래처럼 로그인 상태에 따라 레이블이 바뀌고, 그 레이블을 다른 페이지에서도 볼 수 있음!
1. Provider 만들기
로그인 상태를 바꾸는 ChangeNotifierProvider를 만들고, 그 ChangeNotifierProvider에 의존해서 값이 바뀌는 User라는 클래스를 만들겠음
Auth ChangeNotifierProvider는 아래와 같음!
bool 값을 하나받고, 그 bool 값을 변경하고 리스터들에게 알리는 메서드 하나를 가졌음
class Auth with ChangeNotifier {
bool _isLoggedIn = false;
bool get isLoggedIn => _isLoggedIn;
void changeState() {
_isLoggedIn = !_isLoggedIn;
notifyListeners();
}
}
이제 ChangeNotifierProvider에 의존하는 User라는 클래스 하나를 만들겠음
이 클래스 또한 간단한데, _isLoggedIn라는 속성을 가졌고, getter를 통해 string 값을 받아올 수 있음
class User {
final bool _isLoggedIn;
User(this._isLoggedIn);
String get status => _isLoggedIn ? '로그인 상태입니다.' : '로그아웃 상태입니다.';
}
2. 위젯트리에 Provider 넣기
이제 이 Provider을 사용할 수 있게 위젯 트리 안에서 선언하겠음
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<Auth>(
create: (context) => Auth(),
),
ProxyProvider<Auth, User>(
update: (BuildContext context, Auth value, User? previous) {
debugPrint('=====update');
return User(value.isLoggedIn);
},
)
],
child: MaterialApp(
title: 'addPostFrameCallback',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const ProxyProviderExample(),
),
);
}
}
ChangeNotifierProvider는 평소와 동일하게 선언해주면 되고, ProxyProvider를 선언하는 부분을 보면 됨.
위에서 ProxyProvider는 create가 required가 아니라고 했었음! 그 대신 update가 required 메서드임
그래서 보면 두 개의 타입을 받는데, Auth 타입의 객체에 의존하여 User 타입의 객체 값이 바뀐다고 할 수 있음
의존하는 값(value)이 바뀔 때마다 update 함수가 호출되면서 User 객체가 다시 만들어지고 그 값을 리턴하는 것!
ProxyProvider<Auth, User>(
update: (BuildContext context, Auth value, User? previous) {
debugPrint('✅=====update $previous');
return User(value.isLoggedIn);
},
)
3. 위젯에서 사용하기
이제 이 값을 위젯에 반영해보겠음
위젯은 간단함
1) 로그인 버튼을 누르면 Auth의 changeState() 메서드를 통해 _isLoggedIn이라는 bool 값을 바꿔줌
onPressed: () {
context.read<Auth>().changeState();
}
2) 바뀐 값에 의존하여 User의 status getter 문을 로그인 버튼 위에 보여줌
그리고 다음 페이지 버튼을 누르면 다른 페이지로 넘어가고 거기에도 User 객체의 status를 getter로 읽어옴
final String status = context.watch<User>().status;
3) 전체 코드
class ProxyProviderExample extends StatelessWidget {
const ProxyProviderExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ProxyProvider'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Text(context.watch<User>().status),
const ShowUserStatus(),
const SizedBox(height: 30),
const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
LoginButton(
title: '로그인 상태 바꾸기',
),
],
),
const SizedBox(height: 5),
OutlinedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProxyProviderSubExample(),
));
},
child: Text('다음 페이지'))
],
),
),
);
}
}
class ShowUserStatus extends StatelessWidget {
const ShowUserStatus({super.key});
@override
Widget build(BuildContext context) {
final String status = context.watch<User>().status;
debugPrint('Label build -');
return Text(
status,
style: TextStyle(fontSize: 28.0),
);
}
}
class LoginButton extends StatelessWidget {
const LoginButton({super.key, required this.title});
final String title;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
context.read<Auth>().changeState();
},
child: Text(
title,
style: TextStyle(fontSize: 20.0),
),
);
}
}
class ProxyProviderSubExample extends StatelessWidget {
const ProxyProviderSubExample({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: ShowUserStatus(),
),
);
}
}
이렇게 만든 상태로 ProxyProvider의 update 함수가 언제 호출되는지 보겠음
처음 앱이 빌드되자마자 update함수가 실행됨 (처음에는 null 값이었지만, 바로 객체가 생성되며 _isLogggedIn이 false을 받아 '로그아웃 상태입니다'를 리턴한 거임)
그 후 버튼을 누를 때마다 호출되는 걸 알 수 있음.
create를 해주지 않아도 update 함수를 바로 초기값이 업데이트 되는 것
4. 주의할 점
그런데 여기서 주의할 점은, 위젯 트리상의 순서임!
ProxyProvider는 다른 프로바이더에 의존하는 프로바이더 아니겠음? 근데 내가 의존하는 상대보다 먼저 위젯 트리에 생기는 게 말이 되지 않음! 실제로 아래처럼 프로바이더 순서를 바꿔서 실행하면 오류가 발생함
providers: [
ProxyProvider<Auth, User>(
update: (BuildContext context, Auth value, User? previous) {
debugPrint('✅=====update $previous');
return User(value.isLoggedIn);
},
),
ChangeNotifierProvider<Auth>(
create: (context) => Auth(),
),
],
위에서는 User 클래스 타입을 썼지만, 그냥 간단한 값들의 경우 아래처럼 사용해주면 됨
ProxyProvider<Auth, String>(
update: (context, value, previous) {
return value.isLoggedIn ? '로그인 상태' : '로그아웃 상태';
},
),
값을 읽어올 때는 아래처럼!
final String status = context.watch<String>();
그러나 이렇게 할 경우, String를 리턴하는 ProxyProvier가 여러개라면 문제가 될 수 있으니, 주의가 필요!
'Flutter' 카테고리의 다른 글
Flutter의 Unit Test (Mockito 패키지) (0) | 2024.07.26 |
---|---|
[간단 Tip] Barrel, part, part of (0) | 2024.07.19 |
try-catch 정리해보기 (0) | 2024.07.11 |
addPostFrameCallback를 사용한 error 해결 (0) | 2024.07.09 |
Form 필드로 유효성 체크하기 (1) | 2024.07.02 |
- Total
- Today
- Yesterday