티스토리 뷰

Flutter

ProxyProvider 간단 사용 예제

순진이 2024. 7. 16. 22:32

오늘도 강의를 통해 배운 내용을 정리해보고자 함!

오늘 주제는 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
링크