rrousselGit / riverpod

A reactive caching and data-binding framework. Riverpod makes working with asynchronous code a breeze.
https://riverpod.dev
MIT License
6.3k stars 961 forks source link

Add ability to listen to LiveData inside StateNotifier #986

Closed ebelevics closed 2 years ago

ebelevics commented 2 years ago

Is your feature request related to a problem? Please describe. I'll give brief example

final chatProvider = StateNotifierProvider.autoDispose<ChatNotifier, AsyncValue<ChatState>>(
  (ref) {
    final val = ref.watch(webSocketStreamProvider).value;
    return ChatNotifier(value: val);
  },
);

The problem that triggers me, that I have no way to listen to values, without ref.watch(webSocketStreamProvider) rebuilding ChatNotifier during every WS event. ref.read() is ignored as it doesn't listen to values. ref.listen kind of does the same as ref.watch, because of return. The only way I somewhat managed to solve is by return ChatNotifier(ref: ref); and using ref.listen inside ChatNotifier. When I use ref.watch inside 'ChatNotifier' I get weird exceptions.

Describe alternatives you've considered Of course I could use ref.listen if I passed ref as AutoDisposeStateNotifierProviderRef<ChatNotifier, AsyncValue<ChatState>> not Ref, or otherwise I get The argument type 'AutoDisposeStreamProvider<WsEvent>' can't be assigned to the parameter type 'AlwaysAliveProviderListenable<Object>'

Describe the solution you'd like Being able to use Ref ref inside StateNotifier to not only read but also listen to values, or maybe similar to ref.read as final Reader read but ref.listen as final RefListener listen (also I don't really understand, why Ref is not built into StateNotifier similar as state is built it. Like providers in general always have ref to pass into StateNotifier. Why this additional step is required?

Additional context Recently I have been working on Android Studio Kotlin project, and one thing that IMO kotlin did a lot better is managing data with ViewModels by using LiveData. And Riverpod in that sense looked like it had similar possibility. So I tried to do similar from

class ItemsViewModel(private val repository: Repository) : BaseViewModel() {

    private val _items = SingleLiveEvent<MutableList<Item>>()
    val items: LiveData<MutableList<Item>> = _items

    private val _state = MutableLiveData<State>(State.InitialState)
    val state: LiveData<State> = _state

    private val _error = SingleLiveEvent<Throwable>()
    val error: LiveData<Throwable> = _error

to [Psuedo-code]

final itemsProvider = Provider.autoDispose((ref) => ItemsViewModel(ref:ref));

class ItemsViewModel {
    final _items = StateProvider<List<Item>>((ref) => []);
    get items => ref.watch(_items);
    final _state = StateProvider<State>((ref) => State.InitialState());
    get state => ref.watch(_state);
    final _error = StateProvider<String?>((ref) => null);
    get error => ref.watch(_error);
// and other providers Future, Stream... so on.

    final Ref ref;
    ItemsViewModel({required this.ref});

and theorotically then in widget to initiate viewModel you do the usual

ref.watch(itemsProvider)

and to watch value from other "LiveData" you use

ref.watch(itemsProvider).items

but it seems currently it is hard to achieve something like that, or maybe it's possible. I don't know I haven't tried to inspecting riverpods implementation much more closely and modifying to expect this behaviour

ebelevics commented 2 years ago

From https://github.com/rrousselGit/river_pod/issues/705 I found that there is such Ref as AutoDisposeRef. So it somewhat answers my question. ~Still kind of don't understand, why Ref is not present in StateNotifier already. My first thought, isn't it possible to check in runtime is provider autoDispose or normal?~ (StateNotifier comes from package:state_notifier) But I can close topic as there was answered similar topic. Needs to be documented in website.

Still now I'm trying to find out, can I do similar approach as Kotlin LiveData example. Making StateProviders in class static won't work as I want all StateProviders to be tied to ItemsViewModel instance and autodispose as itemsProvider get disposed.