Flutter - Provider 사용 방법 및 사용 예시 (자동 리빌드, 상태 관리 및 변경 서포터 패키지 설명)

반응형

Overview

Provider 패키지를 사용하여 StatefulWidget의 상태 관리를 편리하게 할 수 있습니다.

아래의 예시는 플러터 provider 패키지의 공식 가이드 문서를 참고하여 작성하였습니다.

또한, 소스 코드는 글쓴이의 취향에 맞게 수정되어 작성되었음을 알려드립니다.


Provider 패키지란?

  • 상속된 위젯들을 감싸는 래퍼
  • 애플리케이션에서 데이터의 상태를 관리하고 공유하기 위한 기능을 제공하는 패키지
  • 상태를 사용하는 위젯을 자동으로 재빌드

Provider 패키지 추가하기

특정 버전으로 추가

  • (프로젝트 디렉토리 내) pubspec.yaml 파일의 dependencies 아래 코드 추가
  • 컨트롤 + S (파일 저장)을 눌러 의존성 주입
dependencies:
  flutter:
    sdk: flutter
  provider: ^6.0.5 # 버전 입력

최신 안정 버전으로 추가

flutter pub add provider

프로바이더의 값 읽고 불러오기

<T> 객체란?

  • 특정한 타입을 일반화하여 나타내는 용어
  • T는 Type의 약자로 실제로 어떤 타입인지는 사용하는 시점에서 결정
    • 예시) int의 경우 T타입 객체는 int 타입의 값을 가지는 변수나 객체를 의미

context.watch<T>()

  • 객체의 상태가 변경될 때 위젯을 다시 빌드하는 데 사용
  • ChangeNotifier를 사용하여 상태가 변경될 때 위젯을 다시 렌더링하여 새로운 상태 반영

context.read<T>()

  • 객체의 데이터를 읽어옴
  • 개체의 상태 변경을 감지하지 않고, 현재 상태를 한번만 읽어오는 데 사용
  • 상태 변경에 따라 위젯을 다시 빌드할 필요가 없는 경우 유용
    • 초기 설정, 특정 작업에서 한 번만 객체의 값을 읽어올 때 등

context.select<T, R>(R cb(T value))

  • 객체의 일부분만 관리할 경우 사용
  • 'cb'라는 콜백 함수를 사용하여 객체 특정 속성을 선택하고 해당 속성만을 관리
  • 전체 객체를 읽어올 필요 없이 선택한 속성에만 의존할 수 있으며, 성능 최적화에 유용
  • 객체가 많은 속성을 가질 경우, 필요한 속성만 확인하여 해당 속성이 변경될 때에만 위젯을 다시 렌더링 함

Provider.of<T>(context, listen: false)

  • Provider로부터 값을 읽어오는 메서드
  • listen: false를 사용하면 위젯은 한 번 값을 읽고나면 다시 렌더링 하지 않음
  • 위젯은 값을 감지하지 않기 떄문에 상태 변경시 값을 업데이트하려면 수동으로 setStaete 함수를 호출하여야 함

사용 예시

메인 함수에 프로바이더 설정

  • CountUpdator은 사용자가 지정한 프로바이더 클래스명임
  • 상태 변경을 위한 로직을 사용자가 정의하고 사용하겠다는 의미
void main() {
  runApp(
    MaterialApp(
      // 멀티 프로바이더 정의
      home: MultiProvider(
        providers: [
          // CountUpdator : 카운트를 증가시키고 업데이트할 Provider Class
          ChangeNotifierProvider(create: (_) => CountUpdator()),
        ],
        // 프로바이더를 제공 받을 페이지
        child: const ProviderTest(),
      ),
    ),
  );
}

Provider 상태 변경 클래스 정의 (CountUpdator)

  • CountUpdator 클래스는 사용자가 임의로 지정한 이름임
  • 해당 클래스의 함수가 호출되면 해당 변수는 자동으로 리빌드되어 상태 변경 됨
// 상태 변경을 위한 Provider 클래스 정의
class CountUpdator extends ChangeNotifier {
  // 숫자 카운트를 위한 변수 초기화
  int _cnt = 0;

