본문 바로가기

Flutter

Riverpod : 리버팟

 

 

Provider를 사용하려면 먼저 전체 앱을 ProviderScope로 감싸줘야 한다.

void main() {
  runApp(
    ProviderScope( // ProviderScope로 감싸줘야 한다
      child: MyApp(),
    ),
  );
}

 

provider 에서 관리되는 데이터를 읽기 위해 필요한 것

  • ConsumerWidget
  • WidgetRef
    • ref.watch : UI관련 코드에만 사용할것. provider 값이 변경되면 build 함수를 다시 실행시켜줌.
    • ref.read : 실행순간 단 1번만 provider 값을 가져온다.(단발성)
// 사용할 Provider
final valueProvider = Provider<int>((ref) {
  return 0;
});

// Stateless --> ComsumerWidget
class MyHomePage extends ConsumerWidget {
  @override
  // BuildContext 파라미터 오른쪽에 WidgetRef 파라미터 추가!!
  Widget build(BuildContext context, WidgetRef ref) {
    // ref.watch = 데이터를 읽어 들이기 위해 watch 한다.
    final value = ref.watch(valueProvider);
    
    return Scaffold(
      body: Center(
        child: Text(
          'Value: $value',
        ),
      ),
    );
  }
}

 

 

 


 

 

Provider의 종류 (6가지)

Provider의 기본은 해당 Provider 들은 글로벌(전역)하게 사용된다. = 어디에서든 접근 할수 있다.

 

 

Provider

provider.dart

// 구현 형태 예시
final valueProvider = Provider<int>((ref) {
  return 0;
});

 

Provider는 읽기만 가능하며 값을 변경할 수 없다. (값 변경 불가!!)

보통 Provider 안에 Provider 를 넣을 경우에, 많이 사용한다.

 

 

 

StateProvider

state_provider.dart

// <int> 적어줘도 되고, 안해도 되고,,, 자동인식
final numberProvider = StateProvider<int>((ref) => 0);

 

state_provider_screen.dart

class StateProviderScreen extends ConsumerWidget {
  StateProviderScreen({Key? key}) : super(key: key);

  final commonStyle = ElevatedButton.styleFrom(
    foregroundColor: Colors.white,
    backgroundColor: Colors.redAccent,
  );
  final todoStyle = ElevatedButton.styleFrom(
    foregroundColor: Colors.black,
    backgroundColor: Colors.greenAccent,
  );

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final provider = ref.watch(numberProvider); /// numberProvider 변수값

    return DefaultLayout(
      title: 'Basic Provider',
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Text(
            provider.toString(),
            textAlign: TextAlign.center,
          ),
          ElevatedButton(
            style: ElevatedButton.styleFrom(
              foregroundColor: Colors.white,
              backgroundColor: Colors.redAccent,
            ),
            onPressed: () {
              /// 값 변경 방법 : .state
              /// 값 변경할려면, Provider 뒤에 notifier 붙힐것
              ref.read(numberProvider.notifier).state =
                  ref.read(numberProvider.notifier).state + 1;
            },
            child: const Text('UP',),
          ),
          ElevatedButton(
            style: ElevatedButton.styleFrom(
              foregroundColor: Colors.black,
              backgroundColor: Colors.greenAccent,
            ),
            onPressed: () {
              /// 값 변경 방법 : .update
              ref.read(numberProvider.notifier)
                  .update(
                      (state) => state + 1
              );
            },
            child: const Text('Update'),
          ),
        ],
      ),
    );
  }
}

StateProvider는 상태를 변경할 수 있는 Provider이다.

내부 상태는 state로 접근이 가능한데, 사용하고자 하는 위젯에서 state 값을 직접 변경할 수 있다.

update 방식으로 데이터를 변경할수 있다.

  • UI에서 직접 데이터를 변경하고 싶을때 사용
  • 단순한 형태의 데이터만 관리
  • Map, List 등 복잡한 형태의 데이터는 다루지 않는다.
  • 복잡한 로직은 사용하지 않는다. => 간단한 로직만 사용

 

 

 

StateNotifierProvider

제일 많이 사용되는 Provider

stateProvider에서 약간 사용법이 변경된다.

  • StateNotifierProvider 생성 시, <Notifier 클래스, 관리하는 상태 타입> 이 추가 된다.
  • 그리고 StateNotifier 클래스를 상속받는 클래스를 하나 만들어야 한다.
  • StateNotifier 클래스를 상속받는 클래스에서 생성자를 만들어야 한다.

 

state_notifier_provider.dart

// < Notifier 클래스, 관리하는 상태 타입>
final shoppingListNotifierProvider = 
StateNotifierProvider<ShoppingListNotifier, List<ShoppingItemModel>>(
  (ref) => ShoppingListNotifier(),
);


