Flutter Study-5주차2(Managing App-Wide State)

CHO WanGi·2023년 11월 20일

Flutter

목록 보기
11/27

상태관리

여러 컴포넌트간 데이터전달과 이벤트 발생을 한 공간에서 관리하는 것

기존 방식의 문제점

즐겨찾기 기능에 관련된 FavoriteState는 TabsScreen, CategoriesScreen, MealsScreen 등 다양한 스크린과 위젯에서 활용되는 상황, 각 스크린에 onToggleFavorite으로 함수를 전달하는 것은 비효율적

=> 한 공간에서 이를 뿌려주자

https://engineering.linecorp.com/ko/blog/flutter-architecture-getx-bloc-provider
Flutter 상태관리 라이브러리 3대장을 잘 비교해주신 글.

RiverPod

Provider 의 Anagram.

[공식 Docs 페이지] https://riverpod.dev/ko/docs/introduction/why_riverpod

기본원리

공급자(Provider)는 소비자(Cumstomer)의 관계기반,
소비자위젯에서 메서드를 호출하면 공급자에서 데이터를 뿌려주어 UI를 변화시킴.
모든 위젯은 공급자에 연결이 가능하다

ProviderScope

root of Application 을 ProviderScope로 감싸주어야 한다

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

Creating a Provider

import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'package:meals/data/dummy_data.dart';

final mealsProvider = Provider((ref) {
  return dummyMeals;
});

위에 코드 스니펫처럼 Provider 함수는 반드시 ref 를 파라미터로 가져야한다.

ref

ref 객체는 프로바이더와, 컨슈머간 상호작용을 담당
1. ref.watch: 프로바이더의 값을 취득하고 변화를 구독. 값의 변경이 발생하면 위젯을 다시 빌드하거나, 값을 구독하고 있는 위체에 상태 값을 전달 및 제공
2. ref.listen: 프로바이더의 상태 값을 구독하거나, 상태값이 변했을때 어떤 행위를 취해야할 경우사용
3. ref.read : 프로바이더의 상태값을 취득, 이벤트 콜백함수에 사용하는데 유용

💡 ref.read와 ref.listen 보단 ref.watch 사용을 권장

두 메서드의 차이는 위젯 재빌드의 여부, 즉 UI 업데이트를 통해 화면을 다시그려야 한다면 ref.watch를, 특정 함수만 호출하고 싶다면 ref.listen. 또한 비동기 처리가 필요한 환경이라면 ref.watch만 가능하다.

📚 아직 ref.read와 ref.watch 의 차이점이 명확하지 않다...

Using a Provider

  1. StatefulWidget -> ConsumerStatefulWidget
  2. StatelessWidget -> ConsumerStatelessWidget

Creating a More Complex Provider with StateNotifier

class FavoriteMealsNotifier extends StateNotifier<List<Meal>> {
  FavoriteMealsNotifier() : super([]); // 빈 리스트로 초기화

  void toggleMealFavoriteStatus(Meal meal) {
    // 즐겨찾기 부분
    final mealIsFavoirte = state.contains(meal);
    if (mealIsFavoirte) {
 
      state = state.where((m) => m.id != meal.id).toList();
    } else {
      state = [];
    }
    state = [...state, meal];

  }
}
final favoriteMealsProvider =
    StateNotifierProvider<FavoriteMealsNotifier, List<Meal>>((ref) {
  return FavoriteMealsNotifier();
});

// meal_details.dart

  Widget build(BuildContext context, WidgetRef ref) {
    final favoriteMeals = ref.watch(favoriteMealsProvider);

    final isFavorite = favoriteMeals.contains(meal);
    
 // tabs.dart   
   
  Widget build(BuildContext context) {
    final availableMeals = ref.watch(filteredMealsProvider);

    Widget activePage = CategoriesScreen(
      availableMeals: availableMeals,
    );
    var activePageTitle = 'Categories';

    if (_selectedPageIndex == 1) {
      final favoriteMeals = ref.watch(favoriteMealsProvider);
      activePage = MealsScreen(
        meals: favoriteMeals,
      );
      activePageTitle = 'Your Favorites';
    }

만약 특정 상황에서 데이터가 바뀐다면?
Notifiter 를 활용하는 것이 요구됨

위 코드 스니펫일 경우, 즐겨찾기 상태를 관리하는 FavoriteMealsNotifier 클래스를 생성, 제네릭(List<Meal>)에 관리할 상태를 명시

  • meal_details.dart, tabs.dart에서 ref.watch를 활용하여 Provider로부터 데이터를 요청

Outsourcing State Into The Provider

final filteredMealsProvider = Provider((ref) {
  final meals = ref.watch(mealsProvider);
  final activeFilters = ref.watch(filtersProvider);

  return meals.where((meal) {
    if (activeFilters[Filter.glutenFree]! && !meal.isGlutenFree) {
      return false;
    }
    if (activeFilters[Filter.lactoseFree]! && !meal.isLactoseFree) {
      return false;
    }
    if (activeFilters[Filter.vegetarian]! && !meal.isVegetarian) {
      return false;
    }
    if (activeFilters[Filter.vegan]! && !meal.isVegan) {
      return false;
    }
    return true;
  }).toList();

같은 Provider 내부에 다중 공급자 설정이 가능,
위 코드 스니펫에선 mealsProvider 와 filtersProvider 로부터 데이터를 공급받음
음식 정보(dummyMeals -> meals)와 음식 카테고리(Filter -> activeFilters)를 통해서 필터 기능 구현

profile
제 Velog에 오신 모든 분들이 작더라도 인사이트를 얻어가셨으면 좋겠습니다 :)

0개의 댓글