Is state the same thing as class variables and their values? - flutter

I'm struggling to understand the concept of state in Flutter. I'm coming from languages like Delphi, Python, and C#, and thinking:
everything is a widget in Flutter is the same as everything is an object (class instance) in other languages
a widget is an object inheriting from a class with a build method, analogous to classes with OnPaint or OnShow methods in other languages
object properties (member variables) make up a widget/object's state. If there are no member variables or they are all declared as const or final, then the widget is stateless. If not then the properties and their values make up the widget/object's state.
What am I missing? What makes up state beside variables?
PS I get further confused when I read about Flutter elements and element trees, though I may understand better once I've got a handle on widget state.

object properties (member variables) make up a widget/object's state. If there are no member variables or theys are all declared as const or final, then the widget is stateless. If not then the properties and their values make up the widget/object's state.
This point is incorrect. Widgets are always immutable, but a StatefulWidget contains a reference to a State instance. States are where your mutable data is held. They have a much longer life than a typical widget, and include special state management methods such as initState(), setState(). They also have a build() method like widgets and for the most part seem like a widget, but they are actually a State<Widget>.
Should you use a StatelessWidget or a StatefulWidget? This question comes up a lot when starting Flutter development. My rule of thumb is that unless you know you really need a StatefulWidget, start with a stateless one. The reason being they are simpler and the most popular IDEs can convert a StatelessWidget to a StatefulWidget (and produce the corresponding State-extending class).

Related

Is ScaffoldWidget InheritedWidget?

I'm confused because the blog I saw introduced ScaffoldWidget as InheritedWidget. In the ScaffoldWidget body, InheritedWidget is not used, but it seems to be treated as InheritedWidget for using the of method.
Is ScaffoldWidget InheritedWidget?
Is the of method like a naming convention?

Flutter What is difference between valueNotifier,changeNotifier,stateNotifier?

What is difference between valueNotifier,changeNotifier,stateNotifier?
ValueNotifier is a special type of class that extends Changenotifier, which can hold a single value and notifies the widgets which are listening to it whenever its holding value gets change.
ChangeNotifier is a class that provides change notification to its listeners. That means you can subscribe to a class that is extended or mixed in with ChangeNotifier and call its notifyListeners() method when there’s a change in that class. This call will notify the widgets that are subscribed to this class to rebuild.
On the other hand, StateNotifier is an immutable state management solution where the state can be directly changed within the notifier only.
There is an interesting difference between ValueNotifier and StateNotifier. The former uses == to assess whether update is needed, while later uses identical check. This has positive performance implications in favor of the later as long as immutable types are used. See https://github.com/rrousselGit/state_notifier#why-are-listeners-called-when-the-new-state-is--to-the-previous-state
For built-in "value types" and enums they work just the same.
One may be tempted to use ValueNotifier for mutable objects, but this doesn't work well because sub-objects of that object can be changed via mutating methods, and this clearly does not trigger updates.
StateNotifier also has some additional options, such as modifying when the value is actually updated.
Therefore my current recommendation is:
Use ChangeNotifier for mutable types.
Use StateNotifier for immutable types.
Ignore ValueNotifier.
StateNotifier is intended to be used with immutable objects, but there is no mechanism in the language to ensure this is the case. This compiles:
class Mutable {
Object? o;
}
class Mistake extends StateNotifier<Mutable> {
Mistake() : super(Mutable());
}

Flutter: Late Initializing a "widget-dot" Class-level Variable in a Stateful Widget -- What's Going on?

