How can I find out when a model changes with Provider? - flutter

Considering below class. Is it possible to listen for whenever appStateModel.bar changes? I have a scenario where I want to perform an animation every time a certain value changes, but I can't seem to figure out how to achieve that.
class Foo extends StatelessWidget {
const Foo();
#override
Widget build(BuildContext context) {
var appStateModel = Provider.of<AppStateModel>(context);
return Container(
child: Text('${appStateModel.bar}'),
);
}
}

In your change notifier, you should include notifyListeners() as stated.
With that, it should work.
On the other hand, you can use the build method to act on change events because the widget will rebuild every time notifyListeners() is called (At least at my point of understanding).
If that won't work, there is the Consumer Widget, it consumes a notifier and rebuilds only if notifyListeners() is called.
This widget also provides a child parameter with which you can save performance when using it because it does not need to rebuild when the notifier changes.
I hope this resolves your issue.

Related

Please clarify the documentation of `StatefulWidget.build` method

In the "Performance considerations" section of StatefulWidget documentation, third point mentions the following:
If a subtree does not change, cache the widget that represents that subtree and re-use it each time it can be used. To do this, assign a widget to a final state variable and re-use it in the build method. It is massively more efficient for a widget to be re-used than for a new (but identically-configured) widget to be created. Another caching strategy consists in extracting the mutable part of the widget into a StatefulWidget which accepts a child parameter.
Its not clear that what is "state variable" here, and what to to assign, because "build" term is mentioned in that line, and there is also a build method for every widget,
I mean,
Should the user do final Widget widget = MyWidget(); or final Widget widget = MyWidget().build(context);
As they both return Widget,
Also please explain, that is there any difference between the above statements, and what is the use of Builder widget, if we have build method, or do they have different work, if so, then what ???
thanking you
Never, ever ever ever run the build method on your own, the build method is there for flutter to call for itself with an updated context, you should not be using it. Always do MyWidget()
The Builder widget is useful when you need a newer context for something inside your widget, for example:
Widget build(BuildContext context) { // context crated here
return MaterialApp( // navigator created here, after the context was defined
home: ElevatedButton(
onPressed: () => Navigator.of(context).pop()
),
);
}
This code won't work because the context was created before the navigator (which was created along with a material app), you could wrap the button inside a builder widget to get a newer context that already has a navigator
Of course, calling the build method on your own is not good because you won't be passing the correct context to the widget, instead, let flutter handle the context creating and just use normal constructors.

Flutter Widget rebuild after one of the property changed

