-
Notifications
You must be signed in to change notification settings - Fork 28.5k
FAQ request: why is the build() method on State, and not StatefulWidget ? #8794
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
If we put the class MyStatefulWidget extends Widget {
...
Widget build(BuildContext context, covariant State state);
} If you defined a closure in that function, the closure would implicitly capture class MyButton extends MyStatefulWidget {
...
final Color color;
@override
Widget build(BuildContext context, MyButtonState state) {
... () { print("color: $color"); } ...
}
} For example, suppose the parent builds In contrast, with the class MyButtonState extends State<MyButton> {
...
@override
Widget build(BuildContext context) {
... () { print("color: ${config.color}"); } ...
}
} Now when the parent rebuilds |
Let me know if that answer makes sense. |
Thanks! I'm going to challenge us to try to come up with the two-sentence explanation (maybe not the full reason, but enough to convince yourself that the difference is well-meaning, well thought out). I can start to understand that part of the reason is the way closures work, and understanding the lifecycle of our widgets (stateful and stateless). Back to your example, I don't see why the problem of creating a closure that could implicitly keep a reference to an old value is unique to StatefulWidget. In your example, we don't refer to MyButtonState in the closure. So wouldn't we also have the problem with StatelessWidgets ?
(There's something here about Dart I probably don't understand, forgive me if I'm missing the obvious thing. :) |
With a stateful widget, it's common to make closures whose lifecycle are tied to the state's lifecycle, which lasts through multiple widgets. With a stateless widget, it's common to make closures whose lifecycle are tied to the widget's lifecycle, which doesn't cause a problem. |
Putting a For example, Conceptually, |
We should include all the above explanations in a |
Hi, @abarth . Im pretty new to this framework so even though you have provided very detailed information, im still confused about some part of it. For example, in the below quote
My understanding is that when the parent is updated, a new instance of MyButton should be recreated as well as the new closure. That should refer to the new $color as my understanding. But why is it still maintaining the old closure and capturing the old $color? This is really confusing to me, maybe it is a dart specific thing? |
That depends on what happened to the old closure. The old closure might still be retained by another object. If called, it does the wrong thing. You're right that the old closure will often be replaced by a new closure that captures the new color, but nothing guarantees that will happen. With the build method on the State, you always get the right behavior. |
The naming here is the part that is confusing to me as someone learning Flutter. The class This is so much more than "state", so coming from pretty much any other paradigm this is going to be confusing. I think it would be better for the docs to have an explanation of what the conceptual responsibilities of the pieces are. Saying "because of the closures, and lifetimes of things, etc." may help someone interested in the nitty gritty details, but is not helpful for someone trying to understand the concepts. |
I am really confused with the concept too when I read the documents for flutter. As far as I can understand from other framework, especially some front framework like If it have to, I think it is better to rename the |
I' m thinking about the result of method createState in statefulWidget, why not build in the statefulWidget using this result? |
I agree the naming convention makes this unnecessary confusing, especially coming from frameworks like React where the boundaries of what we call "state" are so clearly defined. I really hope a name change will at least be considered. |
@KamiShikkaku What would you call "state" in Flutter ideally? I'm curious about potential alternatives. |
Perhaps State should be called ViewModel, it seems to me that StatefulWidget doesn't serve much purpose except being a glue in the framework. |
Way too late but I'm going to chime in anyway in case this helps someone in the future. The problem here is that in order to support the hot reload functionality at development time without any actual specialized platform support for this feature (i.e. runtime magic) the API designers have had to jump through hoops and create a double indirection that allows what should be otherwise immutable state to be swapped out on demand. Unfortunately developers have to live with this awkwardness in their code permanently. The briefest way I can summarize the situation is: A subclass of StatefulWidget is (counterintuitively) an immutable Widget that holds configuration state along with a factory for a mutable widget state object that itself serves as a factory for the actual widget tree to be displayed by the application. It helps a little to mentally rename: StatefulWidget => StatefulWidgetFactory But these are really incorrect and should be something more like: StatefulWidget => StatefulWidget(ViewModelAndWidgetFactory)Factory It begs the question of whether this could have been made clearer by simply introducing explicit classes for the immutable widget config and a widget factory. The separation would have eliminated any ambiguity about closures. Perhaps they tried this and decided it was just too much boilerplate. Personally I think this is so confusing as is that it's going to be a barrier to some developers getting started and an ongoing annoyance for everyone else. I'm disappointed that Google didn't put more effort into this and create a real platform solution for hot reload that doesn't impose this extremely counterintuitive pattern on developers. |
It's like fighting your own language, paradigm (OOP) and design decisions. Why not expose higher level API to hide the inconsistencies? So that for a developer StatelessWidget and StatefulWidget were similar except that StatefulWidget would have additional methods for dealing with state. Or maybe merge both into one Widget and use hooks to add state like they do in React. |
I too got confused by the naming -- when I saw StatefulWidget and State, I expected the following (thinking this would be MVVM built-in into Flutter):
but unfortunately, no. It looks inverted, but not quite. Based on what I have read so far State -> WidgetController I am new to this, so don't have it all thought out, but from past UI frameworks, having a better MVVM model would be interesting. |
This issue should be addressed. I feel like it is making a not so difficult thing to understand, extremely difficult. |
It's addressed in the API docs: https://api.flutter.dev/flutter/widgets/State/build.html |
Also, I did a whole video about it: https://www.youtube.com/watch?v=dkyY9WCGMi0 |
The reason why StatefulWidget uses a separate State class and not having build method inside its body is because all fields inside a Widget are immutable, and this includes all its sub-classes. You might have noticed that StatelessWidget has its build and other associated methods defined inside it, but that was possible due to the nature of StatelessWidget which is rendered completely using the provided info, and doesn't expect any future change in its State. In the case of StatefulWidget, State information occasionally changes (or expected to change) during the course of the app, thus this information isn't suitable for storage in a final field (build) to satisfy Widget class conditions (all fields are immutable). That's why State class is introduced. You just have to override the createState function to attach your defined State to your StatefulWidget, and let all that change happens in a separate class. I hope it help! |
@champ96k The problem is not why is there a separate object. The problem is, the term "State" implies, to most people, that it's a plain old object that holds only logic and has nothing to do with UI. It's like a fancy new supermarket called their meat aisle "Produce area". Every new customer would expect to find fruit and vegetables there, only to have an FAQ next to it explaining why it's not the case... A term like WidgetBuilder would be infinitely more understandable. Then you only need to explain to people that "WidgetBuilder" holds the state of the Widget, and rebuilds the widget when this changes. |
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of |
This comes up often when users are learning our framework. At our recent event, multiple Flutter team members didn't have a canned response to this FAQ.
Suggestion: Add this to our FAQ so for the next event, or next time this pops up in a chat or a conversation with a new user, we can point to our answer.
Can someone write up an answer to "Why is the build() method on State, and not StatefulWidget?" and we'll add it to the FAQ.
Thanks!
The text was updated successfully, but these errors were encountered: