[flutter] Provider로 상태관리

JunKim.dev·2023년 2월 10일
0

Flutter

목록 보기
3/5
post-thumbnail

0. Flutter 상태관리

앱 개발을 하다보면 (물론 다른 코딩도...) UI 와 로직들이 얽히고 설키는 상황들이 많다. 이것을 어떻게 가독성 있게 풀어내느냐가 개발자의 실력...
이거를 해결하기 위해서 구글 개발자가 만든 BLoC 패턴 이 각광을 받았었지만, 진입장벽도 높고 한 로직마다 4개의 클래스를 구현해야 한다는 점이 아쉬웠다.

이를 해소시켜준 것이 Provider 인데, BLoC 패턴 보다 더 쉽게 적용하고 로직 간의 분리도 간단하다.

1. pub.dev

provider 4.3.2 | Flutter Package

1-1 패키지 설치

$ flutter pub add provider

or
pubspec.yaml 파일 dependencies 에 추가해준다.
(이후 터미널에서 flutter pub get 명령어 실행 추천)

dependencies:
  provider: ^4.3.2

2. Provider 구현하기

2-1. 파일 구조

lib/provider 하위에 Provider 생성
파일 이름은 {로직명}_provider.dart 로 통일 권장

lib
├── main.dart
├── provider
│   └── count_provider.dart
└── screens
    ...

2-2. Provider 생성

extends ChangeNotifier 를 상속받아 클래스를 생성해준다.

import 'package:flutter/material.dart';

class CounterProvider extends ChangeNotifier {
  ...
}

Provider 의 가장 Key 포인트는 notifyListeners(); 인데, 이것을 호출해야 해당 Provider 를 구독하고 있던 State 들이 새로운 값으로 변경을 한다.

class CounterProvider extends ChangeNotifier {
  int _count = 0; // 상태

  int get count => _count;

  add() {
    _count++; //상태 변경
    notifyListeners(); // 상태 변경 된 것을 알림
  }

  decrese() {
    _count--; //상태 변경
    notifyListeners(); // 상태 변경 된 것을 알림
  }
}

2-3. Provider 선언

Provider 를 선언한 순간 그 Provider 에 속한 children 모두 Provider 에 접근이 가능하다. 선언 방식에도 단일/다중에 따라 나눠진다.

단일 Provider

ChangeNotifierProvider 에서는 한개의 Provider 를 선언이 가능하다. 이제 child 위젯에서 선언된 Provider 를 사용할 수 있다.

MaterialApp(
  title: 'Flutter Provider Demo',
  home: ChangeNotifierProvider(
    create: (BuildContext context) => CounterProvider(),
    child: Home(),
  ),
);

다중 Provider

MultiProvider 에서는 여러 개의 ChangeNotifierProvider 를 선언이 가능하다.

MaterialApp(
  title: 'Flutter Provider Demo',
  home: MultiProvider(
    providers: [
      ChangeNotifierProvider(
        create: (BuildContext context) => CounterProvider(),
      ),
      ChangeNotifierProvider(
        create: (BuildContext context) => TodoProvider(),
      ),
    ],
    child: Home(),
  ),
);

2-4. Provider 사용하기

해당 상태를 UI에 연동하는 방식에서 2가지가 있다. 각각의 목적이 다르니, 고민해서 활용하자.

Provider.of

Provider 에 직접 접근해서 받아오는 방식이다. 주의할 점은 이렇게 받아오면 현 위젯 전체가 리로드 된다. 가벼운 위젯이라면 상관없다. 현재 위젯 전체라는 것이 애매모호 하다면, 다음 방식을 보자.


Widget build(BuildContext context) {
  CounterProvider counter = Provider.of<CounterProvider>(context);
  return Center(
    child: Text(
      counter.count.toString(),
    ),
  );
}

Consumer

상태가 변경이 되면 Center 를 제외한 builder 부분만 리로드 된다. Provider.of 와는 성능상 같은 역할이지만, 전체 위젯이 아니라 부분부분의 위젯만 변경시키기에 활용에 따라 퍼포먼스 차이가 크기 난다.

return Center(
  child: Consumer<CounterProvider>(
  	builder: (context, provider, child) {
      return Text(
        provider.count.toString(),
      );
    },
  ),
);

builder 부분에 보면 3가지 인자가 있는데, 마지막 child 를 활용하면 더 좋은 코드를 만들 수 있다. childProvider 에서 변경이 생겨도 다시 리빌드 하지 않도록 구성할 수 있다. Consumerchild 를 지정해주면 된다.

이러면 provider 가 변해도 Text('hello') 는 다시 빌드하지 않는다. 이러한 사소한 차이들이 모여서 프로젝트 성능을 좌우하니, 알아두면 좋은 팁인듯 하다.

return Center(
  child: Consumer<CounterProvider>(
    builder: (context, provider, child) {
      return Column(
        children: [
          Text(
            provider.count.toString(),
          ),
          Container(child: child),
        ],
      );
    },
    child: const Text('hello'),
  ),
);
profile
아는 코드도 다시보자 🫠

0개의 댓글