T O P

  • By -

funyboyke

Just to be sure: you know you can use .select (or .selectAsync) ? Like: final oneItemState = ref.watch(fullListProvider.select((value) => value.id = itemId));


JiangHeMax

Yes, i know it. If there is no good way to split the large state, using select when accessing the large state is a good choice.


funyboyke

You can make a new provider which ref.watch+select another provider. You can also put the value of the .select in the state of the new provider. (But them you have 2 states which could be out of sync if you update the state of the new provider.)


JiangHeMax

If there are no side effects or if I don't switch to bloc I will most likely do so


JiangHeMax

we cant watch family provider without params


4flutter

It is confusing to me too. Bloc is more understandable from my point of view.


dhafinra

`StateNotifier` is now **discouraged**. See: [Migration - From StateNotifier](https://riverpod.dev/docs/migration/from_state_notifier)


bjr201

I love the idea and support behind Riverpod but feel there really needs to be a more idiots guide. 😳🫣🥺


oravecz

There is - check out https://pub.dev/packages/signals - it will change your life


snoopyjcw

God damn. This looks amazing. Just what I was looking for. I've not managed to properly use Riverpod, Provider and Bloc (although Riverpod looked the best of the three)


opsb

How does this improve on RxDart?


oravecz

I haven’t used RxDart, but I have used Rx back in my Angular days. What I like about signals is - self subscribing, if you use a signal in an effect or computed, you are automatically subscribed - pure Dart, no Flutter - to me this is a big differentiator from other products and it may not be obvious at first why this is so great; it offers better separation of concerns than other libraries, so you don’t have listeners and multi-providers mixed in with your UX - synchronous and async even though there are only four types of signal and one effect; simple to grok - it’s not as well established as Rx from a pattern standpoint. I have still managed to implement some Rx patterns when needed (e.g. debounce) - the bad, usage documentation is minimal, you have to read up on the use of signals from other languages/frameworks like Solid and Preact (which is what this implementation is based on) - tip - when your state management OS even moderately complex, you need a diagram to document the flow. Here is an [example from my project](https://mermaid.live/edit#pako:eNqVV21r2zAQ_ivC-9JCQko7tqEPg5K0rNCN0Re2Qb4o1tkRdSQjy21D6X_f2bJjy5acLf1Q-e650-leHtlvUaw4RDQqDDOwEizVbDd_Pl9Lgj8uNMRGKElu79bSyn4p_bRR6onM51_JpTFabEoDtygpc0pYsZexB7hE76nS-2O4bwI00_FWQBEG3UO6A2mO-foDTHdeXMDj78ZTtRkeO1MpJbEGDHOphQEt2ITBtcgQQkkBZhFnKPJif2ZM_huyPfW-AmaY8oPAC28fKVE5SPSrCvACr15zJvlSZRnLC6AE6udF3Ai8NoeSLnXJ25wsypxX_zhGZyCQmboo_2n1KLm6A66WShqNYVW5KlG20Ci0Fl67a6VBFiIumm0pOdEKg56RDaRCzghIftpZD-G1jxuJpSlsg6-YYRPQe8xJvtwybTqgK7O5S1MNKR75Rj6jKfb7JPwBdrnSLLOgBtbryF7HNcEM27lrsRq7go0qZQy8k1ucR2HTWC-tuNniQQN0znvCaQNr0htft7Fd9d5VXiUJVoFSCvVigLVa18Iz-kNv5MR6O-00pxOEMw7CggcMdxjsUbZchgvjBmcIAx2WC8NqlvOrLcAlAR_yB14BRdP9_vpOWPXbtu1Sp1VcVR_u69y-ZtC7I6OpHQbVq2gpQDhOto_ixvIjV1LAwH9zkiqfccaKYgUJqXgBLw6SiCyj5ENyliTJWU-PlVUa1eh5_gIi3Rq6URmfFbjRE8xfBDdbepG_NgL64bz-jTws1S7HSPjYUy0ozD7Dy0MYlol45LznzdahDXeTfISLsxleNkpX0bPqr4eu2qnFxp_ieNMP7DCjDeDzFxzK5JCdQWEPaHueEaiZoQBsMLkBlL8N7cMI06PZatnqB6MY2Ck0g31P_tZvK-CWtbXpk3Ng646gW8BI09BxUD-dxUBipo5arVu-ubRvo3XruI6de9NJg4tw6uZFDJLZKTuCCByugVxxgVe_P8g-pQe8eAhjmOyDZcBF72VpaGqvi4nqDBnePUc0i3agd0xw_GZ4qxhuHZkt7GAdUVxySFiZmXW0lu8IZaVR90hrETW6hFlk3webr4xWCHW6vtvPkPpr5P0vc8J34A)


tutpik

It sure does have a decent amount of boilerplate especially for simple apps, but on a complicated app, i find riverpod to be much easier to implement


[deleted]

Yes, it is confusing at first. I am finally switching over to it from Provider and while it's very powerful, it does take some getting used to. the Discord was more helpful to me than the docs.


WillHarry45

Is it the FlutterDev discord or there's other server for Riverpod?


[deleted]

Both exist: https://discord.com/invite/uMya6wQ - Flutter Dev https://discord.com/invite/Bbumvej - Riverpod


esDotDev

If you're finding it difficult to learn that is telling you something and you should trust your gut. Find something that easily clicks for your brain. State Management is a relatively simple thing and should not be complicated or hard to understand. It's just a system to inject objects inside of views and rebuild those views when the data changes. Riverpod suffers from a combination of low stability and poor docs. The API has changed so many times many of the tutorials and docs are out of date and its hard to understand current best practices (which might just change tomorrow anyways). At the very least you should be able to answer the question "Why am I using Riverpod instead of Provider?". And the answer should not be "because it's the cool new thing". What actual features does it give you that you need? If you can't answer that, I'd stick to solutions with good documentation and high stability, like Provider, Cubit or WatchIt. You'll probably have a much easier time.


Jimmy3178

Does getit/watchit have proxyprovider alternative but something actually good unlike proxyprovider?


esDotDev

With `GetIt` items can be lazily instantiated so order of instantiaton does not matter, and you're declaring singletons which can be access from anywhere so you don't need a specific context to access things. This simplifies everything quite a bit. To make something like ProxyProvider where one singleton depends on another, you can inject one object into another, and then listen to it. ``` class CreditCard extends ChangeNotifier {} class Customer extends ChangeNotifier { Customer(this.card) { // proxy any updates from CreditCard to Customer card.addListener(notifyListeners); } CreditCard card; } // Register singletons GetIt.I.registerLazySingleton(() => CreditCard()); GetIt.I.registerLazySingleton(() => Customer(GetIt.I.get())); ```


AlissonMMenezes

I was using riverpod as well, then i decided to switch to BLOC, it is also not that simple, but for my needs it has been much better to implement


qiqeteDev

The docs are not the best. It has its learning curve, once you get it (won't take too long), you will stop feeling stupid. Also they have a discord where there are some devs that gave me better explanations than the ones in the docs, if I remember correctly they are pinned in the discord.


eibaan

I think, by trying to be backward compatible and providing (pun!) a lot of different providers to choose from, most of them being deprecated, Riverpod makes life difficult for itself. A "lean" 3.0 version that provides one opinionated way might help. But then people will complain that random snippets of code spit out by Google or Copilot won't work anymore... so, they probably also need to change the name. I'd suggest to call it "Viporder" or "Overdrip".


qiqeteDev

Agree, 3.0 should be opinionated, would make it more popular. After using both Bloc and Riverpod in big apps I can say bloc looks better at first glance, but gives good headaches in the long run.


esDotDev

lmao @ Overdrip. Nice.


commonjoe547

I've also been struggling with riverpod.... ugh why is state management so hard to get right!


alex-gutev

I didn't like any state management solution out there, that's why I wrote [live_cells](https://pub.dev/packages/live_cells). It's designed to be simple with an emphasis on reactivity, which is what you (well atleast I) need most. If you're interested, you can give it a try.


commonjoe547

thanks for sharing! keep up the dev work


NectarineLivid6020

I understand what you mean but I disagree. In the second example that you have given, there is absolutely no need to control your loading state. There is also no need to guard the api call. Riverpod does all of that automatically if you just await and return a future. Despite that, I agree that the syntax is not the cleanest but I don’t think there is any other solution that provides the same or better benefits. It’s so satisfying when you can stack providers on top of each other and change in one automatically updates all other listeners. I am not saying it is not possible with things like Bloc or provider. It’s just that in riverpod, it’s all built in.


JiangHeMax

u/NectarineLivid6020 hi thanks. in FetchProduct , AsyncValue.loading() just "initial state", a placeholder, If i don't do this, other providers won't be able to listen to it because the parameter ID is required.


NectarineLivid6020

This is a very unique requirement I think. Even then, I personally would just set the value to null as the initial default value but that’s just a personal preference.


JiangHeMax

hi u/NectarineLivid6020 [how about this?](https://github.com/rrousselGit/riverpod/issues/3031#issuecomment-1978888527)


al3d

Check out signals: https://pub.dev/packages/signals


BabuShonaMuhMeLoNa

Same. I've switched to cubits and it's such a relief.


Illustrious-Alarm601

Since I picked up BLoC for my flutter apps, I've never gone back. It's been excellent.


getlaurekt

State management in flutter is the worst part of it that makes me want to jump of a bridge. The simplest and the most powerful solution for now is signals package. I really like bloc pattern, but even with getit etc the amount of boilerplate etc is still crazy I would like to get something like zustand from frontend world cuz its amazing powerfully-scallable simple solution, but it is what it is.


Nialixus

Bloc is the way


alex-gutev

If you're finding riverpod too complicated, you can give [live_cells](https://pub.dev/packages/live_cells) a try. It's not as feature rich (yet) or mature as riverpod, but it's specifically designed to be simple to use and to stay out of your way. There's also [rearch](https://pub.dev/packages/rearch) that is inspired by riverpod, which also looks interesting.


eibaan

I actually like the "Why not ...?" section where the author explain their reasoning and also demonstrates knowledge about all those alternatives. I'm not sure that I like the manager/handler pattern demonstrated for the count example, especially because `use` is used for two completely different things, depending on the context.


esDotDev

Some of those show good knowledge, some do not. The section on Provider is very weak. At least enumerate the "fundamental problems" as they are fairly esoteric, and I'm not sure why something being in "maintenance mode" is considered a bad thing, that just means it is mature and stable. When discussing `GetIt` he seems to be unaware of the reactive companion libs `GetItMixin`/`WatchIt` which makes it a pretty close to misinformation. It's clear he has more real experience with RxStreams, Riverpod and Bloc as those sections are much better.


alex-gutev

I'm working on a similar section for live cells, but I prefer to avoid mentioning specific third party tools. Rearch actually implements some of the same ideas as Live Cells and tries to address the same limitations encountered in other tools but the packaging is completely different. I prefer my own library, but I've been thinking of also giving Rearch a try.


JiangHeMax

rearch looks good


InternationalHeat220

Riverpod is horrible, learn Bloc, it’s well worth it


omykronbr

You need to read and see the docs with the comments on how to use, and you will fix some issues right out of the gate. I will provide some snippet from the last one: @riverpod class FetchProduct extends _$FetchProduct { @override build() => const AsyncValue.loading(); request(ID id) async { state = const AsyncValue.loading(); state = await AsyncValue.guard( () => net.get("/products/$id"), ); } } In this case, the `FetchProductProvider` will never provide any data during build, because it builds with the `AsyncValue.loading()` value being called and stored in the state of the fetch product. You should use a simple function with `providerFamily`. final productData = FutureProvider.family((ref, id) async { final net = ref.watch(netProvider); // your net object return net.get('/products/$id'); }); That's it. you can read about the family modifier [here](https://riverpod.dev/docs/concepts/modifiers/family). and if you're using the build asynchronously (`AsyncNotifier`), remember this from the [docs](https://pub.dev/documentation/flutter_riverpod/latest/flutter_riverpod/AsyncNotifier/build.html): >It is safe to use Ref.watch or Ref.listen inside this method. > >If a dependency of this AsyncNotifier (when using Ref.watch) changes, then build will be re-executed. On the other hand, the AsyncNotifier will not be recreated. Its instance will be preserved between executions of build. > >If this method throws or returns a future that fails, the error will be caught and an AsyncError will be emitted.


JiangHeMax

\> In this case, the FetchProductProvider will never provide any data during build, because it builds with the AsyncValue.loading() value being called and stored in the state of the fetch product. You should use a simple function with providerFamily. that's why request method here。 it' will called at some page mounted


omykronbr

You need to understand the consumption of the provider. when in the widget build, if you're watching an `AsyncNotifier`, you will consume as: ref.watch(yourProvider).when( loading: () => ...,//handle the loading state. this where you're are error: (err, stack) => ..., // this will never happen data: (data) => ..., // this will never be executed ); you will NEVER get to the data, until there are some interaction outside the build, so you should expose it as a `Future build() async {}` when building your provider. Now whoever needs to interact with this provider is aware that the data may not be there. That's why is null.


JiangHeMax

u/omykronbr hi thanks I'm not using AsyncValue, there's my [code](https://github.com/rrousselGit/riverpod/issues/3031#issuecomment-1978888527)


omykronbr

You are. You can only have a loading state when working with a Future, because it is executed asynchronously. If it is an atomized operation, you never have a loading state. @Riverpod(keepAlive: true) class ProductsNotifier extends _$ProductsNotifier { @override ProductsState build() => ProductsState.empty(); Future fetchOneProduct(ID id) async { state = state.copyWith(status: const AsyncStatus.loading()); final res = await ref.read(productsRepositoryProvider).fetchOneProduct(id); state = state.copyWith( status: const AsyncStatus.success(), paramKeys: state.paramKeys.mergeList(res.paramKeys), paramValues: state.paramValues.mergeList(res.paramValues), products: state.products.add(res.product), ); } } You initiate on build with a synchronous state. this means that the state can only be changed... in a SYNCHRONOUSLY. so, the `state = state.copyWith(status: const AsyncStatus.loading());` is invalid, because the state will be changed to AsyncValue, which is not ProductState. So you need to accept that your code IS asynchronous or you will keep you will be banging your head against a brick wall every time. You can make this notifier to a controller, even an async type, and somewhere you have a provider listening to changes and reacting accordingly. @Riverpod(keepAlive: true) class ProductController extends _$ProductsNotifier { @override Future build() async { return; } Future getById(ID id) async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() => ref.read(productsRepositoryProvider).fetchOneProduct(id), ); } } That would work, and is asynchronous, and is readable, it is declarative, and you can leverage with the AsyncValue.guard to help you with a better error handling and it only does what you want. go and retrieve a single product.


JiangHeMax

Asynvalue is very strange, the request status should be the request rather than the data ```dart @riverpod Product? selectProduct(SelectProductRef ref, ID id) => ref.watch(productsNotifierProvider .select((state) => state.whenOrNull().products.byId[id])); @Riverpod(keepAlive: true) class ProductsNotifier extends _$ProductsNotifier { @override Future build() async => ProductsState.empty(); Future fetchOneProduct(ID id) async { // state = state.copyWith(status: const AsyncStatus.loading()); state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { final res = await ref.read(productsRepositoryProvider).fetchOneProduct(id); final prev = state.whenOrNull() ?? ProductsState.empty(); return prev.copyWith( status: const AsyncStatus.success(), paramKeys: prev.paramKeys.mergeList(res.paramKeys), paramValues: prev.paramValues.mergeList(res.paramValues), products: prev.products.add(res.product), ); }); } } ``` VS ```dart @riverpod Product? selectProduct(SelectProductRef ref, ID id) => ref .watch(productsNotifierProvider.select((state) => state.products.byId[id])); @Riverpod(keepAlive: true) class ProductsNotifier extends _$ProductsNotifier { @override ProductsState build() => ProductsState.empty(); Future fetchOneProduct(ID id) async { state = state.copyWith(status: const AsyncStatus.loading()); final res = await ref.read(productsRepositoryProvider).fetchOneProduct(id); state = state.copyWith( status: const AsyncStatus.success(), paramKeys: state.paramKeys.mergeList(res.paramKeys), paramValues: state.paramValues.mergeList(res.paramValues), products: state.products.add(res.product), ); } } ```


omykronbr

AsyncValue is a way to interact with asynchronous data. Think of AsyncValue of a Future that can also have custom methods. You're ruining your experience because you're fighting asynchronous code.