본문 바로가기
스파르타/Flutter

[Flutter] 마이메모 #2 provider - ( week 3 )

by bakcoding_sparta 2023. 4. 22.

상태 관리(State Management)

State Management Docs

상태 관리의 역할은 최신 상태의 데이터를 보여주도록 페이지 간 데이터를 주고받고 관리하는 행위를 의미한다.

 

소규모 프로젝트의 경우 상태관리가 없이도 충분히 진행이 가능하다. 하지만 서로 다른 페이지의 화면을 갱신해 주는 기능에서부터는 까다로워지는데 물론 부모 위젯의 setState를 자녀 위젯으로 넘겨주고 자녀 위젯에서 부모 위젯의 화면을 갱신하도록 할 수 있지만 이 방법은 페이지가 조금만 많아져도 코드가 지나치게 복잡해지는 문제가 발생한다.

이 문제를 해결하기 위해서 사용하는것이 상태관리 패키지이다.

 

상태 관리 패키지

대부분의 상태 관리 패키지들은 중앙 집중식으로 데이터를 한 곳에 모아서 관리한다. 통상적으로 데이터를 담당하는 클래스를 서비스라고 부른다. 각 페이지에서는 데이터에 대한 CRUD는 모두 서비스에게 요청하는 방식으로 구현되고 이를 통해 각 화면 간 데이터를 주고받는 문제를 해결할 수 있다.

 

패키지 종류

  • GetX
  • Provider
  • BloC
  • Riverpod

이 중에서 사용법이 쉬우면서 Flutter 공식 문서에서 추천하는 패키지인 Provider를 사용해 본다.

 

Provider

 

패키지 배포사이트에 들어가서 명령어를 복사하고 내 프로젝트의 콘솔창에 복사한 명령어를 입력해 준다.

 

Provider 설치가 완료되었다. 패키지의 설치 유무는 pubspec.yaml 파일에서 dependencides에서 확인이 가능하다.

 

1. 파일 분리

이제 페이지의 데이터를 모두 서비스에서 관리하도록 만든다.

memo_service.dart 파일을 생성하고 아래 코드를 작성한다.

import 'package:flutter/material.dart';

import 'main.dart';

// Memo 데이터의 형식을 정해줍니다. 추후 isPinned, updatedAt 등의 정보도 저장할 수 있습니다.
class Memo {
  Memo({
    required this.content,
  });

  String content;
}

// Memo 데이터는 모두 여기서 관리
class MemoService extends ChangeNotifier {
  List<Memo> memoList = [
    Memo(content: '장보기 목록: 사과, 양파'), // 더미(dummy) 데이터
    Memo(content: '새 메모'), // 더미(dummy) 데이터
  ];
}

현재까지 메모는 단순하게 string 값으로만 만들었는데 더 많은 확장성을 위해서 별도의 Memo 클래스를 생성해 준다.

그리고 Memo정보를 일괄로 관리하기 위해서 ChangeNotifier 클래스를 상속받은 MemoService 클래스를 만들어준다.

ChageNotifier를 상속받게 되면 notifylistners() 함수를 사용할 수 있게 되는데 이 함수를 호출하여 위젯들을 갱신하는 기능으로 사용할 수 있다.

 

2. 패키지 설정

패키지를 사용하기 위해서 설정을 해준다.

Provider를 이용해서 MemoService를 위젯 트리 최상단에 배치하고 어디서든 쉽게 접근할 수 있도록 만든다.

 

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

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) => MemoService()),
      ],
      child: const MyApp(),
    ),
  );
}

 

해당 클래스에 접근하기 위해서 필요한 패키지와 파일을 참조한다.

 

3. Provider 적용

mymemo 앱에 Provider를 적용시킨다.

Provider의 Service 데이터를 사용하기 위해서는 사용 중인 모든 위젯들을 Consumer위젯으로 감싸주어야 위젯 트리 최상위에 있는 MemoService에 접근이 가능하다.

Consumer 위젯의 형식은 다음처럼 context, memoService, child 매개변수 3개를 받아야 한다.

Consumer<MemoService>(
  builder: (context, memoService, child) {
    // memoService 라는 변수에 MemoService 를 담아서 쓸 수 있습니다.
    return 위젯;
  }
)

_HomePageState의 Scaffold 위젯을 Build로 감싸준다.

형식이 비슷하기 때문에 Builder로 감싸준 후에 Consumer로 위젯을 변경하고 필요한 값을 추가로 작성해 준다.

  @override
  Widget build(BuildContext context) {
    return Consumer<MemoService>(builder: (context, memoService, child) {
      return Scaffold(

 

이제 메모 리스트를 MemoService에서 선언한 List <Memo> memoList를 가져다 사용하도록 한다.

class _HomePageState extends State<HomePage> {
  //List<String> memoList = ['장보기 목록: 사과, 양파', '새 메모: 사과, 양파']; // 전체 메모 목록

  @override
  Widget build(BuildContext context) {
    return Consumer<MemoService>(
      builder: (context, memoService, child) {
        // memoService로 부터 memoList 가져오기
        List<Memo> memoList = memoService.memoList;

이전에 사용하던 memoList와 타입이 다르기 때문에 이것부터 일치시켜 준다.

String => Memo

                    Memo memo = memoList[index]; // index에 해당하는 memo 가져오기

 

텍스트 내용으로 사용 중이던 부분은 memo의 content 필드를 가져다 쓴다.

// 메모 내용 (최대 3줄까지만 보여주도록)
                      title: Text(
                        memo.content,

 

나머지 목록을 생성하던 기능들에서 발생한 에러들은 기능들을 통째로 주석처리해서 에러를 제거해 놓고 해당 부분의 구현은 앞에서부터 진행하도록 한다.

 

여기까지 진행하면 메모를 조회할 수 있게 된다.

 

다음장에 계속