본문 바로가기

Flutter  학습/플러터 지식들

Provider

Provider란?

앱이 규모가 커지고 한 페이지 내에 UI와 데이터 모두 관리한다면 코드는 복잡해진다. 그렇게 되면 가독성과, 유지보수 모두 힘들어진다. 

이를 해결하기 위해 나온 것이 Provider와 Bloc 패턴이다.

 

Provider는 디자인 패턴으로 데이터 공유나 로직을 분리하는데 좀 더 용이합니다. 구글에서는 Flutter 개발 시 Bloc 패턴을 권장합니다. 그런데 간단한 로직 하나 구현하는데도 패턴을 적용하면 최소 4개의 클래스를 작성해야하는 불편함이 있습니다.

 

Provider는 Bloc보다는 쉽게 패턴을 적용할 수 있습니다. 즉, 데이터 공유와 로직의 분리가 좀 더 쉬워집니다.

 

Provider를 사용해야 하는 이유

  • 관심사의 분리 : 한 클래스에서 여러 기능이 집중되어 있는 경우가 있습니다. 클래스에 기능이 여러개 있다면 클래스가 커지고 관리가 어렵습니다. 클래스가 하나의 역할을 하게 하는 것. 
  • 데이터의 공유 : 여러 페이지에서 하나의 데이터를 공유할 때 편하게 구현 가능
  • 간결한 코드 제공 : Bloc 패턴에 비하면 코드가 좀 더 간결해 집니다. 이건 직접 사용해 보면서 느낄 수 있겠네요.

즉, 중규모 프로젝트에서는 Provider 패턴, 대규모 프로젝트는 BLOC를 사용하는게 좋습니다.

 

Provider 예제 : 

1. 먼저 pubspec.yaml 파일에 추가를 합니다.

 

글 작성 기준으로 6.0.5가 최신이고 가장 최신거는 링크에서 확인 ( https://pub.dev/packages/provider/install )

dependencies:
  provider: ^6.0.5

 

 

2. CountingStarProvider.dart

import 'package:flutter/material.dart';

class CountingStarProvider extends ChangeNotifier {
  int _star = 0;

  int get star => _star;

  void increase() {
    ++_star;
    notifyListeners();
  }

  void decrease() {
    --_star;
    notifyListeners();
  }
}

 

  • ChangeNotifier 상속 받이 상태 관리한다.
    • 즉, ChangeNotifier를 상속 받은 class 안의 변수가 List, Array 등등의 정보를 실시간 상태관리(업데이트) 할수 있다.
    • 이것을 바탕으로 바로 데이터 정보를 업데이트 할수 있다.
  • 상태 변경후 notifyListeners()를 사용해 열려줘야 한다.

 

3. provider_example.dart

import 'package:flutter/material.dart';
import 'package:practice/provider_example/counting_star_provider.dart';
import 'package:provider/provider.dart';

class ProviderExample extends StatelessWidget {
  late CountingStarProvider _countintgStarProvider;

  @override
  Widget build(BuildContext context) {
    _countintgStarProvider =
        Provider.of<CountingStarProvider>(context, listen: false);

    return Scaffold(
      appBar: AppBar(
        title: Text('provider sample'),
      ),
      body: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          IconButton(
            onPressed: () => _countintgStarProvider.increase(),
            icon: Icon(Icons.add),
          ),
          Container(
            child: Consumer<CountingStarProvider>(
              builder: (context, provider, child) => Text(
                provider.star.toString(),
                style: TextStyle(fontSize: 60),
              ),
            ),
          ),
          IconButton(
            onPressed: () => _countintgStarProvider.decrease(),
            icon: Icon(Icons.remove),
          ),
        ],
      ),
    );
  }
}

 

  • Provider 불러오기
    • CountingStarProvider _countintgStarProvider = Provider.of<CountingStarProvider>(context, listen: false);
    • UI를 변경하지 않는곳에서는 listen : false, 그게 아니면 이부분은 생략하면 알아서 true로 된다.
  • Comsumer
    • provider.of 와 Consumer는 성능상으로 완전히 똑같다. 다만 Consumer는 사용하기에 따라 of.context보다 더 퍼포먼스를 높일 수도 있다. provider.of는 notifyLisiteners() 가 실행되면 위젯 전체가 rebuild된다. 그러나 Consumer는 해당 위젯에서 일부부만 rebuild되도록 설정할 수 있는 것이다.
    • 만약에 큰 위젯이 존재하면, 따로 쪼개서 provider.of를 사용하던가 Consumer를 사용해서 해당 부분만 rebuild되게 하면 되는 것이다.
    • 일부분만 rebuild 되게 하려면 provider.of를 불러오고 listen: false 로 구독은 취소해두고, 일부분만 Consumer를 사용하면 된다.
    • Comsumer는 3개의 인수를 받는데 context, provider, child
      • context : build가 호출되고 난 후 만들어지는 context는 Scaffold의 부모 위젯의 BuildContext이다.  BuildContext는 Widget tree 에서 현재 Widget의 위치 를 알 수 있는 정보라고 함.
      • provider :  ChangeNotifier를 상속했던 provider 객체를 의미한다. 이번 예제에서는 CountingStarProvider.dart
      • child : 서브트리 위젯입니다. 이를 이용해서 다시 서브트리를 작성하지 않고 재활용할 수 있습니다. 즉 최적화를 하는데 유용하게 사용할 수 있습니다. 아래는 해당 코드입니다.

 

4. main.dart

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

import 'provider_example/counting_star_provider.dart';
import 'provider_example/provider_example.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: ChangeNotifierProvider(
        create: (_) => CountingStarProvider(),
        child: ProviderExample(),
      ),
    );
  }
}
  • ChangeNotifierProvider를 이용해 변화에 대해 구독한다

 

해당 예제는 하나만 구독한 상태이지만 여러개를 하려면 MultiProvider를 이용해 할수 있다. (나중에 좀더 추가할 예정)

MultiProvider(
	// MultiProvider를 통해 변화에 대해 구독
	providers: [
		...
	],
	child:
		...           
 );