-
About state management in the example I see that riverpod is used in the example, why not getx? I see a lot of noise in the community about the comparison between riverpod and getx, although it has nothing to do with flex_color_schema, but I would like to ask about the difference between the two and the performance gap, stability in production, etc |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
Hi @laterdayi, Good question. Although, which example are you referring to? The FlexColorScheme repository examples? Or perhaps the stand alone Riverpod example? I’ll answer for both of them 😄 FlexColorScheme repository examplesThe tutorial examples in the FlexColorScheme repo itself only use Flutter SDK features for their state management. They are using the so called skeleton architecture from the Flutter team. Not using any package for state management was done on purpose to avoid the state management debate in the examples and tutorials in FlexColorScheme. In the FlexColorScheme docs, in its tutorial section it is discussed here https://docs.flexcolorscheme.com/tutorial2. It is quite interesting that it has worked as well as it has all the way to example5, which is the current version of the Themes Playground. To be honest though, I would not recommend it for something of the scale of the Themes Playground for managing state with as many properties as the Playground has for its dynamic theme, for pretty much anything else than the theme, and even there it is a bit questionable in some cases. If I were to intentionally build something like the Themes Playground I would probably not use the current solution. Or if I would, maybe design it with a bit more granularity at least 😄 Originally example 5 was only a tiny bit more complex than example 4 in the tutorial, to show how you could add a few more dynamic properties to your app theme. It did not have hundreds of in app modifiable properties. But it kind of grew out of hand and I never bothered to change it. I also wanted to test how far you can take this simple architecture for dynamic themes. The used solution will rebuild the theme as soon as one of its single theming properties change. However, in these apps that is actually what we want, as soon as a single theme property is modified, I want the example apps to rebuild, so that you see the change immediately in the surrounding app, that also uses the modified theme, as you change the theme value. When the values are not changed, the MaterialApp is not being rebuilt, so the calls to make new ThemeData are not used either. And in many cases when only a few properties have been changed that do not impact the UI globally, Flutter is actually smart enough to minimize the UI redraws to only modified parts. I’ve studied this in the Playground app with rainbow repaints feature, and it surprised me how well it does that. But in many cases when you change the theme, like e.g. switching from light to dark mode, the entire app must be rebuilt anyway to show you the new design. This is why this simplistic design actually works very well for the app theme. You can make it more granular by introducing more ChangeNotifiers and ValueNotifiers. And I might in fact do so for a few cases. Flutter SDK also caches a few last used ThemeData objects, and tries to reuse them if a used theme matches a cached one. However, with the MaterialState callbacks being used in custom themes, I am not sure that works so well, since it seems to break equality checks. I should look into this more and discuss it with the Flutter team, if it is indeed so. The idea with the ThemeData cache is to avoid recomputing ThemeData objects when not needed, since the actual computation of ThemeData can be a bit costly. The color algorithms used by e.g Material3 are not exactly super cheap. Typically apps only use a few static themes that you switch between, and the cost of course only happens when you change those theme styles and compute ThemeData, typically only when swapping between light/dark theme in most apps, or compute a new ThemeData object for whatever reason. In the Playground you can change so many things that the chance of hitting the cache is quite slim, expect when you toggle light/dark mode. So yes when you change settings it will calculate new ThemeData. When the theme is not being modified, the MaterialApp is not modified and not rebuilt. This applies to any state management that you use as it is intended and designed to be used. So the choice of used state management for the theme will not impact the performance of the app itself when you are not changing the theme. Depending on how interactively you want the theme to rebuild, you can impact this by not reacting to every change immediately. Building such granularity is certainly easier by using most package state management solutions. But as mentioned you can also do it with the architecture used here. For theme state management though, the used state management is unlikely to impact the performance of the app at all in any meaningful way. What matters is when you trigger a rebuild and when you want that to happen. Calculating the new ThemeData object is heavier than any state management overhead. What will impact things is when you trigger the rebuild, this depends on what you need for your app, but typically users expect to see the result as you modify a value, and for ThemeData that does mean triggering a rebuild of the entire MaterialApp. What then actually repaints as a result of it, depends a lot of what is changed in the new ThemeData and how that effects changes in used widgets and their need to repaint based on that. Control over triggering the MaterialApp rebuild when you change theming properties is another topic. Some values can be changed multiple times in rapid succession in the Playground, triggering a lot of MaterialApp rebuilds. In the Playground this in particular applies to sliders where you can rapidly change props that can visually modify a lot of widgets that causes a lot to rebuild and repaint interactively. The global border radius setting is a good example. I could e.g. choose to only rebuild when the slider movements stops for any sliders, but I have opted not to do so, so that users can interactively see the changes in the resulting app itself as they change a theme value with a slider. A design choice for these example apps, made on purpose. Where the example apps do more performance related optimization is for persisting the theme settings. Typically you could opt to serialize a big state holding all the current setting values and persist all of it as anything changed. With the hundreds of props used in the example 5/Playground app, that becomes a rather large string that needs to be serialized and rewritten every time any property value changes. This is why this implementation have opted to use a very tedious design, where every theme value setting has it its own key and value in the local storage, and only updates that value when it is actually changed. A simpler approach could be to time debounce the change and persist all as one big JSON string with all the latest settings, after things have stopped changing for a few seconds. Sure everything is not persisted at once then, only after the debounce time, but for most practical use cases, I don’t think it would matter that much. If the serialization and save would take too long, it might need to run in an isolate to not block the UI. The current solution does fine without that. Riverpod exampleI do have a separate Riverpod example that is not a part of the FlexColorScheme repo here https://github.com/rydmike/theme_demo Maybe that is the example you were referring to? I wrote it mostly to test some "a bit more exotic" features of Riverpod, like using it also as a service locator for the used key-value persistence implementation and making it possible to change this implementation dynamically from inside the app. Something you would typically only do in code for testing. This part could also be done another simpler way with Riverpod. I should probably make a fork to show that too, and maybe demo using code generation in Riverpod too. Also the Riverpod example has some rather unique design criteria, similar to Playground, that is also explained in its readme. State managementThe idea with not having any state management in the FlexColorScheme repository examples, is to avoid the debate and let users use other sources to study that and decide for themselves what they want to use. You can do a lot with just Flutter SDK, you don’t necessarily need a state management package, especially not for the theme. Still, state management package are certainly nicer to use to implement complex architectures, some of them even come with their own opinionated architecture. Just for fun I sometimes publish statistics about popular state management packages in Flutter https://x.com/RydMike/status/1715404003951943932?s=20. I try to be fairly neutral in my comments to it. If it is of interest, by reading responses to it and following it back to older versions of the stats tweets you can find a lot of discussions about pros and cons of different solutions. I won’t go into that here. Personally I think the Flutter SDK should have come with a more opinionated architecture for building larger apps and a state management solution that makes using it easy, and thus the preferred choice for most devs. The current situation creates sub-optimal fragmentation for devs that have deal with a lot of different code bases. But it is a bit late for that, the fragmentation already exists. Had a good solution been offered from the start out of the box, there would have been no need or at least less incentive to create so many different approaches. Sure they still could have been created and some would have been made and probably become semi successful, but most devs and projects would probably just have stuck to what came out of the box. There are a lot of building blocks in the Flutter SDK to manage state and build an app architecture too, but they are a bit low level and no opinionated ready made architecture, so hence all the higher abstractions made on top of them. |
Beta Was this translation helpful? Give feedback.
-
Yes, there are already many state management architectures, which cause a lot of problems. getx can simply store a lot of data in a controller, while riverpod uses each data as a provider, which will increase the amount of code. But getx and riverpod have a lot of different voices in the community, and if they both perform equally well, I prefer simplicity and security |
Beta Was this translation helpful? Give feedback.
Hi @laterdayi,
Good question. Although, which example are you referring to? The FlexColorScheme repository examples? Or perhaps the stand alone Riverpod example? I’ll answer for both of them 😄
FlexColorScheme repository examples
The tutorial examples in the FlexColorScheme repo itself only use Flutter SDK features for their state management. They are using the so called skeleton architecture from the Flutter team.
Not using any package for state management was done on purpose to avoid the state management debate in the examples and tutorials in FlexColorScheme. In the FlexColorScheme docs, in its tutorial section it is discussed here https://docs.flexcolorscheme.com/tutorial2.
It is quite…