  // 카운트 된 값을 cnt 변수에 저장
  int get cnt => _cnt;

  void updateCount(int number) {
    // 함수가 호출되면 카운트 + 1
    _cnt = number + 1;
    // 상태가 변경 업데이트
    notifyListeners();
  }
}

메인 페이지(ProviderTest)

  • ProviderTest 클래스는 사용자가 임의로 지정한 이름임
  • 플로팅 액션 버튼을 클릭시 프로바이더 클래스의 CountUpdator 함수를 통해 카운트 +1
    • 원래 구문 : context.read<CountUpdator>().updateCount(param)
    • onPressed 핸들러를 위한 변경 구문 : Provider.of<CountUpdator><context, listen: false).updateCount(param)
  • 텍스트 위젯에서 카운트된 값 출력
    • 구문 : context.watch<CountUpdator>().cnt
class ProviderTest extends StatefulWidget {
  const ProviderTest({super.key});

  @override
  State<ProviderTest> createState() => _ProviderState();
}

class _ProviderState extends State<ProviderTest> {
  // 카운트 초기 값 설정
  int countNumber = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Builder(builder: (context) {
        countNumber = context.watch<CountUpdator>().cnt;
        return Center(
          child: Text(
            //'${context.watch<CountUpdator>().cnt}',
            countNumber.toString(),
            style: const TextStyle(
              fontSize: 50,
              fontWeight: FontWeight.bold,
            ),
          ),
        );
      }),
      // 클릭시 카운트가 +1씩 되는 플로팅 액션 버튼
      floatingActionButton: FloatingActionButton(
        // Provider 데이터 저장
        // 기존 구문 : context.read<클래스명>().함수명(파라미터명)
        // onPressed 핸들러는 위젯 트리 외부에서 실행되므로 Provider 사용을 위한 값 설정 (listen:false)
        onPressed: () => Provider.of<CountUpdator>(context, listen: false)
            .updateCount(countNumber),
        tooltip: '카운트+1',
        child: const Icon(Icons.add),
      ),
    );
  }
}

전체 소스 코드

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(
    MaterialApp(
      // 멀티 프로바이더 정의
      home: MultiProvider(
        providers: [
          // CountUpdator : 카운트를 증가시키고 업데이트할 Provider Class
          ChangeNotifierProvider(create: (_) => CountUpdator()),
        ],
        // 프로바이더를 제공 받을 페이지
        child: const ProviderTest(),
      ),
    ),
  );
}

// 상태 변경을 위한 Provider 클래스 정의
class CountUpdator extends ChangeNotifier {
  // 숫자 카운트를 위한 변수 초기화
  int _cnt = 0;

  // 카운트 된 값을 cnt 변수에 저장
  int get cnt => _cnt;

  void updateCount(int number) {
    // 함수가 호출되면 카운트 + 1
    _cnt = number + 1;
    // 상태가 변경 업데이트
    notifyListeners();
  }
}

class ProviderTest extends StatefulWidget {
  const ProviderTest({super.key});

  @override
  State<ProviderTest> createState() => _ProviderState();
}

class _ProviderState extends State<ProviderTest> {
  // 카운트 초기 값 설정
  int countNumber = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Builder(builder: (context) {
        countNumber = context.watch<CountUpdator>().cnt;
        return Center(
          child: Text(
            //'${context.watch<CountUpdator>().cnt}',
            countNumber.toString(),
            style: const TextStyle(
              fontSize: 50,
              fontWeight: FontWeight.bold,
            ),
          ),
        );
      }),
      // 클릭시 카운트가 +1씩 되는 플로팅 액션 버튼
      floatingActionButton: FloatingActionButton(
        // Provider 데이터 저장
        // 기존 구문 : context.read<클래스명>().함수명(파라미터명)
        // onPressed 핸들러는 위젯 트리 외부에서 실행되므로 Provider 사용을 위한 값 설정 (listen:false)
        onPressed: () => Provider.of<CountUpdator>(context, listen: false)
            .updateCount(countNumber),
        tooltip: '카운트+1',
        child: const Icon(Icons.add),
      ),
    );
  }
}

참고

Flutter - Package - provider

반응형