Methods vs separate classes for widgets in Flutter? - flutter

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).

Related

Correct way to define state in StatefulWidget

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).

Build Context and state object in flutter

The newly created State object is associated with a BuildContext. This
association is permanent: the State object will never change its
BuildContext. However, the BuildContext itself can be moved around the
tree along with its subtree.
what does this statement trying to say? i find it quite subtle. it is from the official documentation of flutter
There are a lot of core concepts here, first of all you need to understand how flutter render the widgets, ill try to make a summary.
At run time, flutter internally manage three trees in order to achieve the high performance: Widget tree, Element tree and RenderObject tree.
I'm not going to get deep into this since is complicated but basically each tree has different responsibilities:
Widget: describe the configuration for an Element. It handle
Configuration.
Element: an instantiation of a Widget at a particular location in the
tree. It manage Life cycle.
RenderObject: handles size, layout, etc. It handle render and
painting aspects.
So, for every widget, Flutter builds a corresponding Element and build the Element Tree.
For Stateless widgets, the relation between widget and the corresponding element is trivial, but for Stateful widgets the underlying Element structure looks a little different. Those elements add a state object, which holds the mutable part of the configuration, a color for example.
The other thing that you should know is that BuildContext is actually a Element.
With that in mind, the meaning of this:
The newly created State object is associated with a BuildContext. This
association is permanent: the State object will never change its
BuildContext. However, the BuildContext itself can be moved around the
tree along with its subtree.
Is trying to say that when you build a Stateful widget, flutter is going to build a BuildContext (an element that hold the widget position, among other properties) and that contexts will hold the mutable State object.
Then the buildContext (element) itself can change (moved on the tree for example), but the thing that never is going to happen is that changing the state object will change the BuildContext. And that's why you can change, for example, the widget color or any mutable property on State object, and it will never change the element position in the tree.
Is a really interesting topic but is not simple. I highly recommend you to check this video and this article that have a deep explanation into the this.
Hope it helps!

StatefulBuilder VS StatefulWidget in Flutter - performance

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.

setState() vs notifyListeners(), which one is more efficient?

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.

What does BuildContext do in Flutter?

What does BuildContext do, and what information do we get out of it?
https://docs.flutter.dev/flutter/widgets/BuildContext-class.html is just not clear.
https://flutter.dev/widgets-intro/#basic-widgets on the 9th instance of the term BuildContext there is an example, but it's not clear how it is being used. It's part of a much larger set of code that loses me, and so I am having a hard time understanding just what BuildContext is.
Can someone explain this in simple/very basic terms?
BuildContext is, like it's name is implying, the context in which a specific widget is built.
If you've ever done some React before, that context is kind of similar to React's context (but much smoother to use) ; with a few bonuses.
Generally speaking, there are 2 use cases for context :
Interact with your parents (get/post data mostly)
Once rendered on screen, get your screen size and position
The second point is kinda rare. On the other hand, the first point is used nearly everywhere.
For example, when you want to push a new route, you'll do Navigator.of(context).pushNamed('myRoute').
Notice the context here. It'll be used to get the closest instance of NavigatorState widget above in the tree. Then call the method pushNamed on that instance.
Cool, but when do I want to use it ?
BuildContext is really useful when you want to pass data downward without having to manually assign it to every widgets' configurations for example ; you'll want to access them everywhere. But you don't want to pass it on every single constructor.
You could potentially make a global or a singleton ; but then when confs change your widgets won't automatically rebuild.
In this case, you use InheritedWidget. With it you could potentially write the following :
class Configuration extends InheritedWidget {
final String myConf;
const Configuration({this.myConf, Widget child}): super(child: child);
#override
bool updateShouldNotify(Configuration oldWidget) {
return myConf != oldWidget.myConf;
}
}
And then, use it this way :
void main() {
runApp(
new Configuration(
myConf: "Hello world",
child: new MaterialApp(
// usual stuff here
),
),
);
}
Thanks to that, now everywhere inside your app, you can access these configs using the BuildContext. By doing
final configuration = context.inheritFromWidgetOfExactType(Configuration);
And even cooler is that all widgets who call inheritFromWidgetOfExactType(Configuration) will automatically rebuild when the configurations change.
Awesome right ?
what is the BuildContext object/context?
Before we knowing about BuildCotext, We have to know about the Element object.
What is Element object
(note: As a flutter developer we never worked with Element object, but we worked with an object(known as BuildContext object) that's similar to Element object)
The Element object is the build location of the current widget.
What's really mean by "build location" ?
when the framework builds a widget object by calling its constructor will correspondingly need to create an element object for that widget object.
And this element object represents the build location of that widget.
This element object has many useful instance methods.
Who uses the Element object and its methods ?
They are 02 parties that use the Element object and its methods.
Framework (To create RenderObject tree etc)
Developers (Like us)
What is BuildContext object ?
BuildContext objects are actually Element objects. The BuildContext interface is used to discourage direct manipulation of Element objects.
So BuildContext object = discouraged element object (That contains less number of instance methods compared to the original Element object)
Why framework discouraged the Element object and pass it to us ?
Because Element object has instance methods that must only be needed by the framework itself.
but what happens when we access these methods by us, It's something that should not be done.
So that the reason why framework discouraged the Element object and pass it to us
Ok Now let's talk about the topic
What does BuildContext object do in Flutter ?
BuildContext object has several useful methods to easily perform certain tasks that need to be done in the widget tree.
findAncestorWidgetOfExactType().
Returns the nearest ancestor widget of the given type T.
findAncestorStateOfType().
Returns the State object of the nearest ancestor StatefulWidget.
dependOnInheritedWidgetOfExactType().
Obtains the nearest widget of the given type T, which must be the type of a concrete InheritedWidget subclass, and registers this build context with that widget such that when that widget changes.
[Used by Provider package]
The above methods are mostly used instance methods of BuildContext object if you want to see all the methods of that BuildContext object visit this LINK + see #remi Rousselot's answer.