We can use StatefulBuilder to update a specific element on the UI of a StatelessWidget and in other hand, we can archive similar result moving the StatelessWidget to a StatefulWidget and getting rid of the StatefulBuilder widget.
I prefer StatefulBuilder because it makes the code much easier and cleaner, but what's the best in terms of performance?
A: Wrap everything on a StatefulBuilder using StatelessWidget
B: Use StatefulWidget
There is effectively no difference in performance between the two. Both involve the creation of a State object.
Given that your using global state, there is no real reason to use a StatefulBuilder relating to part of that existing global state. The StatefulBuilder provides a mechanism to have its own associated State object with state you define and can mutate. In your case (global data), your state is already in memory, so you might as well just mutate it in your StatefulBuilder builder callback, e.g.
setState(() => myGlobalCounter.increment());
You are likely aware, that one would normally prefer an InheritedWidget based state solution. While this can be considered equivalent in some ways to just moving global state to a global tree location, it more easily allows for all interested Widgets to be notified of changes to that state.
Related
Problem: build() depends in StatefulWidget's state
Recently I moved a property inspector widget from being an always visible part of the UI to a Drawer.
Once I moved the property inspector, the app rendered random update operations regarding the object when the property inspector was about to edit values: The property inspector updated a random other object, not the current 'focus' object.
Solution : Make build() depend on the State<T> of StatefulWidget
I solved the issue by making the State<PropertyInspector> solely depend on state variables defined inside State<PropertyInspector> itself - instead of defining state variables in StatefulWidget and accessing these variables using widget.<someVariable>.
Root cause of the problem seems to be the fact that the Property Inspector in the Drawer gets more updates from the framework than the permanently visible alternative.
General advice
In general, Flutter needs to insert / remove / update StatefulWidget at various moments of time. While StatefulWidget might change, its State<T> object persists.
Thus, the build() method of a StatefulWidget should depend on State<T> - and not on the changing StatefulWidget.
Rules of thumb
All instance variables of StatefulWidget should be final
Thus, if an instance variable of StatefulWidget can't be marked as final, the design of the StatefulWidget is wrong.
From within State<T>, access to 'widget.*' instance variables are fine, as long as those variables are final.
Instance variables of State<T> may be non-final
Is my description correct?
This site doesn't warn about using state as widget.*. Isn't that wrong? or at least dangerous / bad style?
As far as I understand, the proposed state in StatefulWidget might work fine for some time. But, under different conditions, it is likely to render problems (as mine).
Helpful video How Stateful Widgets Are Used Best - Flutter Widgets 101 Ep. 2 in Flutter's StatefulWidget class documentation.
Contradiction
This lint error contradicts my explanation: no_logic_in_create_state
Update
I'd be happy if answers would explain, if this description / practice is correct or not.
I established this practice since certain widgets didn't behave as expected in some projects.
If I have understood your description properly then I would say your rules of thumb are correct. The state class and fields persist across renders, therefore any mutable state should be enclosed within the state class not within the widget class.
The lint no_logic_in_create_state doesn't contradict this, the lint is saying no logic in the create state method, which is in the widget class.
Any mutable field in the widget class could be reset when the widget re-renders and therefore no mutable state should be kept in any widget.
I will also add that just because an instance variable in a widget is final doesn't mean it wont change within the lifecycle of the app. If you pass a prop into the widget then the instance variable would update if the prop updates (the widget would be recreated but the state wouldn't).
I heard a lot about "flutter method-widgets is considered anti-pattern". But why? Elements don't care about widgets' parameters (equal or not). Only const widgets save rebuilds. So why do I have to separate them to relatively verbose classes, when I could just
Widget _buildMe() => Container() // here is some widget, which depends on other params (so it cannot be const)
inside my StatelessWidget class.
Update: I also assume that methods don't depend on InheritedWidget. In that case it's obvious, that they should be separated.
It's an anti-pattern, because in some cases it leads to unnecessary rebuilds (depending on Inherited widget for example). But in general you can use it, if you use it wisely (2-3 methods in class don't make any harm).
I am wondering what will happen if I define a StatelessWidget but return a stateful Widget from its build method? I've tried it and everything seems to be working, but I just want to know what is going on behind the scene so that I can be sure nothing will break when I ship it into production. Specifically:
1) I am wondering if every rebuild of the parent StatelessWidget will trigger a rebuild of the StatefulWidget it returns? If so, is it saying I am effectively returning a StatelessWidget?
2) I am wondering if the parent StatelessWidget will still be in the widget tree, given it is merely a wrapper and does not have any visual element to be rendered?
3) If I want to give the child StatefulWidget a Key, should I give the parent StatelessWidget the same key? Or, should I just put the key on the parent StatelessWidget?
Mixing Stateless and Stateful is a very, very, very common use-case.
The answer is relatively simple: Nothing special happens.
Stateless+Stateful is the same as Stateless*2 or Stateful*2. There's no behavior change and no extra code needed.
I am wondering if every rebuild of the parent StatelessWidget will trigger a rebuild of the StatefulWidget it returns? If so, is it saying I am effectively returning a StatelessWidget?
No. Each widget is independent and can rebuild without forcing other widgets to rebuild.
A child rebuilding won't make the parent rebuild. Similarly, a parent rebuilding doesn't necessarily force the child to rebuild either.
I am wondering if the parent StatelessWidget will still be in the widget tree, given it is merely a wrapper and does not have any visual element to be rendered?
Yes, a StatelessWidget is still in the tree.
No, it is not "just a wrapper." A StatelessWidget can use InheritedWidgets and override ==.
These can cause the widget to rebuild independently from other widgets. And as such, this widget must say in the tree.
It even has a setState equivalent; it's just not public.
If I want to give the child StatefulWidget a Key, should I give the parent StatelessWidget the same key? Or, should I just put the key on the parent StatelessWidget?
No. That's not needed.
If the key is on a widget, this will impact its entire subtree. So there's no need to put it on descendants too.
In fact, you can't, depending on the Key. GlobalKey, for example, requires to be unique.
I always aim to make my widgets Stateless instead Stateful for performance benefits. In some cases(updating the BottomNavigationBar index e.g.) notifyListeners() can provide identical functionality of the setState().
At first, I think notifyListener() is lower level, more fundemantal function comparing to setState(), so it should be more efficient. Because setState() method may triggers too many higher level framework methods, so it may spends more CPU power.
But it's hard to be sure without making a proper and detailed performance testing. So what is the answer?
Edit: Also, in some cases, notifyListeners() behaves exactly like setState(). For example, I have Text widget inside a StatelessWidget that holds a Random value and when I notify the an unrelated value inside the Class, the Text widget is also getting update. So, what is the difference?
Assuming that you're comparing ChangeNotifier.notifyListener with State.setState to rebuild the widget tree, then:
setState will always win.
The reason, notifyListener rebuilds your widget tree because it causes setState itself.
For notifyListener to work, there's usually a StatefulWidget somewhere in your tree that does the following:
class MyState extends State<T> {
ChangeNotifier notifier;
initState() {
notifier.addListener(() => setState(() {}));
}
}
In any case, this probably doesn't matter.
I would like to know the best way to pass the bloc. I read about the bloc providers, but what`s the difference between using them and just passing the bloc in the constructor like:
ExampleView X = ExampleView(bloc,...)
Actually I find this way easier to test and also a better way to keep the code cleaner. For example, if I have more blocs, something like this can happen:
XBlocProvider(
bloc: XBloc,
child: YBlocProvider(
bloc: Y,
child: ZBlocProvider...
)
or maybe it's just my lack of knowledge.
So, which ones are the benefits?
The problem with this:
ExampleView X = ExampleView(bloc,...)
It only make sense if you use your bloc as normal class/provider with some Stream\ValueNotifier. Otherwise it has some issues.
If it's global bloc, it's more exhausting way to pass it. You should use XBlocProvider on top of MaterialApp.
By the way, if it's global/top level bloc, you can do this:
XBlocProvider(
bloc: xBloc, // Singleton
child XPage,
...
This way, you can access this bloc from anywhere of the application and also you can listen it.
If it's local bloc, because the way we listen Bloc or ChangeNotifierProvider via InheritedWidget's updateShouldNotify method, it doesn't make sense to pass as constructor because you can't use it directly as you intended. You need to put that instance inside BlocProvider and consume it again, so it's extra work.
https://api.flutter.dev/flutter/widgets/InheritedWidget/updateShouldNotify.html
To overcome multi nested BlocProviders you can use MultiProvider or MultiBlocProvider.
Example:
MultiBlocProvider(
providers: [
XProvider(...),
YProvider(...),
ZProvider(...),
],
child: someWidget,
)
There are multi ways to pass depends on your need but don't worry about InheritedWidget because it's pretty fast and convenient way to obtain your XBlocProvider.
At the end, try to comprehend every approach, I especifically suggest you to grasp this article:
https://www.didierboelens.com/2018/12/reactive-programming---streams---bloc---practical-use-cases/
You'll get the idea when to use bloc with provider or as a singleton or instantiate like your example etc.
The first difference is how you access your bloc from the widget.
when you pass it through the constructor you have direct access to it
when you use BlocProvider then, in most cases depending on your
bloc implementation, you have obtain it via Provider which extends InheritedWidget
using context for example:
final xBloc = Provider.of<XBloc>(context);
What's more when you wrap your bloc with BlocProvider then your bloc's scope is limited to this widget's subtree so it can be only accessed by the BlocProviders descendants.
Not having to pass the bloc in the constructor is the benefit of using the provider. It reduces the complexity of your application by enabling you to propagate information down the widget tree with the help of InheritedWidget. All you just need is to access the bloc from the child widget with BlocProvider.of(context).