Riverpod의 절반! NotifierProvider 이해하기

HyunHo Shin·2023년 6월 5일
0

NotifierProvider은 riverpod에서 상태를 저장, 변경하는 방법으로 추천하는 방식입니다.

🎯 NotifierProvider 구현 방식

RIverpod에서는 State를 다음과 같은 방식으로 관리합니다:

State(상태)란 UI에 변화를 주는 데이터 전반을 말합니다.

  • 상태는 Notifier에 저장 됩니다. (상태의 저장)
  • 저장된 상태는 Notifier에 구현한 함수로만 변경할 수 있습니다. (상태의 변경)

🎯 예제 살펴보기: Todo List

이번 예제 에서는 체크박스 기능이 있는 Todo 앱을 만들어 보도록 하겠습니다.

우리가 살펴볼 파일의 구조는 다음과 같습니다:

  • 데이터 형식을 규정할 model.dart
  • todo의 변화를 담당하는 todo_controller.dart
  • UI를 보여줄 todo_page.dart

🎯 Model.dart

먼저, Todo 데이터 모델을 만들어 줍니다.

Todo는 id, description, completed의 세가지 멤버 변수를 가지고 있습니다.

import 'package:freezed_annotation/freezed_annotation.dart';

part 'model.freezed.dart';


class Todo with _$Todo {
  factory Todo({
    required String id,
    required String description,
    required bool completed,
  }) = _Todo;
}

🎯 Controller.dart

Riverpod를 사용할 때 흔한 패턴 중 하나는 Notifier 또는 StateNotifier 객체를 만들고, 이 객체 자체를 데이터를 조작하는 controller로 사용하는 것입니다.

Controller는 앱에서 비즈니스 로직을 관리하는 역할을 합니다. 비즈니스 로직이란 애플리케이션에서 어떤 일을 하는지를 설명하는 규칙들의 모음입니다.

class TodosNotifier extends Notifier<List<Todo>> {
	// 첫번째 순서는 build 함수를 정의하는 것입니다.
	// Notifier의 build 함수는 state의 디폴트 값을 제공하는 함수 입니다.
	// 이 클래스는 state로 List<Todo>를 만들고 있습니다.
	// 앞으로 state는 List<Todo>를 의미한다고 이해하시면 됩니다.
  
  List<Todo> build() {
    return [
      Todo(
        id: '1',
        description: 'Finish homework',
        completed: false,
      ),
      Todo(
        id: '2',
        description: 'Go grocery shopping',
        completed: false,
      ),
      Todo(
        id: '3',
        description: 'Walk the dog',
        completed: true,
      ),
    ];
  }
	// 위에서 build 함수로 정의된 state값을 변경하는 함수를 직접 정의할 수 있습니다.
	// 아래 함수는 state에 새로운 todo를 추가하는 함수이비다.
  void addTodo(Todo todo) {
    state = [...state, todo];
  }

  void removeTodo(String todoId) {
    state = [
      for (final todo in state)
        if (todo.id != todoId) todo
    ];
  }

  void toggle(String todoId) {
    state = [
      for (final todo in state)
        if (todo.id == todoId)
          todo.copyWith(completed: !todo.completed)
        else
          todo,
    ];
  }
// 마지막으로 이렇게 만든 Notifier를 다른 파일에서 호출 가능하도록 todosProvider를 만들어 줍니다.
final todosProvider = NotifierProvider<TodosNotifier, List<Todo>>(
  () {
    return TodosNotifier();
  },
);

🎯UI

마지막으로 이렇게 완성된 Notifier를 이용해 UI를 변경하는 방법을 확인해 보겠습니다.

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

import '../controller/todo_controller.dart';
import '../models/model.dart';

class TodoPage extends HookConsumerWidget {
  const TodoPage({super.key});
  
  Widget build(BuildContext context, WidgetRef ref) {
		// todosProvider를 호출하여 앞서 정의한 state를 활용하겠습니다
		// ref.watch는 provider를 입력값으로 받고, state를 리턴하는 함수입니다.
		// * 앞서 말한 바처럼, 이 곳에서 state란 List<Todo>를 말합니다.
    List<Todo> todos = ref.watch(todosProvider);
    return Scaffold(
      body: ListView(
        children: [
          for (final todo in todos)
            CheckboxListTile(
              value: todo.completed,
							// ref.watch에 provider가 아니라. provider.notifier를 입력하면
							// state가 아닌, state를 변경하는 Notifier를 호출합니다.
							// 호출된 notifier에 미리 정의된 toggle method를 사용하여 checkbox를 바꿔줍니다.
              onChanged: (value) =>
                  ref.watch(todosProvider.notifier).toggle(todo.id),
              title: Text(todo.description),
            ),
        ],
      ),
    );
  }
}
profile
관심사가 다양한 사람

0개의 댓글