/// StateNotifier 를 절대적으로 상속해야 한다.
class ShoppingListNotifier extends StateNotifier<List<ShoppingItemModel>> {
  /// 생성자 적용
  ShoppingListNotifier()
      : super(
          [
            const ShoppingItemModel(
              name: '김치',
              quantity: 3,
              hasBought: false,
              isSpicy: true,
            ),
            const ShoppingItemModel(
              name: '라면',
              quantity: 5,
              hasBought: false,
              isSpicy: true,
            ),
            const ShoppingItemModel(
              name: '불닭소스',
              quantity: 1,
              hasBought: false,
              isSpicy: true,
            ),
            const ShoppingItemModel(
              name: '삼겹살',
              quantity: 10,
              hasBought: false,
              isSpicy: false,
            ),
            const ShoppingItemModel(
              name: '수박',
              quantity: 2,
              hasBought: false,
              isSpicy: false,
            ),
            const ShoppingItemModel(
              name: '카스테라',
              quantity: 5,
              hasBought: false,
              isSpicy: false,
            ),
          ]
        );

  void toggleTodo({required String name}) {
    // state 는 상속받은 StateNotifier 의 state를 의미한다.
    state = state.map((e) =>
    e.name == name
        ? e.copyWith(hasBought : !e.hasBought)
        : e,
    ).toList();
  }

}

 

state_notifier_provider_screen.dart

class StateNotifierProviderScreen extends ConsumerWidget {
  const StateNotifierProviderScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    List<ShoppingItemModel> items = ref.watch(shoppingListNotifierProvider);

    return DefaultLayout(
      title: 'StateNotifierProvider',
      body: ListView(
        children: items
            .map(
              (e) => CheckboxListTile(
                title: Text('${e.name}(${e.quantity})'),
                value: e.hasBought,
                onChanged: (value) {
                  /// Provider에서 데이터 읽어 오고 싶을때는 read 사용(단발성 액션엔 read)
                  /// ref.read(shoppingListNotifierProvider.notifier) 
                  /// 는 ShoppingListNotifier 의 인스턴스를 호출하는 것!
                  /// 해당 인스턴스를 호출하는 것이니, 메소드를 불러올수 있는것이다.
                  ref.read(shoppingListNotifierProvider.notifier).toggleTodo(
                    name: e.name,
                  );
                },
              ),
            )
            .toList(),
      ),
    );
  }
}

 

 

 

 

FutureProvider

  • Future 타입만 반환 가능
  • API 요청의 결과를 반환할때 보통 사용
  • 1회성 실행 => 가장 큰 단점.. 반복실행이 필요한 기능에서는 StateNotifierProvider 사용 

 

future_provider.dart

// 구현 형태 예시
final multiplesFutureProvider = FutureProvider<List<int>>((ref) async {
  await Future.delayed(
    const Duration(
      seconds: 2,
    )
  );

  /// 일부러 에러 발생 시키는 코드
  // throw Exception('에러');

  return [1,2,3,4,5];
});

 

future_provider_screen.dart

class FutureProviderScreen extends ConsumerWidget {
  const FutureProviderScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    /// FutureProvider 는 반환시 AsyncValue 로 반환한다.
    final AsyncValue<List<int>> numbers = ref.watch(multiplesFutureProvider);

    return DefaultLayout(
      title: 'FutureProvider',
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          /// Freezeed
          /// 종류1 : .map
          /// 종류2 : .when
          numbers.when(
            // data: 로딩후 데이터가 있을때, 반환받은 값
            data: (data) => Text(
              data.toString(),
              textAlign: TextAlign.center,
            ),
            error: (err, stack) => Text('Error: $err'),
            loading: () => const Center(
              child: CircularProgressIndicator(),
            ),
          ),
        ],
      ),
    );
  }
}

 

 

 

 

StreamProvider

  • Stream 타입만 반환 가능
  • socket 을 이용한 데이터 통신에 사용. 보통 실시간 채팅기능 작업 시 사용.

stream_provider.dart

final multiplesStreamProvider = StreamProvider<List<int>>((ref) async*{
  for(int i = 0; i < 10; i++){
    await Future.delayed(Duration(seconds: 1));

    yield List.generate(3, (index) => index * i);
  }
});

 

stream_provider_screen.dart

class StreamProviderScreen extends ConsumerWidget {
  const StreamProviderScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    /// StreamProvider 는 반환시 AsyncValue 로 반환한다.
    final AsyncValue<List<int>> stream = ref.watch(multiplesStreamProvider);

    return DefaultLayout(
      title: 'StreamProvider',
      body: Center(
        child: stream.when(
          data: (data) => Text(data.toString()),
          error: (error, stack) => Text('Error: $error'),
          loading: () => const CircularProgressIndicator(),
        ),
      ),
    );
  }
}

 

 

 

 

 

'Flutter' 카테고리의 다른 글

빈 화면 터치. 키보드(자판) 숨기기  (0) 2024.02.24
[GoRouter] Go vs Push  (0) 2024.02.14