달달한 스토리

728x90
반응형

 

참고 블로그

https://terry1213.github.io/flutter/flutter-provider/

 

[Flutter] Provider를 통해 상태 관리하기

Flutter를 통해 개발을 진행하다보면 상태(State) 관리가 매우 중요하다는 것을 느낄 수 있다.

terry1213.github.io

 

예전에 내가 약간 난독이 있어나 보다.

 

이해가 별로 되지 않아서 그런가? 어려운 공식문서나

 

누가 가독성 어렵게 설명해 놓은 글을 제대로 읽지 못해 여간 스트레스를 받고 있었다.

 

아무래도 가독성인 듯하다.

 

누가 flutter Provider에 대한 글을 너무 잘 써두어서 읽기가 너무 편했다.

 

일단 pub.dev에서 provider을 설치해보자.

 

https://pub.dev/packages/provider

 

provider | Flutter Package

A wrapper around InheritedWidget to make them easier to use and more reusable.

pub.dev

들어가서 

 

dependencies에 넣어줄 provider 버전을 

 

yaml파일에 넣어주고 pub get을 해주자.

 

(이 방법은 알고 있을 거라 믿는다.)

 

Provider란

우선 provider를 설명하기 전에 state에 대해 먼저 설명하는 것이 좋을 것 같다.

 

state란 위젯이 빌드가 되는 동시에 읽을 수 있고, 위젯의 생명 주기 동안 변경할 수 있는 정보를 말한다.

 

사용자와 상호작용하면서 데이터들이 이 state에 해당된다.

 

state에서 특정 상태를 한 레이어(페이지) 안에서 관리하는 것은 어렵지 않겠지만,

 

여러 상태를 여러 페이지에서 관리한다면 많은 어려움을 겪게 될 것이다.

 

이러한 어려움을 해결하기 위해 flutter에서는 효과적인 패키지들이 많이 생겼다.

 

1. Provider

2. IngeritedWidget & InheritedModel

3. Redux

4. BLoC / Rx

5. GetX

 

이렇게 5가지가 있다.

 

아직 플러터 짬찌인 나는 Provider를 간신히 배우는 중이다.

 

Provider는 크게 생산하는 부분과 소비하는 부분으로 나뉜다.

 

간단하게 설명하면

 

Provider라는 클래스의 있는 하나의 변수 값을

 

여러 위젯들이 참조할 수 있게 하는 것이다.

 

예를 들어보자..

 

1. 기본적인 Provider(생산)

 

class Counter {
int _conter = 0;

int get count => _count;

add(){
    _count++;
  }

}

 

Counter라는 클래스를 정의해주자.

 

그리고 Provider를 생성해준다.

 

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      title: 'Counter',
      theme: ThemeData(

        primarySwatch: Colors.blue,
      ),
      home: Provider(
        create: (BuildContext context) => Counter() , //create에 참조할 Counter클래스를 리턴해준다.
    
        child: Home(), //자식값은 값을 받을 위젯(Home)을 넣어준다.
      ),

    );
  }
}

 

Provider 객체를 만들어 안에 create와 child 값에 다음과 같은 값을 넣는다.

 

여기서 Counter클래스를 참조해서 _count값을 올려봤자 화면(UI)에 표시되지 않을 것이다.

 

2. ChangeNotifierProvider(생산 2)

그래서 이 ChangeNotifierProvider를 이용하는 것이다.

 

Provider객체 대신 ChangeNotifierProvider를 사용하면 _counter 증가될 때마다

 

UI의 값도 변하게 된다.

 

과정은 이러하다.

 

ChangeNotifierProvider은 ChangeNotifier를 상속받은 Counter 클래스의 notifyListeners() 메서드가

 

호출될 때 자신의 자식을 재 빌드하여 UI를 업데이트해준다.

 

말이 어려우니 코드를 보자.

 

class Counter extends ChangeNotifier{
  int _count = 0;
  int get count => _count;

  add(){
    _count++;
    notifyListeners(); //변경되었다는 신호를 보내줘야 실횅된다.
  }
}

아까 위에서 사용했던 Counter클래스이다.

 

이 클래스에 ChangeNotifier를 상속해주고,

 

count가 호출되면 _count의 값을 반환해 주도록 한다.

 

그리고 add()를 호출하면 _count의 값이 1 증가하고, 

 

notifyListeners()를 호출하여 (값이 변했다고 UI에게 알리기)

 

UI값을 재 빌드하여 값을 변경해준다.

 

