[Flutter] Provider로 전역상태관리하기 📱

dosilv·2021년 7월 21일
0
post-thumbnail

🦋 Provider란?

React로 치면 Redux, Recoil 같은 도구!
StatefulWidget의 setState로도 state 변수를 관리할 수 있지만, 다른 위젯에서 해당 state값을 사용하려면 위젯에서 위젯으로 콜백을 넘겨줘야 하고... 복잡...😵 그래서 보다 편리한 전역 상태관리를 위해 Provider를 이용할 수 있다. Bloc패턴, getX 등 다른 방법도 있지만 가장 쉽고 직관적인 Provider를 학습해 봄!



🦋 구현해 본 것

앱 하단에 컬러 선택 버튼을 만들고, Color 타입의 state 변수를 만들어서 provider로 이를 관리하며 테마 색을 바꿔 보았당. (+다크모드)



🦋 설치

설치 명령어: flutter pub add provider



🦋 사용 방법

🌈 ChangeNotifier로 state 변수 생성하기

우선 lib 디렉터리 안에 provider 디렉터리를 따로 만들어 myProvider.dart 파일을 생성했다. state가 많아지면 provider 파일 또한 주제별로 분리하는 게 좋겠지만, 일단 연습용이기 때문에..!

그리고 myProvider.dart 파일을 열어 state 변수를 담아 놓을 클래스를 생성하는데, 이때 ChangeNotifier를 상속(extends 키워드 이용)받거나 믹스인으로 추가(with 키워드 이용)해야 함!
_color라는 private 변수를 만들고, 외부에서 꺼내 써야 하니까 getter도 만들었다.

class ThemeColor with ChangeNotifier {
  Color _color = Color.fromRGBO(80, 80, 80, 1);
  Color get color => _color;
}

그리고 같은 클래스 내에 state 값을 변경하기 위한 함수를 정의해 준다.

  void changeColor(Color color) {
    _color = color;
    notifyListeners();
  }

tap에 따라 테마 색상을 바꿀 거기 때문에, 미리 Color 타입 변수를 만들어 테마 색상을 몇 가지 정의해 놓고 이용했다.

  Color red = Color.fromRGBO(237, 115, 88, 1);
  Color yellow = Color.fromRGBO(242, 220, 139, 1);
  Color green = Color.fromRGBO(172, 201, 131, 1);
  Color blue = Color.fromRGBO(134, 197, 207, 1);
  Color purple = Color.fromRGBO(123, 113, 209, 1);

그렇게 완성된 myProvider.dart 파일



🌈 MultiProvider & ChangeNotifierProvider로 변수 전달하기

이제 해당 state를 사용할 파일(main.dart)로 넘어가서! 변수와 메서드를 사용할 수 있도록 세팅을 해 줘야 한다. 그 전에 provider패키지&myProvider 파일 임포트 하기~~

import 'package:provider/provider.dart';
import '../../provider/myProvider.dart';

원래 main.dart 내부의 기본 main() 함수는 아래처럼 runApp 메서드의 콜백으로 최상단 위젯(MyApp)을 전달하는 형태이다.

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

provider를 사용하기 위해서는, 여기서 최상단 위젯인 MyApp을 MultiProvider로 감싸줘야 함! 사실 지금은 ChangeNotifierProvider로 전체를 감싸도 되지만... 상태 관리할 provider class가 두 개 이상이라면 멀티프로바이더를 사용해야 한다.

구체적으로 MultiProvider에는
1. 필수 프로퍼티인 providersChageNotifierProvider<클래스명>(create: (_) => 클래스명())의 형태로 필요한 ChangeNotifierProvider의 리스트를 전달해야 한다.
2. child에 최상단 위젯(MyApp())을 넣는다.

void main() {
  runApp(MultiProvider(providers: [
    ChangeNotifierProvider<ThemeColor>(create: (_) => ThemeColor())
  ], child: MyApp()));
}


🌈 context.watch/read로 변수 사용하기

이제 MyApp() 내에서 ThemeColor내부의 변수 및 메서드를 사용할 수 있다!

state값이 변할 때마다 반영할 곳에는 context.watch<클래스명>().변수명으로 state 변수를 넣어 준다. 나는 AppBar의 색이 되는 ThemeDataprimaryColor 프로퍼티 값으로 color를 적용해 줌!

Widget build(BuildContext context) {
  return MaterialApp(
      title: 'Let\'s Learn Flutter 📱',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
          primaryColor: context.watch<ThemeColor>().color,
          ...
          )
        ...

그리고 state값 조작이 필요한 곳에는 context.read<클래스명>().메서드명([인자]) 형태로 onTap 등의 콜백으로 전달한다.

나는 컬러 수만큼 GesturedDetector 위젯으로 버튼을 만들어 줬는데, 반복을 줄이기 위해서 컬러 리스트를 정의하고 map함수를 이용해 다음과 같이 코드를 짰다.

class MyApp extends StatelessWidget {
  final colorList = <Color>[
    ThemeColor().red,
    ThemeColor().yellow,
    ThemeColor().green,
    ThemeColor().blue,
    ThemeColor().purple
  ];
  ...

🔺 컬러 리스트 선언


persistentFooterButtons: colorList
    .map<Widget>(
      (color) => GestureDetector(
        onTap: () {
          context.read<ThemeColor>().changeColor(color);
        },
        child: Container(
          width: 20,
          height: 20,
          decoration:
              BoxDecoration(shape: BoxShape.circle, color: color),
        ),
      ),
    )
    .toList()

🔺 context.read & map 사용

이번에 알게된 map 사용시 주의할 점!
children처럼 위젯 리스트가 필요한 곳에서 map을 쓰려면, .toList()를 꼭 붙여줘야 한다. 맵의 결과는 리스트가 아닌 Iterable이기 때문에 타입 불일치 오류를 방지하려면 리스트 형태로 변환이 필요하기 때문에..!
그리고 map 뒤에도 <Widget>이라고 타입을 명시해 주면 타입 추론으로 발생하는 오류도 방지할 수 있음!




플러터....... 새롭고 어렵지만 재밌당

🧚🏻 Reference

https://dev-yakuza.posstree.com/ko/flutter/provider/
https://terry1213.github.io/flutter/flutter-provider/
https://dev.to/theotherdevs/starting-with-flutter-a-simple-guide-for-provider-247o

profile
DevelOpErUN 성장일기🌈

0개의 댓글