개발을 시작하는 이야기

Flutter의 상태관리 02 본문

개발 이야기/Flutter

Flutter의 상태관리 02

Teiresias 2024. 6. 16. 18:10

Provider란?

Provider란 Riverpod에서 가장 중요한 부분으로 상태를 캡슐화하는 객체이자 상태의 변화를 감지하는 역할을 하고, Riverpod은 이런 Provide를 용도에 따라 세분화한 상태관리 라이브러리라고 할 수 있다. Provider는 크게 두가지 클래스로 구성되는데, 하나는 ChangeNotifier 클래스 그리고 Provider 클래스가 있다. 

 

ChangeNotifier 클래스는 Mixin을 이용하여 상태를 변경하고, 변경된 상태를 Provider 클래스에 알리는 역할을 한다. ChangeNotifier 클래스를 상속받은 클래스를 만들고, 해당 클래스 내부에서 상태를 변경하면 Provider에 상태 변경을 알린다.

 

Provider 클래스는 StateNotifirerProvider, StateProvider, FutureProvider, StreamProvider, ChangeNotifierProvider 같은  다양한 클래스를 제공한다.

  • StateNotifirerProvider : 상태를 알려주는 StateNotifier의 상태 변화를 관찰하다가 변경된 상태를 알려주는 Provider. 사용자와의 상호 작용이나 이벤트로 변화하는 상태를 관리할때 사용함
  • StateProvider : StateNotifierProvider보다 단순한 상태를 관리한다.
  • FutureProvider : 일반적인 Provider와 역할은 같지만, 비동기 처리가 가능한 Provider로 네트워크 통신, 파일 입출력, 데이터베이스 입출력 등에 사용된다.
  • StreamProvider : FutureProvider와 비슷하지만 Stream 처리에 유용하다.
  • ChangeNotifierProvider : Provider 라이브러리의 ChangeNotifierProvider를 사용하는경우 Riverpod에서도 동일하게 사용가능하지만 권장되는 방법은 아니다. 공식문서에서는 mutable한 상태가 아니라면 StateNotifierProvider 사용을 권장한다.

Provider 클래스를 이용하여 상태를 관리하면, 해당 상태가 변경될 떄마다 Provider 하위에 있는 모든 위젯들이 랜더링 되며 상태 변화에 따라 위젯을 갱신하는 것이 용이하다. 우선 Provider가 동작할 수 있도록 하기 위해서는 앱의 최상위 위젯을 ProviderScope로 감싸주어야 한다.

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

Provider와 상호작용

Provider를 사용하는 위젯은 Provider를 참조할 수 있는 객체인 WidgetRef를 사용해 Provider와 상호작용을 해야 한다. Riverpod은 WidgetRef 객체를 얻기 위해 ConsumerWidget과 ConsumerStateFulWidget을 사용한다.

 

ConsumerWidget을 사용하면 build 함수의 파라미터로 WidgetRef를 사용할 수 있다.

class MainScreen extends HookConsumerWidget {
  const MainScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    var me = ref.watch(meProvider);
    
    ...
  }
}

 

ConsumerStateFulWidget의 경우 WidgetRef가 ref 속성으로 정의되어 있어 WidgetRef를 build 이외의 다른 함수에서도 사용할 수 있다. 

class SearchBar extends ConsumerStatefulWidget {
  const SearchBar({super.key});
  
  @override
  ConsumerState<ConsumerStatefulWidget> createState() => _SearchBar();
}

class _SearchBar extends ConsumerState<SearchBar> {
  @override
  Widget build(BuildContext context) {
    var me = ref.watch(meProvider);
    return Container();
  }
}

ref의 주요 용도

ref의 주요 용도는 세가지가 있다.

ref.watch : Provider의 값을 가져오고 변경 내용을 모니터링 한다. 이 값이 변경되면 해당 값을 구독하고 있는 위젯을 다시 빌드하거나 구독 하고 있는 위치에 상태를 전달한다. 상태 변화를 화면에 즉각적으로 표현할때 사용한다.

ref.read : Provider의 상태를 가져오고 값을 변경할 수 있다. 일반적으로 사용자와 상호작용으로 발생 가능한 트리거 함수에서 사용한다.

ref.listen : Provider에 리스너를 추가하여 해당 Provider의 변경을 모니터링 하지만 위젯을 다시 빌드하거나 상태를 전달하지는 않고, 값이 변경될 때 정의한 함수를 실행한다. SnackBar나 Dialog를 처리하는데 주로 사용한다.

 

코드 예시

 일 목록을 조작하는 기존 StateNotifierProvider가 있다고 가정합니다.

class Todo {
  Todo(this.description, this.isCompleted);
  final bool isCompleted;
  final String description;
}

class TodosNotifier extends StateNotifier<List<Todo>> {
  TodosNotifier() : super([]);

  void addTodo(Todo todo) {
    state = [...state, todo];
  }
  // TODO "removeTodo"와 같은 다른 메소드들을 추가하기
}

final todosProvider = StateNotifierProvider<TodosNotifier, List<Todo>>((ref) {
  return TodosNotifier();
});

여기서 Provider로 필터링 하여 완료된 할 일 목록만 표시할 수 있다.

final completedTodosProvider = Provider<List<Todo>>((ref) {
  // todosProvider로부터 모든 할일(todos)목록을 가져옵니다.
  final todos = ref.watch(todosProvider);

  // 완료된(completed) 할일(todos)들만 반환합니다.
  return todos.where((todo) => todo.isCompleted).toList();
});

이제 UI에서 수신하여 완료된 할 일 목록을 표시할 수 있다.

Consumer(builder: (context, ref, child) {
    final completedTodos = ref.watch(completedTodosProvider);
    // TODO a ListView/GridView/...등을 사용하여 todos를 표시하기/* SKIP */
    return Container();
    /* SKIP END */
  });

 

 

 

 

Riverpod

Safely read providers Reading a provider will never result in a bad state. If you can write the code needed to read a provider, you will obtain a valid value. This even applies to asynchronously loaded values. As opposed to with provider, Riverpod allows c

riverpod.dev

 

 

Flutter documentation

Get started with Flutter. Widgets, examples, updates, and API docs to help you write your first Flutter app.

docs.flutter.dev

 

'개발 이야기 > Flutter' 카테고리의 다른 글

Flutter의 Hooks 사용하기 02  (0) 2024.06.18
Flutter의 Hooks 사용하기 01  (0) 2024.06.17
Flutter의 상태관리 01  (0) 2024.06.15
객체지향 프로그래밍 정리  (1) 2024.06.14
Android Google Cloud용 SHA-1 생성  (0) 2024.06.05