Consider the following class level property inside the state class of a stateful widget:
int myInt = widget.int;
Android Studio informs that: "The instance member 'widget' can't be accessed in an initializer."
(I understand what this error means).
So then if we add the late keyword, it appears to be fine:
late int myInt = widget.int;
However... this is surprising to me that I’m allowed to do all that in one line — I thought that late variables had to be not set/set as null:
late int myInt;
... and then assign inside onInit.
Since I didnt declare when to assign it, I dont know when the assignment takes place.
The question is:
Is the one-liner “late int myInt = widget.int;” exactly equivalent to assigning it myself in the initState method?
The late keyword in Dart has 2 distinct usages, melt into a single keyword.
The first usage, e.g. late int i:
This usage is well-known: delay assigning a value until later. This is most commonly used to make a field non-nullable, even though you might not have the value right away. I'm sure you are familiar with this usage.
The second usage, e.g. late int i = 0:
This is to delay the value calculation until the field is being accessed. This is useful when the value is expensive to calculate, so you might want to delay its calculation until it's needed for the first time. It's stated on the official documentation:
When you do this, the initializer becomes lazy. Instead of running it
as soon as the instance is constructed, it is deferred and run lazily
the first time the field is accessed. In other words, it works exactly
like an initializer on a top-level variable or static field. This can
be handy when the initialization expression is costly and may not be
needed.
So basically, depends on whether you assign a value right away (on the same line), Dart will decide which of the 2 usages you are using. If you write late int i it will be the first usage, if you write late int i = 0 or late int i = calculateValue() it will be the second usage: delay the calculation until when the field i is accessed for the first time. It's like lateinit in Kotlin or lazy in Swift.
Now back to your case. By assigning a value on the same line as the late keyword, you are using the second usage, basically "lazy init" until the field is accessed for the first time. By the time it's accessed, this class would've been instantiated, so (by that time) you are allowed to use the this keyword.
In the first case Android studio throws that error because int myInt requires a value the moment you are declaring it.
In that particular moment, in the Statefull widget state, the widget object is not accessible.
In the second case:
late int myInt = widget.int;
That is a valid one line declaration and assignment of the variable, but the effect is a bit different from the onInit alternative.
The late keyword works in a lazy way.
Instead of running as soon as the instance is built, it run the first time the field is used. In that moment the widget object will be accessible.
Take a look at the answer to this question, it can be helpful: here
Assigning the value inside the onInit guarantees that the value is actually assigned only once when the widget is initialized.
widget.xxx corresponds to the value of xxx of the instance of a widget, ie the widget once it exists.
So when you use widget.xxx in initialisation of the widget, the var xxx does not exists.
That's why the dart compiler tell you The instance member 'widget' can't be accessed in an initializer .
By adding the keyword late in front of the declaration, you tell the compiler that this variable will be defined later.
But be careful, it will then really have to be defined later (in initState for example) and in any case before any use.
This error comes from the fact that dart is now a null safety aware language.
That is to say a language that strives to ensure that no variabales can have a null value. This is for reasons of code quality and greater code security.

Why can't non-nullable fields be initialized in a constructor body in Dart?

Often times I have an instance field that needs to be initialized in a constructor. For example, it might be needed to be calculated based on other instance fields, hence I can't initialize it inline (where declared) or with a constructor initializer list.
But it either has to be nullable, or declared late, if I need to initialize it in constructor. The rationale behind late keyword is that programmer states "I'll initialize this before using, trust me", when it can not be determined by the compiler that initialization will take place before first usage. BUT: this "programmer guarantee" seems A) terrible and B) unnecessary in case of constructors, because it can be determined by compiler whether the field was initialized in a constructor (and constructor itself is obviously guaranteed to execute before any other instance methods).
Obvious downside to using late fields in such scenarios is that nothing enforces them compile-time to be actually initialized during construction (or anywhere, for that matter). Plus, every time the late field is read, a runtime check is inserted to make sure it has been assigned a value - I don't need that when I initialize in constructors.
Therefore, it seems that, technically it should be possible to have non-nullable non-late fields that are initialized within a constructor body (and if they are not - compiler can throw an error).
So what is the rationale of requiring constructor-initialized fields to be either nullable, or declared as late? Is there a technical reason why this limitation is imposed, or is it just a design oversight by the Dart team?
Dart executes constructor bodies inside-out, from base class to derived class. This allows virtual dispatch to occur in the constructor body. The fact that virtual dispatch can occur in the constructor body means that the compiler cannot statically determine what code will be executed by the constructor body, and therefore it cannot deduce what the constructor body might ultimately initialize.
That the constructor body can execute arbitrary code (including callbacks) that might initialize members or that might depend on initialized members makes it even more complicated.
Furthermore, allowing members to be not initialized when the constructor body runs would be error-prone and a source for confusion. For example, with:
class SomeClass {
int member;
SomeClass() {
updateMember(0);
}
void updateMember(int value) {
print(value); // Oops.
member = value;
}
}
With Dart's current design, all instance methods (and their overrides) can be guaranteed that members are initialized when the method is called. If members were allowed to be uninitialized when the constructor body is executed, that would not longer be true, and all instance methods then would need to consider if they might be invoked from the constructor (or from a base class constructor), possibly indirectly from other method calls, and whether accessed members might not be initialized yet.
(I'll grant that that previous point isn't terribly strong since it currently can still happen that a member is initialized to an object that the constructor body must mutate, but typically instance methods receiving an empty List, Map, etc. is less of a problem than receiving uninitialized members. The above situation also could happen with late members, but that's the baggage that comes with choosing to use late.)
Null-safety disallows the possibility of accessing uninitialized non-late variables, but your proposal would make that possible.
Also, because there is a distinction between initializing members via an initializer list and via a constructor body, people are encouraged to use initializer lists when possible.
The point that you are ignoring here is that when you want to pass a variable to a constructor you will definitely initialize it, otherwise you wouldn't be able to use that widget because you have to pass the variables needed to its constructor. so this late or nullable keywords can be used for the values that you are trying to pass to a widget and not in the widget itself that you are passing them to, but before it.

The member 'notifyListeners' can only be used within 'package:flutter/src/foundation/change_notifier.dart' or a test

I am using ChangeNotifier and i change their value and notify like below,
// Declare
ValueNotifier<bool> isDisplay = new ValueNotifier(false);
// Change value
isDisplay.value = false;
isDisplay.notifyListeners();
It's working fine. but it's raise warning like below,
Can we resolve this using proper implementation or other things ?
I was getting these errors too, more specifically, those 2:
The member 'notifyListeners' can only be used within instance members of subclasses of 'package:flutter/src/foundation/change_notifier.dart'.dart(invalid_use_of_protected_member)
The member 'notifyListeners' can only be used within 'package:flutter/src/foundation/change_notifier.dart' or a test.dart(invalid_use_of_visible_for_testing_member)
After a bit of research, I've come to some conclusions and a solution. Thought I should share it with people who encounter this problem.
The problem
What it says actually is quite obvious, “If you wanna call notifyListeners(), you have to do it in a subclass of ChangeNotifier or in a test file”
“But why is that?” Understandably, this was my second question. But before that, a little bit about where the heck that notifyListeners method is coming from
notifyListeners method
If you look at the implementation of ValueNotifier, it extends ChangeNotifier class:
and ChangeNotifier has a notifyListeners() method (which is why, in the first place, we can use this method) but not in a usual way:
It's protected.
#protected annotation in Dart | doc
[#protected annotation] Indicates that the annotated instance member (method, getter, setter, operator, or field) m in a class or mixin C should only be referenced in specific locations.
A reference from within the library in which C is declared is valid. Additionally, a reference from within an instance member in C, or a class that extends, implements, or mixes in C (either directly or indirectly), or a mixin that uses C as a superclass constraint is valid. Additionally, a reference from within an instance member in an extension that applies to C is valid.”
In a more worldly explanation: it can be used in subclasses that directly extends (or mixins) the ChangeNotifier class or in test files. Since you want to use it in your logic code, you have to do the first section in this paragraph: “extend or mixin ChangeNotifier where you want to call notifyListeners method.”
Where to extend (or mixin) the ChangeNotifier class?
You may be tempted to use it in your Stateful or Stateless Widget, and, since Dart is single-inheritance, you'd have to “mixin” the ChangeNotifier class to your class, but you'd be wrong.
1. Why you must not mixin to StatefulWidget
ChangeNotifier class defines its own dispose method. And since the class or mixin that mixins to your class supersedes the previous inherited methods, by looking at the methods of ChangeNotifier, you'll notice the dispose method (which is used to remove the listeners). So, if you mix ChangeNotifier to StatefulWidget, now your dispose would be ChangeNotifier's dispose rather than the State's dispose.
Remember, you have to release the resources used by State class no matter what. Normally, if you do not override the dispose method in your State, the framework does this for you and releases the resources by itself, which is cool; but since you just changed the dispose method's ancestor, framework will not trigger the required dispose method. And that will raise a runtime error.
2. Why you must not mixin to StatelessWidget
And on the StatelessWidget's case, now your widget would have an instance variable _listener that's not final which comes from the mixed ChangeNotifier. So, you couldn't define a const constructor, which is the most important part of a StatelessWidget (talking about widget rebuild and performance-related situations).
What to do?
In the end, a solution to this would be, to create a new class to manipulate value changes and extend or mixin ChangeNotifier to this class. Then you can call notifyListeners() method wherever and whenever you like! (but of course, don't call notifyListeners unless you use complex objects or send the same value)
Also, these classes are preferably called as 'Controller', 'ViewModel', 'BLoC', or any other name to state that they're mutating/controlling your values and trigger UI to rebuild when needed. (Which what you should do in the first place: separate your UI from your Logic layer).
Further readings
State management for minimalists
You do not need to call notifyListeners, as this is automatically called when a new value is set.
ChangeNotifier implementations like ValueListener should never require notifyListeners to be called manually.