Study/Dart,Flutter

9. Flutter 상태관리법 Riverpod 0.14.0 그리고 firebase (2, ChangeNotifier)

코딩 잘 할거얌:) 2021. 11. 11. 01:21
반응형

이번 포스팅은 상태 관리법 Riverpod 0.14.0 버전을 어떻게 사용하는지 알아보도록 하자. 

쉬우니까 천천히 따라해보자 (아악!!)

목차

  1. Flutter의 상태관리 Riverpod 0.14.0
    1. Riverpod의 기본 사용법
      1. pubspec.yaml, import 그리고 main
    2. Riverpod ChangeNotifier

오늘은 Flutter의 상태 관리 중 Riverpod이다. 단순한 Riverpod사용법은 아래 링크에 아주 자세하게 되어있으니 한번 꼭 보도록 하고, 여기 포스팅에서는 Firebase의 RealtimeDatabase를 가져오는 코드로 설명하도록 하겠다.

https://www.youtube.com/watch?v=atwWbkBdepE&t=647s&pp=ugMICgJrbxABGAE%3D 

내가 이해한 것 그리고 내가 사용했던 코드들을 가지고 포스팅을 해보도록 하겠다. 차근차근 시작해보도록 하자.

 


1. Riverpod의 기본사용법

 

Riverpod 0.14.0의 사용법은 Provider의 ChangeNotifier를 이용한다.

상태 관리법을 찾게 되는 이유 중 하나는 아마도 Futurebuilder의 무분별한 사용과 관리하기 힘들 정도로 커져버린 경우가 있을 것이다.

tempFuturebuilder() {
    return FutureBuilder(
        future: _futureFunction(),
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          if (!snapshot.hasData) {
            return CupertinoActivityIndicator();
          } else {
            return Container();
          }
        });
  }

Futurebuilder로 작성을 하게 되면 statefulwidget상태에서 setState를 하여 화면을 rebuild 하는 경우에 관리하기가 상당히 복잡해진다. 상태 관리법으로 이제 상태를 제대로 관리해보자.

 

1. pubspec.yaml과 import

우선 riverpod을 적용해야 한다. 아래 공식 링크를 통해서 들어간다면 적용할 수 있다.

 

https://pub.dev/packages/flutter_riverpod/versions/0.14.0+3 

 

flutter_riverpod 0.14.0+3 | Flutter Package

A simple way to access state from anywhere in your application while robust and testable.

pub.dev

dependencies:
	flutter_riverpod: ^0.14.0+3

pubspec.yaml로 들어가서 다음과 같이 적는다. 그 후 ctrl+s 혹은 터미널에다가 flutter pub get을 치고 enter를 누르면 다운로드가 된다.

그리고 riverpod이 필요할 때마다 아래 import를 적으면 된다. 자동완성 기능이 있다면 굳이 적지 않아도 된다.

import 'package:flutter_riverpod/flutter_riverpod.dart';

마지막으로 main에다가 아래 코드처럼 적용해주어야 한다.

 

