주요 콘텐츠로 건너뛰기

웹소켓 및 동기 실행

지금까지는 Future를 생성하는 방법만 다루었습니다.
이는 의도적으로 Future가 Riverpod 애플리케이션을 빌드하는 방법의 핵심이기 때문입니다. 하지만 Riverpod는 필요한 경우 다른 형식도 지원합니다.

특히 providers는 Future 대신 자유롭게 객체를 반환할 수 있습니다:

  • 'Repository' 생성 등 객체를 동기적으로 반환할 수 있습니다.
  • 웹소켓을 수신하기 위해 Stream을 반환합니다.

Future를 반환하는 것과 Stream 또는 객체를 반환하는 것은 전반적으로 매우 유사합니다. 이 페이지는 이러한 사용 사례에 대한 미묘한 차이점과 다양한 팁을 설명하는 페이지라고 생각하시면 됩니다.

동기적으로 객체 반환하기

객체를 동기적으로 생성하려면 provice가 Future를 반환하지 않는지 확인하세요:


int synchronousExample(SynchronousExampleRef ref) {
return 0;
}

provider가 객체를 동기적으로 생성하면 객체가 소비되는 방식에 영향을 미칩니다. 특히 동기식 값은 "AsyncValue"로 래핑되지 않습니다:

  Consumer(
builder: (context, ref, child) {
// The value is not wrapped in an "AsyncValue"
int value = ref.watch(synchronousExampleProvider);

return Text('$value');
},
);

이 차이로 인해 provice가 에러를 발생시키면 값을 읽으려고(read) 하면 에러가 다시 발생(rethrow)합니다. 또는 ref.listen을 사용할 경우 "onError" 콜백이 호출됩니다.

수신 가능(Listenable) 객체 고려 사항

ChangeNotifier 또는 StateNotifier과 같은 수신가능 객체는 지원되지 않습니다.
호환성상의 이유로 이러한 객체 중 하나와 상호 작용해야 하는 경우, 한 가지 우회 방법은 해당 알림 메커니즘(notification mechanism)을 Riverpod로 연결(pipe)하는 것입니다.

/// A provider which creates a ValueNotifier and update its listeners
/// whenever the value changes.

ValueNotifier<int> myListenable(MyListenableRef ref) {
final notifier = ValueNotifier(0);

// Dispose of the notifier when the provider is destroyed
ref.onDispose(notifier.dispose);

// Notify listeners of this provider whenever the ValueNotifier updates.
notifier.addListener(ref.notifyListeners);

return notifier;
}
정보

이러한 로직이 여러 번 필요한 경우, 공유된 로직에 주목할 가치가 있습니다! "ref" 객체는 컴포저블(composable)하게 설계되었습니다. 이를 통해 공급자에서 dispose/listening 로직을 추출할 수 있습니다:

extension on Ref {
// We can move the previous logic to a Ref extension.
// This enables reusing the logic between providers
T disposeAndListenChangeNotifier<T extends ChangeNotifier>(T notifier) {
onDispose(notifier.dispose);
notifier.addListener(notifyListeners);
// We return the notifier to ease the usage a bit
return notifier;
}
}


ValueNotifier<int> myListenable(MyListenableRef ref) {
return ref.disposeAndListenChangeNotifier(ValueNotifier(0));
}


ValueNotifier<int> anotherListenable(AnotherListenableRef ref) {
return ref.disposeAndListenChangeNotifier(ValueNotifier(42));
}

스트림 수신하기(Listening)

최신 애플리케이션의 일반적인 사용 사례는 websocket과 상호 작용하는 것입니다(예: Firebase 또는 GraphQL 구독).
이러한 API와의 상호 작용은 종종 Stream을 수신하여 수행됩니다.

이를 돕기 위해 Riverpod은 Stream 객체를 자연스럽게 지원합니다. Future 객체와 마찬가지로 이 객체는 AsyncValue로 변환됩니다:


Stream<int> streamExample(StreamExampleRef ref) async* {
// Every 1 second, yield a number from 0 to 41.
// This could be replaced with a Stream from Firestore or GraphQL or anything else.
for (var i = 0; i < 42; i++) {
yield i;
await Future<void>.delayed(const Duration(seconds: 1));
}
}

class Consumer extends ConsumerWidget {

Widget build(BuildContext context, WidgetRef ref) {
// The stream is listened to and converted to an AsyncValue.
AsyncValue<int> value = ref.watch(streamExampleProvider);

// We can use the AsyncValue to handle loading/error states and show the data.
return switch (value) {
AsyncValue(:final error?) => Text('Error: $error'),
AsyncValue(:final valueOrNull?) => Text('$valueOrNull'),
_ => const CircularProgressIndicator(),
};
}
}
정보

Riverpod은 RX의 BehaviorSubject와 같은 같은 커스텀 Stream 구현을 인식하지 못합니다. 따라서 생성시 이미 사용 가능하더라도 BehaviorSubject를 반환하면 value가 위젯에 동기적으로 노출되지 않습니다.

Stream/FutureAsyncValue로 변환하지 않기

기본적으로 Riverpod는 StreamFutureAsyncValue로 변환합니다. 거의 필요하지 않지만, 반환 유형을 Raw typedef로 감싸서 이 동작을 비활성화할 수 있습니다.

주의

일반적으로 AsyncValue 변환을 비활성화하는 것은 권장하지 않습니다. 자신이 무엇을 하고 있는지 알고 있는 경우에만 그렇게 하세요.


Raw<Stream<int>> rawStream(RawStreamRef ref) {
// "Raw" is a typedef. No need to wrap the return
// value in a "Raw" constructor.
return const Stream<int>.empty();
}

class Consumer extends ConsumerWidget {

Widget build(BuildContext context, WidgetRef ref) {
// The value is no-longer converted to AsyncValue,
// and the created stream is returned as is.
Stream<int> stream = ref.watch(rawStreamProvider);
return StreamBuilder<int>(
stream: stream,
builder: (context, snapshot) {
return Text('${snapshot.data}');
},
);
}
}