코딩

Provider 패턴

코딩하는후운 2022. 10. 20. 15:31
반응형

Provider 패턴


프로바이더 패턴을 쓰는 이유는?
a.관심사의 분리
관심사의 분리는 디자인 원칙의 하나입니다.
보통 관심사는 어떤 코드가 하는 일을 말합니다.
UI를 담당하는 코드, 네트워크를 담당하는 코드, 데이터를 담당하는 코드 등

보통은 한 클래스가 여러 역할을 할 수록, 클래스가 커지고 관리가 어렵게 됩니다.
따라서 클래스가 하나의 역할(관심)만 갖도록, 클래스를 나눈다.

이게 바로 관심사의 분리이다.

Provider나 Bloc패턴을 쓰는 이유는 관심사의 분리를 위해서이다.

b. 데이터의 공유
하나의 데이터를 여러 페이지에서 공유하고 싶을 때가 있다.
ex) 유저 인증 정

Provider패턴을 쓰면 데이터 공유를 쉽게 할 수 있습니다.

c.좀 더 간결한 코드
Bloc패턴의 경우 클래스들을 역할 별로 나누는 데는 좋지만 코드 자체가 복잡해 지는 경향이 있다.

Provider패턴을 쓰면 좀 더 적은 코드로 클래스들을 구분해서 쓸 수 있죠.

구글에서도 중규모 프로젝트는 Provider패턴을, 대규모 프로젝트는 Bloc패턴을 추천하고 있다.


1.프로바이더의 구조 - 데이터 생산과 소비
Provider는 데이터를 생산하고, 소비하는 2부분으로 되어 있습니다.
어떤 데이터를 생산하느냐에 따라 Provider의 종류가 달라지죠.
그냥 Provider가 되기도 하고 StreamProvider가 되기도 하고 합니다.

라이브러리 추가
pubspec.yaml
provider: ^3.0.0+1

1.1 Provider 데이터 생산하기

Provider<int>.value(  //int 타입 사용
  value: 5,
  child: Container(),
)


데이터를 생산할 때는 꼭 데이터 타입을 적어줍니다.
여기선 int 형태로 데이터를 만들었죠.

1.2 Provider 데이터 소비하기

var data = Provider.of<int>(context) //int 타입 사용. Provider를 만들 때 int 타입을 썻기에..



Provider에서 만든 데이터를 쓰려면
Provider.of(context)나 Consumer()위젯을 사용합니다.

저는 Provider.of(context)가 더 쓰기 편해 이쪽을 더 선호합니다.


예제 1.1 - 기본 Provider사용해보기 : 데이터 생산

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

class SimpleApp extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    return Provider<int>.value( //Provider로 위젯을 감싸면, 자식 위젯에서 그 값을 사용 가능.
      value: 5, //Provider에서 제공하는 값은 5
      child: MaterialApp(
        home: SimplePage(),
      ),
    );
  }
}


Provider를 쓰려면 부모 위젯을 Provider로 감싸줘야합니다.
전체 앱에서 사용하고 싶으면 MaterialApp을 감싸면 됩니다.

예제 1.2 - 간단히 Provider 사용해보기 : 데이터 소비

class SimplePage extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    var data = Provider.of<int>(context); //가까운 Provider로 부터 값을 가져옵니다.

    return Scaffold(
      appBar: AppBar(
        title: Text('Simple App'),
      ),
      body:  Center(
        child: Text('${data}'), //값이 표시 됩니다.(여기서는 5)
      ),
    );
  }
}



Provider.of(context)함수를 통해 데이터를 가져옵니다.


2. ChangeNotifierProvider 사용하기 - 변하는 값 처리하기
UI에 있는 값이 변했을 때, UI를 변경해줘야 합니다.
이를 setState()로 할 수도 있겠지만
ChangeNotifier를 쓰면 같은 일을 할 수 있습니다.

ChangeNotifier를 믹스인(mixin)한 클래스는
notifyListener()함수를 부를 수 있습니다.
이 함수를 쓰면 UI가 업데이트 됩니다.

예제 2.1 - Counter클래스

import 'package:flutter/material.dart';

class Counter with ChangeNotifier{
  int _counter;

  Counter(this._counter);

  getCounter() => _counter;
  setCounter(int counter) => _counter = counter;

  void increment(){
    _counter++;
    notifyListeners();  //값이 변할 때마다 플러터 프레임워크에 알려줍니다.
  }

  void decrement(){
    _counter--;
    notifyListeners();  //값이 변할 때마다 플러터 프레임워크에 알려줍니다.
  }
}



Counter클래스를 Provider에 넣어볼것이다.
Counter클래스가 ChangeNotifier를 믹스인 했기에 ChangeNotifierProvider를 사용해줘야 합니다.

예제2.2 - CounterApp.
ChangeNotifierProvider로 위젯을 감싸줍니다.- 데이터 사용할 준비하기

class CounterApp extends StatelessWidget{
  @override
  Widget build(BuildContext context){
    return ChangeNotifierProvider<Counter>(//Counter 타입 사용. Counter클래스의 데이터가 변하는지 보고 있다가 변하면 알려줍니다.
      builder: (_) => Counter(0), //초기값 정하기
      child: MaterialApp(
        title: 'Flutter Value',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: HomePage(),
      ),
    );
  }
}



예제 2.3 - ChangeNotifierProvider의 값 사용하기

class HomePage extends StatefulWidget{
  HomePage({Key key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>{
  @override
  Widget build(BuildContext context){
    final counter = Provider.of<Counter>(context);  //Counter타입의 데이터를 가져옴.

    return Scaffold(
      appBar: AppBar(
        title: Text("provider demo"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              '${counter.getCounter()}',
              style: Theme.of(context.textTheme.display1,
            ),
            RaisedButton(
              onPressed: openFirstPage,
              child: Text('first paget'),
            ),
            RaisedButton(
              onPressed: openSecondPage,
              child: Text('second page'),
            ),
          ],
        ),
      ),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          FloatingActionButton(
            onPressed: counter.increment, //버튼 누를 때마다, 값이 증가. notifyListeners()가 호출 되기에 UI가 갱신.
            tooltip: 'Increment',
            child: Icon(Icons.add),
            heroTag: null,
          )
        ],
      ),
    );
  }

  //다른 페이지로 이동 합니다. 이동한 페이지에서도 Counter의 값은 동일합니다.
  Future openFirstPage(){
    return Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => FirstPage()),
    );
  }
}

 

참조 :

https://software-creator.tistory.com/26?category=681555

반응형

'코딩' 카테고리의 다른 글

Bloc Pattern  (0) 2022.10.20
팩토리 패턴(Factory pattern)  (0) 2022.10.20
빌더 패턴 (Builder pattern)  (0) 2022.10.20