Develop/Flutter

[Flutter] 플러터 ChangeNotifierProvider를 통해 Provider 구현

JunJangE 2021. 12. 22. 10:50

이번에는 ChangeNotifierProvider를 통해 Provider를 구현해보자.

ChangeNotifier는 Flutter SDK에 포함된 클래스로 청취자에게 변경 알림을 제공해주는 것이다. 즉, ChangeNotifier를 통해 변화에 대해 구독할 수 있다. 

Provider는 이전에 간단하게 알아보았으므로 구현하는 방법에 대해서 바로 알아보자.

 

[Flutter] 플러터 Bloc 패턴과 Provider 패턴

Flutter 앱을 개발하다 보면 자주 등장하는 Bloc 패턴과 Provider 패턴에 대해서 간단하게 알아보자. Bloc 패턴은 Google 개발자가 권장하는 Flutter의 상태 관리 시스템이다. 프로젝트를 관리하는데 도움

fre2-dom.tistory.com

라이브러리

아래 링크에 들어가 installing에 Provider 패키지 코드를 복사하여 pubspec.yaml 파일에 추가하여 다운로드한다.

 

provider | Flutter Package

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

pub.dev

dependencies:
  provider: ^6.0.1

코드의 이해를 돕기 위해 폴더의 위치를 다음과 같이 구성한다.

폴더 위치

폴더의 위치를 구성하고 Provider 패키지를 추가했다면 본격적으로 코드를 작성하도록 하자.

main.dart

import 'package:ev_app/src/provider/count_provider.dart';
import 'package:flutter/material.dart';
import 'package:ev_app/src/home.dart';
import 'package:provider/provider.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ChangeNotifierProvider(  // ChangeNotifierProvider를 통해 변화에 대해 구독(하나만 구독 가능)
        create: (BuildContext context) => CountProvider(), // count_provider.dart
        child: Home() // home.dart // child 하위에 모든 것들은 CountProvider에 접근 할 수 있다.
     )
    );
  }
}

ChangeNotifierProvider를 통해 변화에 대해 구독한다.(하나만 구독 가능) 

⇒ 여러 개를 구독하기 위해서는 MultProvider로 감싼 후 사용해야한다.

ChangeNotifierProvider를 통해 CountProvider()를 구독하는 것이고 CountProvider은 count_provider.dart에 생성된 것이다.

child 하위에 모든 것들은 CountProvider에 접근할 수 있고 Provider 선언한 위치에서 그 상위 Widget은 Provider에 접근할 수 없다.

home.dart

import 'package:ev_app/src/provider/count_provider.dart';
import 'package:flutter/material.dart';
import 'package:ev_app/src/ui/count_home_widget.dart';
import 'package:provider/provider.dart';

class Home extends StatelessWidget {

  late CountProvider _countProvider;

  @override
  Widget build(BuildContext context) {

    // Provider를 호출해 접근, listen: false를 통해 구독된 모든 위젯들에게 알림을 보내지 않게 한다.
    _countProvider = Provider.of<CountProvider>(context, listen: false);

    return Scaffold(

      appBar: AppBar(
        title: Text("Provider Sample"),
      ),
      body: CountHomeWidget(), // count_home_widget.dart
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
        IconButton(
          icon: Icon(Icons.add), 
        onPressed: () {
          _countProvider.add(); // 클릭 되었을 때 add()
        },),
        IconButton(icon: Icon(Icons.remove), 
        onPressed: () {
          _countProvider.remove(); // 클릭 되었을 때 remove()
        },)
        

      ],),
      
    );
  }
}

Provider를 호출해 접근한다. 

그 후 버튼을 눌렀을 때 더해지고 빼지는 연산을 구현한다.

count_home_widget.dart

import 'package:ev_app/src/provider/count_provider.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class CountHomeWidget extends StatelessWidget {
  const CountHomeWidget({ Key? key }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Consumer<CountProvider>( // Consumer를 활용해서 provider에 접근하여 데이터를 받아올 수 있다 
        builder: (context, provider, child) {
          return Text(
            provider.count.toString(), // count를 화면에 출력
          style: TextStyle(fontSize: 40.0),);
        }
      ), 
    );
  }
}

Consumer를 활용해서 provider에 접근하여 데이터를 받아올 수 있다. 

return Center(
      child: Text(
        Provider.of<CounterProvider>(context).count.toString(),
      ),
    );

 

Provide.of를 사용해서 CounterProvider로 직접 접근을 할 수 있으며 상태를 받아와 업데이트가 가능하다.

이렇게 위 코드로도 데이터를 받아올 수 있고 두 코드는 목적에 따라 활용해야 한다.

Provide.of를 사용하는 경우는 직접 접근해서 받아오는 경우로 현 위젯 전체가 리로드 되는 현상이 발생된다. 즉, 가벼운 위젯일 경우 바로 사용해도 무방하다.

Consumer를 사용하는 경우는 builder 부분만 호출되기 때문에 현 위젯이 리로드 되지 않는다. 즉, 여러 효과나 연산이 많이 있는 위젯일 경우는 Consumer를 사용하는 것이 좋을 것으로 보인다.

count_provider.dart

import 'package:flutter/material.dart';

// ChangeNotifier 상속 받이 상태 관리
// 이 안에 있는 맴버 변수 값들을 상태 관리 한다.
class CountProvider extends ChangeNotifier{
  int _count = 0;
  int get count => _count; // get 함수를 사용해 외부에서 접근이 가능하게 한다.

  // 더하기
  add(){
    _count++;
    notifyListeners(); // notifyListeners 호출해 업데이트된 값을 구독자에게 알림
  }

  // 빼기
  remove(){
    _count--;
    notifyListeners(); // notifyListeners 호출해 업데이트된 값을 구독자에게 알림
  }

}

 

Provider에서는 상태 관리를 직접 하지 않고 ChangeNotifier가 스스로 상태 관리를 한다.

get 함수를 통해 외부에서 _count를 접근 가능하게 한다. 

다음으로 더하기, 빼기 함수가 있는데 여기서 notifyListeners를 사용하지 않으면 업데이트된 값을 구독자에게 알리지 못하므로 반드시 작성해야 한다.

위 코드를 모두 작성했다면 다음 영상과 같이 실행되는 것을 확인할 수 있다.

<결과 영상>

사용자는 Provider를 통해 데이터 클래스를 생성 요청하고, 그 데이터를 사용하는 뷰에서 구독만 해주면 데이터 변화를 자동으로 알림 받을 수 있게 된다. 모든 로직은 Provider가 관리하고 UI는 보여주기만 하면 된다.

참고

 

provider | Flutter Package

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

pub.dev

 

[Flutter / 플러터] BloC 패턴이 있는데 왜? Provider 를 써야 할까?

안녕하세요 개발하는 남자입니다. 오늘은 플러터 포스팅의 2번째 게시글입니다. 첫 번째 포스팅에서 다뤄보았던 Bloc 패턴을 알아봤습니다. 혹시 Bloc 패턴에 대해 궁금하시다면? 아래 링크 클릭

sudarlife.tistory.com

github

 

GitHub - junjange/Flutter-Learning: 플러터 학습

플러터 학습. Contribute to junjange/Flutter-Learning development by creating an account on GitHub.

github.com