그렇다면 이 Counter클래스를 사용하기 위해 Provider부분도 변경해주자.

 

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      title: 'Counter',
      theme: ThemeData(

        primarySwatch: Colors.blue,
      ),
      home: ChangeNotifierProvider(
        create: (BuildContext context) => CountProvider() ,
        child: Home(),
      ),

    );
  }
}

이런 식으로 ChangeNotifierProvider로 바꿔주자.

 

이 ChangeNotifierProvider가 ChangeNotifier를 상속받은 클래스의 notifyListeners()가 호출될 때까지 기다릴 것이다.

 

이렇게 생산하는 부분이 완료되었다.

 

이제 이 notifyListeners()가 호출되게 만들어보자.

 

이제 이 데이터를 소비하는 부분을 알아보자.

 

3. Provider의 소비 watch, read (소비 1)

 

크게 context.watch <T>()와 context.read <T>() 두 가지 데이터 사용법이 있다.

 

비슷하지만 이 둘은 차이점이 있다.

 

context.watch <T>()는 T의 데이터  값이 변경되었을 위젯을 재 빌드하고,

 

context.read <T>()는 T의 데이터  값이 변경되었을 위젯을 재 빌드하지 않는다.

 

전자는 데이터 값을 주로 화면에 보여주기 위해 사용하고, (재빌드하여 변경된 값을 보여주어야 하기 때문에)

 

후자는 T의 데이터 변경하는 등의 이벤트들을 위한 용도로 사용한다(버튼) (T의 데이터 값의 변경에 따른 거기 때문에 재 빌드 필요가 없음)

 

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Counter'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text(
            '현재 숫자: ${context.watch<Counter>().count}', // watch 사용.
          ),
          onPressed: () {
            context.read<Counter>().add(); // read 사용.
          },
        ),
      ),
    );
  }
}

ElevatedButton을 클릭하면 현재 숫자가 증가하여 숫자가 변하면서 재빌드되기 때문에 watch를 사용했고,

 

버튼을 누르면 이벤트로 인한 재빌드는 없기 때문에 read를 사용했다.

 

4. Provider의 소비 Provider.of(context) (소비 2)

 

위에 read와 watch 말고 한 가지 방법이 더 있는데

 

기능상으로 차이점은 없다.

 

바로 Provider.of(context)이다. 

 

context.watch<T>()는 Provider.of<T>(context)와 동일하고, 

 

context.read<T>()는 Provider.of<T>(context, listen: false)와 동일하다.

 

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Counter'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text(
            '현재 숫자: ${Provider.of<Counter>(context).count}', // Provider.of<Counter>(context) 사용.
          ),
          onPressed: () {
           Provider.of<Counter>(context, listen: false).increment(); // Provider.of<Counter>(context, listen: false) 사용.
          },
        ),
      ),
    );
  }
}

이렇게 사용해도 전과 차이점이 없다.

 

예전에는 특정 조건에서 read와 watch를 사용하지 못한다는 단점이 있었는데,

 

지금은 4.3.3 버전 업데이트 이후부터는 없어졌다고 한다.

 

즉, watch, read, Provider.of(context)를 모든 조건에서 사용 가능하다는 것이다.

 

무엇을 사용하든 편한 것을 사용하면 될 것 같다.

 

5. Consumer(생산, 소비)

 

Consumer는 context.watch<T>()context.read<T>() Provider.of(context) 모두를 사용할 수 없는 상태에서

 

사용한다.

 

그런 경우는 언제일까?

 

하나의 빌드 메서드에서 Provider를 생성하는 동시에 소비도 같이 해야 하는 상황이다.

 

Consumer를 사용하지 않고 생성과 소비를 하면 컴파일 에러가 생긴다.

 

아래와 같이 Consumer를 사용해보겠다.

 

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<Counter>(
      create: (_) => Counter(),
      child: MaterialApp(
        title: 'Counter',
        home: Scaffold(
          appBar: AppBar(
            title: Text('Counter'),
          ),
          body: Center(
            child: Consumer<Counter>( // Consumer를 사용하여 ElevatedButton을 감쌌다.
              builder: (context, provider, child) => ElevatedButton(
                child: Text(
                  '현재 숫자: ${provider.count}',
                ),
                onPressed: () {
                  counter.add();
                },
              ),
            ),
          ),
        ),
      ),
    );
  }
}

이런 식으로 Consumer로 감싸서  생산과 소비를 같이 할 수 있게 되었다.

 

이렇게 Provider와 Consumer에 대해 정리를 해보았다.

 

https://github.com/qjsqjsaos/provider_example

 

qjsqjsaos/provider_example

Provider 플러그인 예제. Contribute to qjsqjsaos/provider_example development by creating an account on GitHub.

github.com

 

728x90
반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading