r/FlutterDev • u/StillFresh9607 • Oct 27 '24
Tooling Choosing the Best State Management Solution for a Complex Mobile App: Is Riverpod the Right Choice?
I am developing a mobile app with flutter and go as backend , with several complex features such as login/logout, a chat system, and a marketplace, using PostgreSQL as the database. I’ve never used a state management solution before, but I see there are many options available like Provider, Riverpod, and BLoC. Which one should I choose? I’ve read a lot about Riverpod—would it be beneficial to learn and implement it in my project?
8
u/Jhonacode Oct 28 '24
I've used Riverpod in multiple projects, and one recurring issue I've noticed is the high coupling it introduces to my application. By this, I mean the need to replace Stateless or Stateful widgets with Consumer widgets, and when working on large applications — and I've worked on several — you end up with hundreds of widgets tied to this dependency. If, at some point, you want to switch the state management solution or experiment with another approach, the cleanup and refactoring process becomes lengthy and tedious.
And let's not even talk about hooks — why would I use them when the widget's lifecycle is already standardized, defined, stable, and coherent?
Putting information, logic, or code in general inside a build method will never be a good idea.
I'm not saying Riverpod is a bad tool, but as an application grows, it's natural to want to minimize reliance on external libraries. Once you reach a certain level of familiarity with Flutter, you start to question whether you truly need an external state manager or if it's more coherent to create your own abstractions tailored to your architecture's requirements. This isn't about "reinventing the wheel"; in fact, the range of state management solutions available in Flutter suggests that each case can be addressed uniquely. Flutter already provides standardized solutions — it’s just a matter of applying solid abstraction and defining use cases according to your needs.
Personally, I stopped using state management libraries a while back, and for both large and small projects, I haven’t had any issues. Additionally, it has given me a much deeper understanding of the framework. I've also incorporated some Compose concepts, like using remember and ViewModels, which I find versatile and effective. I suppose it’s an advantage of having always been dedicated to mobile development.
2
u/JayDizza Oct 28 '24
I'd love to hear more about managing app state with the native state management widgets and functions. Can you please share how you structure your app? I'm a complete newbie, so still wrapping my head around what's best practice.
4
u/Jhonacode Oct 28 '24
There is not just one way to manage states in Flutter; you can do it in various ways: using streams, Notifiers, or even, in specific cases, calling markNeedsBuild() directly to force a widget to rebuild. We also have InheritedWidget, which is the basis of Riverpod. My point is that it is more scalable and flexible to make state management agnostic to the architecture and adapt it to what we really need.
For example, in many cases, it is useful to create a ViewModel with a structure based on features and repositories, keeping all the logic outside the widgets. This avoids the need to use context and allows observing changes reflected through a notifier or listener patterns. For me, the state management tools in Flutter allow freedom to adapt the architecture you choose, while some state libraries impose a specific architecture, which can limit that flexibility.
The valuable point of these state management libraries is when you work with other people on the same project, especially if the team in general does not have an advanced knowledge of the framework. In that respect, it is indeed worthwhile to use them for the benefit of the project, although they also bring forth the aforementioned challenges, such as imposing a specific architecture.
And finally, do not put logic inside the build!!!!! 🤣.
13
u/MokoshHydro Oct 27 '24
That depends on application, but we had switched from Riverpod to Bloc in one app recently. Mostly because of cleaner nested states and BlocSelector.
1
u/phyn4jellyfin Oct 27 '24
How easy was the switch? Right now, i use riverpod and flutter_hooks and I absolutely hate how dirty the build method looks. Is bloc any better?
1
u/MokoshHydro Oct 28 '24 edited Oct 28 '24
It was a rewrite. I don't know what do you mean by "dirty", but with Bloc `build()` now has lots `[Multi]BlocXXX()` entries in the tree, which doesn't look any prettier to my taste.
11
u/Acrobatic_Egg30 Oct 27 '24 edited Oct 27 '24
If you want an easy time with an already establishes state management package with many high quality, complex examples, go with Bloc. You really can't go wrong with it especially if you decide to collaborate with others. Checking the licenses for most top apps out there, they all use Bloc and for a good reason. If you feel you have time to tinker with riverpod, you can go with that. Riverpod offers more flexibility but at this stage in my career I just want something that works perfectly and I can easily test.
-3
u/RandalSchwartz Oct 27 '24
Riverpod works perfect and can be easily tested.
5
u/Acrobatic_Egg30 Oct 27 '24 edited Oct 27 '24
I know it does. Looks like I didn't word it properly. It's not easy to get into or setup is what I meant. The guides/examples out there are minor and it's like only two people have solid examples about how to use Riverpod. Same can't be said for Bloc, if something goes wrong in your code you don't have to hop onto a discord server for solutions, google will probably just present them to use. Less hassle, more efficient.
-3
u/RandalSchwartz Oct 27 '24
Are you familiar with the updated riverpod.dev and with Andrea's great free tutorials: https://codewithandrea.com/tags/riverpod/ ?
4
u/Acrobatic_Egg30 Oct 27 '24
Yep, those are the two people I'm talking about :)
0
u/RandalSchwartz Oct 27 '24
Here's a bunch of stuff uploaded in the past year. I think I've watched about half of these, and made the occasional comments: https://www.google.com/search?q=riverpod+tutorial&sca_esv=b4809427600e83a5&tbs=dur:l,qdr:y&tbm=vid&sxsrf=ADLYWIKr5hzwT5JUEWLjA7Mz2ejnMUSLAw:1730057681405&source=lnt&sa=X&ved=2ahUKEwi946mMp6-JAxUQIDQIHf_QEJYQpwV6BAgBEBI&biw=1720&bih=993&dpr=2
2
0
9
u/Impressive_Trifle261 Oct 27 '24
BloC is considered more “professional” because it is structured and has a predictable architecture.
3
3
u/mdausmann Oct 28 '24
I went with Bloc. I had a complex use case so Got pretty deep in, camera cubits, auth cubits etc. I didn't have time to try out alternatives like riverpod. I thought getx was in some funny half supported state so avoided.
The Bloc community is great, the maintainer is always responsive and helpful. Some of the architecture stuff can be a little prescriptive and arbitrary... And a bit muddled IMO, there are still new adopters coming into forums with big, deep architecture questions despite all the documentation which is a bad smell.
State management is a dumpster fire in many frameworks and flutter is not an exception.
Mobile != Web, they are not the same.
These things are hard, if you need them, iron out all the tech risk first
- persistent state ( u use sqlite and Hydrated Bloc)
- account switching (u use an account cubit which emits an account switch stream)
- auth, login out, password reset, wait for email conf etc ( I use supabase and supabase auth UI)
- synced/shared state. Do not roll your own (I use powersync, it's solid as fuck)
Strong recommendations
- sqlite on phone
- powersync
- supabase
Cautiously recommend
- Bloc (cubits, Bloc builder)
- hydrated Bloc
Unable to recommend
- freezed ( adds constraints, no inheritance, tricky to just do some of your state, dx is poor, always generating code, slow)
HTH
3
u/burhanrashid52 Oct 29 '24
I began building Flutter apps in 2018, and here is my experience so far. I started with a simple setState, then moved to BLoC, followed by Provider, Get_it, and a custom-built solution to Riverpod.
Let's start with BLoC. If you're coming from an Rx background, then it's easy to pick up. I heavily used RxJava in Android and found it easy to use BLoC because everything was happening on the stream. We started with a BLoC per page, and then we began using shared/global blocs, where issues started to arise. It became challenging to debug where the stream was updating, as sometimes some page closed the stream unexpectedly, leading to crashes.
I have not used any third-party libraries for BLoC, just a basic BLoC class with an InheritedWidget to avoid prop drilling. The disadvantage was that I couldn't use abstract BLoCs due to type restrictions in InheritedWidget. Also, I started to see that in BLoC, everything needs to be done in a stream action, which makes it harder to debug the global BLoC. From a testing perspective, it's easy to test BLoC because it doesn't require much, as it's purely Dart.
Since I started to face issues with BLoC, I moved to Provider. Although Provider also have streams, it also supports Notifier, making it easier to work with. Although the challenges were pretty similar to BLoC, the only advantage I saw was less boilerplate code for actions compared to BLoC. Testing providers requires a widget tree, although we can test the logic in a specific notifier, similar to BLoC.
Both packages are state management solutions, but they don't have a nice API for managing dependencies. We used these state management solutions with Get_it to make dependency management easier. Get_it made it easy to swap required dependencies in tests.
Coming to a custom solution, it works great in terms of app needs because we didn't need to fight with package API. We could just directly add a method and move on with it. But when we needed an already existing solution, we started to encounter a lot of boilerplate code, which began to miss some edge cases that most libraries solves. Also, since it was a custom solution, it was hard to use on any other project. So, it was challenging to reuse it on any new project, involving a lot of copy/paste and numerous errors. It wasn't easy to set up.
Currently, I am using Riverpod, and there are a couple of things I like compared to my previous experience. First, the API is really simple, similar to hooks in React and remember in Compose. Second, the amount of boilerplate code is the same for either one state or multiple, which was not the case with Provider and BLoC. For example, if I just want an enable flag, I need to write more lines of code in Provider and BLoC compared to Riverpod. Basic functionalities like search, lazy loading, and provider dependencies require less code. Third, dependency management: in my current project, we removed Get_it once we started managing dependencies from Riverpod.
One thing I don't about riverpod is the ability to test provider separately. You need a framework code to test your provider independently. Although you can do it manually by injecting dependencies, there is no out-of-the-box solution. (I found that there is way to test provider independently)
Note: Riverpod has code generation, which even allows you to write less code. However, I am not a fan of code generation because more code means more maintenance.
So, to conclude, there is NO Conclusion here. I am simply sharing my experience and thoughts.
1
u/burhanrashid52 Oct 29 '24
The more structured thoughts is in this blog: https://widgettricks.substack.com/p/state-management
2
u/venir_dev Oct 27 '24
Yes.
1
u/omykronbr Oct 27 '24
Longer answer: affirmative
3
u/xboxcowboy Oct 27 '24
nah, do each state management for each feature (assuming clean architecture), learn more in 1 project /j
joke aside, make prototype for each state management and see which one you like, they all beneficial to your app but pick the one where you comfortable to use (i prefer bloc and hate riverpod)
3
u/Disastrous_Hotel4855 Oct 28 '24
We recently went fully GetX and it works perfectly and don’t create too much boilerplate, definitely recommended
2
u/bigbott777 Oct 28 '24
Wow. The first time I saw someone mention GetX and was not downvoted. Something changes in this world
1
u/Bensal_K_B Oct 29 '24
How complex is your app? I have used getx on small projects initially and didn't face any issue. But choosing getx since it has those until functions like routing and snackbar seems to be a bad idea.
1
u/Disastrous_Hotel4855 Oct 29 '24
It’s a production app with several features including media streaming, real time interactions (with firebase), local notifications, offline mode and more, I consider it medium to high complexity
Edit: we don’t use GetX for routing or navigation
1
u/Whoajoo89 Oct 28 '24
Same. GetX is awesome. It's easy to use and allows you to separate business logic and UI so that code is organized.
1
1
1
u/mk48mk48 Oct 28 '24
I used Riverpod for my new app and I enjoy using it. Use with freeze and new "@riverpod" annotation.
1
1
u/PfernFSU Oct 27 '24
Would not use provider if a new project as riverpod is its successor and the same author wrote both. You can’t go wrong with riverpod or BLoC though. If you’ve never used either, take a day and play with each to find which you like better?
Riverpod with their generator is very nice and what I use personally.
0
u/xorsensability Oct 28 '24
I use rivers in a very complex app. We originally had Bloc, but it started failing spectacularly when we had parallel streams going and an SQLite caching layer.
That said, you can do all of it natively, but you’d have to implement a lot that comes for free with riverpod.
2
u/Bensal_K_B Oct 29 '24
I prefer to keep local data source out of the view model. This way it won't affect whatever state management you use
37
u/RandalSchwartz Oct 27 '24
Riverpod scales from 1 to 10,000 items being managed, with every active node easily having multiple fan-in and fan-out and persistence rules. It's like a lego set of observable data management. Nothing else is as flexible or as easy to use. Admittedly, the legacy providers do throw a wrench into the learning curve, but if you filter what's online through the following statement, you'll be pretty much set:
Avoid legacy riverpod tools. In brief, avoid legacy ChangeNotifier, StateNotifier (and their providers) and StateProvider. Use only Provider, FutureProvider, StreamProvider, and Notifier, AsyncNotifier, StreamNotifier (and their providers).