인프런 커뮤니티 질문&답변

유하님의 프로필 이미지
유하

작성한 질문수

Flutter 앱 개발 실전

BLoC 패턴

riverpod의 provider 사용

해결된 질문

작성

·

42

1

 

안녕하세요 선생님 !!

강의 잘보고 있습니다.

Provider : 수신측에 변경 사항을 알리지 않음 라고 하셔서

코드를 작성해보았습니다.

 

countprovider안에서 ref.watch(towProvider) 사용해봤는데요

우선 여기서 watch를 써서 상태가 바뀐건 이해합니다.

그래서 "counter 생성자" 가 계속 출력되고요 .

 

근데 main함수에서 counterProvider 를 ref.watch했는데 화면이 계속 갱신 되는 건 이해가 가지 않습니다.

수신측에 변경 사항을 알리지 않음 이라고 하셔서 여기서는 호출해도 변함이 없겠구나 라고 생각했습니다...

 

 

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final towProvider= NotifierProvider<Two, int>(
(){return Two();}
);
final counterProvider = Provider<Counter>((ref) {
  print("hello");
  int num=ref.watch(towProvider);
  return Counter(num:num);});

class Two extends Notifier<int>{
  Two(){
    print("Two 생성자");
  }
  @override
  int build() => 0;
  int add() => state++;
}
class Counter {
  Counter ({required this.num}){
    print("Counter 생성자");
  }
  int num;
  int getIncreasedNumber() => num ++;
}


void main() {
  runApp(
    const ProviderScope(
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        home: MyApp(),
      ),
    ),
  );
}

class MyApp extends ConsumerWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    print("build");
    Counter counter = ref.watch(counterProvider);
    return Scaffold(
      body: Center(
        child: Text(
          "${counter.num}",
          style: const TextStyle(
            fontSize: 24,
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: (){ref.read(towProvider.notifier).add();},
        child: const Icon(Icons.add),
      ),
    );
  }
}

답변 1

1

DevStory님의 프로필 이미지
DevStory
지식공유자

안녕하세요 유하님

ref.watch는 Provider에서 전달받은 상태가 변경되는 경우 위젯을 rebuild를 하는 방식으로 동작합니다.

image.png

Provider 자체는 내부적으로 상태가 갱신될 일이 없기 때문에 ref.watch로 접근해도 화면이 갱신될 일이 없는게 맞으며 이 부분을 강의에선 변경사항을 알리지 못한다고 표현하였습니다. 하지만 아래와 같이 ref.watch에 의해서 매번 새로운 Counter 인스턴스가 반환되는 경우 화면이 갱신되는게 맞습니다.

final counterProvider = Provider<Counter>((ref) {
  int num = ref.watch(towProvider);
  return Counter(num:num);
});

감사합니다 :)

 

유하님의 프로필 이미지
유하
질문자

아하 감사합니다.

그럼 추가적으로 궁금한 것이 생겼습니다.

새로운 Counter 인스턴스가 반환되서 => 갱신되는 거라고 하셨는데
다시 ref.watch를 빼고 다시 실행했을때는, 어떻게 보면 이때도 새로운 인스턴스가 반환되는 것 아닌가요? 앞에 const 키워드를 붙이지 않아서요... !

final counterProvider = Provider<Counter>((ref) { 
print("hello"); 
return Counter();});
DevStory님의 프로필 이미지
DevStory
지식공유자

ref.watch(counterProvider)를 호출하는 경우, 무조건 Counter() 인스턴스를 새롭게 만들어서 반환하는 방식으로 동작하지 않습니다. 첫 호출시에만 Counter()를 생성해서 내부적으로 들고 있고, 이후에 요청이 들어오면 내부적으로 변경 사항이 없는 경우 처음에 만들어둔 객체를 그대로 반환합니다. 공식 문서에선 캐싱 기능이라고 표현합니다. (autoDispose로 생성하는 경우에는 해당 Provider에 대한 참조가 없다면 제거될 수 있습니다. 자세한 사항은 공식 문서를 참고해 주세요.)

Provider<Counter>는 내부에 상태가 없기 때문에 자체적으로 상태 변화를 일으킬 수 없으므로 Provider 내부에 ref.watch가 없다면 한 번 생성한 객체를 계속 들고 있으며 변경된 값을 반환할 수 없습니다.

유하님의 프로필 이미지
유하

작성한 질문수

질문하기