void main() {
//ProviderScope를 추가해주자.
	runApp(ProviderScope(child : MyApp());
    }

 

자 여기까지 했다면 riverpod을 사용하기 위한 기본적인 세팅은 끝이 났다. 이제 사용법을 알아보도록 하자.

https://riverpod.dev/docs/migration/0.13.0_to_0.14.0

 

^0.13.0 to ^0.14.0 | Riverpod

With the release of the version 0.14.0 of Riverpod, the syntax for using [StateNotifierProvider] changed

riverpod.dev

 

 

2. Riverpod ChangeNotifier

Future은 비동기식 표현법이다. 간단하게 짚고 넘어가 자면,

  //return type은 Future이라는 상자에 int의 DataType을 넣어서 반환한다.  
Future<int> returnFutureInt() {
  return Future<int>.delayed(Duration(seconds: 1), () {
    return 1;
  });
}

  function() async {
    int result = 0;
    try {
    //Future 기다리는 부분.
      result = await returnFutureInt();
    } catch (error) {
      //Future Error
      result = -1;
    } finally {
      //Future whenComplete
    }
  }

비동기 Future은 일반적으로 이렇게 사용한다. Future <dynamic>는 비동기로 진행이 되며, 사용자에게 '데이터가 완성될 때까지 Future라는 상자를 들고 있어!'라고 이야기를 하는 거와 같다. await 같은 경우, Future상자에 정상적인 반환이 끝날 때까지 기다려라는 뜻으로 이해하면 된다. await를 사용하기 위해서 async를 반드시 사용해야 한다.

 

riverpod 사용해보도록 하자. riverpod에서 하나의 상태를 구성하는 요소는 3가지로 나누어진다.

  1. provider 정의하는 부분
  2. 상태 관리를 어떻게 하는지 명시하는 ChangeNotifier부분
  3. 상태 관리와 Widget을 이어주는 ConsumerWidget부분

1. provider 정의하는 부분

Riverpod은 각각 상태를 관리하기 위해서는 하나하나 provider를 정의해주어야 하고, 이름을 다르게 해주어야 한다. 기존의 상태 관리 provider는 데이터 타입이 동일한 경우에 되지 않았지만, Riverpod은 데이터타입이 같더라도 이름을 다르게 정의하면 서로 다른 상태 관리가 된다.

final stateName =
    ChangeNotifierProvider<DataType>((ref) => DataType());
    
 //ex
 final riverpodProvider =
    ChangeNotifierProvider<PersonProvider>((ref) => PersonProvider());

이걸 작성하는 부분은 class 내부가 아닌 외부에 정의하여 전역 변수로 정의를 한다.

 

2. ChangeNotifier

ChangeNotifier부분은 데이터를 가져오고 가공하여 저장하는지 까지 모든 게 들어있는 부분이다.

 

import 'package:cakeorder/StateManagement/Riverpod/providerImplement.dart';
import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';

class Person {
  final name;
  final socialNumber;
  Person({this.name, this.socialNumber});
}

class PersonProvider with ChangeNotifier {
  final _personList = <Person>[];
  final _dropDownList = [];
  
  //bool type의 Fetch를 이용하여 데이터를 가져오는 중인지 체크한다.
  bool _isFetch = false;

  get isFetching => _isFetch;

  get getData => _personList;

  fetchData() async {
    if (_isFetch) return;

    _isFetch = true;
    _personList.clear();

    try {
      //Realtime Database에 있는 person Key-Value형식의 데이터를 가지고 옴.
      DataSnapshot dataSnapshot =
          await FirebaseDatabase.instance.reference().child("person").once();
          
      //Iterable에서 Map으로 변경한다.
      Map<String, dynamic> personMap =
          Map<String, dynamic>.from(dataSnapshot.value);
          
      //Person socialNumber(key)와 name(value)를 저장한다.
      personMap.forEach((keySocialNumber, valueName) {
        _personList.add(
            new Person(socialNumber: keySocialNumber, name: valueName));
      });
      
      
	//모든것이 완료되면 notifyListeners를 이용하여 연결되어있는 위젯을 업데이트 시켜준다.
    //setState와 비슷하다고 생각하면 된다.
    //만약 상태는 변경되었지만 notifyListeneres를 호출하지 않으면 위젯은 업데이트되지않는다.
      notifyListeners();
      
    } catch (error) {
    //에러가 발생하는 경우.
      print("ERROR fetchData in PersonProvider.dart : $error");
      
    } finally {
    //모든 작업이 끝나면 fetch가 false로 반환이 되게 한다.
      _isFetch = false;
    }
  }
}

위의 예시에서는 Firebase의 Realtime데이터를 가져오는 경우이다. 만약 firestore를 이용하는 경우라면, firestore의 CRUD를 이용하여 작성하면 된다.

 

3. ConsumerWidget부분.

마지막으로 ConsumerWidget부분이다. 

class PersonWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, ScopedReader watch) {
    //watch는 Provider를 관찰하는 역할
    //watch된 Provider에서 notifyListener()가 작동하는지 확인한다.
    final watchProvider = watch(riverpodProvider);

    if (watchProvider.isFetching) {
      //Provider에서 isFetch가 true인 경우 데이터를 가져오는 중
      return Center(
        child: CupertinoActivityIndicator(),
      );
    } else {
      //Provider에서 isFetch가 false인 경우 widget을 띄운다.
      return Container();
    }
  }
}

이렇게 사용하게 되면 notifiyListener가 작동되면 provider를 watch 되어있는 ConsumerWidget이 rebuild가 된다! 

 

다음 포스팅에서는 Riverpod의 Stream을 사용하는 법을 포스팅하도록 하겠다. 


오류, 지적사항 그리고 궁금한 것은 댓글 부탁드립니다.

 

728x90