How to manage to force a Widget to rebuild immediately after one of the property value has changed?
Some pseudo code:
class Live extends StatefulWidget {
String name;
Live(this.name);
#override
_LiveState createState() => _LiveState();
}
class _LiveState extends State<Live> {
// some turbo logic I don't want to move to Live class
..
#override
Widget build(BuildContext context) {
return Column(childrens: [
Text(widget.name),
Card(content calculated based on turbo logic),
]
);
}
}
When String name property has updated (based on parent's setState call), everything is happening in real time. The change is reflected immediately in Text widget. The value is visible immediately only because i am using widget.name call so in built() method I am using property from Live class instead of State.
The problem is that another widget wrapped in Card is calculated in place marked as // some turbo logic I don't want to move to Live class. Due to this fact when I want to see updates in this section I need to switch tab and go to e.g Setting and then return to Live tab to see changes related to Card content. I believe it trigger build() method again.
Golas:
Once the name value is updated in Live widget, a State widget rebuilds immediately.
do not move turbo logic to Live class and keep it in State class
First off, your StatefulWidget should be immutable and therefore only contain immutable fields. I'd suggest you move the name field into State and change it there using a setter. The setter should call setState(), this will cause the desired rebuild.
See this introduction for more information - specifically the "Bird" sample code to see how to code a setter.

How to initiate ViewModel for widget(view)

So i am writing a Flutter application that utilize the MVVM architecture. I have a viewModel for every screen(widget) with ValueNotifiers and i want to initiate the viewModel for that view.
Now most guides suggest a Provider approach, but why provide it when i can just normally initiate it.
Code:
class FooModel{
final ValueNotifier<bool> _active = ValueNotifier<bool>(false);
ValueNotifier<bool> get active => _active;
FooModel(){_active = false;}
doSomething(){_active=!_active}
}
What i want to do:
#override
Widget build(BuildContext context) {
_viewModel = FooModel();
return Scaffold(
body:ValueListenableBuilder<bool>(
valueListenable: _viewModel.active,
builder : (context,value,_){
if(value)return(Text("active");
return Text("unactive");
}
)
}
what is suggested:
Widget build(BuildContext context) {
return Provider<FooModel>(
create: (_) => FooModel(),
builder: (context, child) {
final vm = Provider.of<FooModel>(context);
return ValueListenableBuilder<bool>(
valueListenable: vm.active,
builder: (context, value) {
if (value) return Text("active");
return Text("unactive");
});
},
);
}
Now i understand that what i suggested creates the viewModel with every build, but that should only happen when screen is loaded thanks to ValueNotifier so its fine.
I guess i just don't understand the value of providing the viewModel.
Flutter has a different ideology.
yes, you can create Value Notifier and it's fine to do that but just thinking of the bigger picture.
Check this flow you want to call an API then perform parsing and filtering on that and you have 2 views on the screen to show the same data one is to showcase the data and the other one is to interact with data and this update needs to be reflected on showcased data.
to do this what we need to do?
create valuenotifier at class level that encloses both screen widgets.
Call API and filter code at the class level.
pass this valuenotifier to both screen widgets you may ask why right? well because one class need to update other class widgets. and that's only one way to push updates to the valuenotifier is the object itself. so you will need to pass this valuenotifier in both classes.
once you do that and update has been synchronized if any setState has been called to the main widget that encloses both of this widgets then you need to do all this again.
also there will be multiple instances of valuenotifier which is bad as valuenotifier is a stream and you need to close your streams once you're done with the stream so you will be needing logic to close your streams at any setState event on main widget.
What is provider exactly? and how it works? well, the provider is a change notifier class which calls setState when you call notifyDataChanged or notify method. this triggers the widget state change which is listening to that data changes.
and that widget gets rebuild. This is the same logic for each and every state management library out there Bloc, ScopedBloc, or any widget like streamBuilder or ValueListenableBuilder.
In Flutter if you want to change data you just need to call setState. Just to be testable, more readable and maintainable what we will be doing is to separate logic into different files just like we do in Android or iOS and that's where this type of libraries comes into the picture to reduce our headache of typing code all over again and focusing on the main task which is the functionality of the app.
See we can create loops in different formats
for(int i=0;i<length;i++)
while(i++<length)
for(i in 0...length)
It's up to us to provide clean and understandable code so that any other developer does not need to delete all code just because he isn't able to understand our code.
There's nothing right and wrong here in development. It's matter of what is more convenient or what makes more sense. like int i does not make sense but if we replace it with index or listIndex it will.
Also, one thing to mention is what you're doing is creating a model that is kind of the same as bloc pattern. so you're already halfway through. you just need to call state change from model check bloc and other patterns you will understand.

Flutter performance of StatefulWidget and StatelessWidget

I use a lot StatelessWidgets when I have to create "templates" of widgets that are used multiple times inside my app because the docs say so:
Stateless widget are useful when the part of the user interface you
are describing does not depend on anything other than the
configuration information in the object itself and the BuildContext in
which the widget is inflated.
Here is an example:
class StepInputButton extends StatelessWidget {
final int pos;
final String value;
const StepInputButton({
this.pos,
this.value
});
#override
Widget build(BuildContext context) {
return Row(
// Text, Icon and a tiny button
);
}
}
The above is good because I can use const StepInputButton(val, "val"), in the code with CONST which improves performances.
PROBLEM
I am using the famous Provider widget to manage the state and the page of my apps usually look like this:
class SuccessPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
var prov = Provider.of<Type>(context);
return Scaffold(...);
}
}
That's a page of my app with Scaffold that has a Drawer, a float action button and an appTitle.
Here I use a StatelessWidget because I do not use setState() since provider does all the work for me. But still in the official flutter doc they say:
For compositions that can change dynamically, e.g. due to having an
internal clock-driven state, or depending on some system state,
consider using StatefulWidget.
So do I have to change class SuccessPage extends StatelessWidget to class SuccessPage extends StatefulWidget? Do I get advantages?
Note: if you want to put the question in another way: should I use StatefulWidgets to create "app pages" whose state is going to change and StatelessWidgets for "reusable widgets" whose state doesn't change?
StatefulWidget is necessary for when the widget itself is maintaining its own state. In the example you gave, the Provider package is handling the state for you, assuming you're using the correct provider type higher up the widget tree (for example, ChangeNotifierProvider). There also doesn't seem to be anything in this code that would benefit from having access to the widget's lifecycle, so you wouldn't need access to methods like initState or dispose.
As such, there's nothing for the widget itself to manage, so converting your class to be stateful is unnecessary.
One thing I might suggest, though, is to use a Consumer instead of calling Provider.of directly. A Consumer handles the call for you and removes any ambiguity on whether your widget will get updated when the Provider detects a state change.
You use StatelessWidget for widgets that don't change their state, that will stay the same all the time. Example, appBar is stateless.. The build(...) function of the StatelessWidget is called only once and no amount of changes in any Variable(s), Value(s) or Event(s) can call it again.
Therefore, when you need to change state(ex value) then use StatefulWidgets, basically StatelessWidget is used for building UI widgets that are static
Keeping it simple:
If you have non-final global variables in your widget then you need a StatefulWidget
If all global variables are final then you should use StatelessWidget;
Reason:
If your global variable is non final that means it is allowed to change and if it's value is changed that means state of your object(Widget) is changed (basic oops concept I am talking about). In such case you would like to call build method of your widget so that your changes get applied on the UI (if it matters for your UI). We do it by calling setState(); and so we use StatefulWidget for such use-case.
If it is enough that once you initialize your global variable in constructor, you don't need to assign it any value in future then in such case use StatelessWidget.
I have tried to keep it very simple and not technical enough so, if you still have any doubts please comment on this answer.

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.