Where should you place the state value in Riverpod? - flutter

To access the state of a StateProvider or StateNotifierProvider:
Sometimes in the Riverpod documentation, the state variable is added after the watch function.
int count = watch(counterProvider).state;
However, my code where I am using a StateNotifier, works only if I refer to it inside watch. i.e
watch(myNotifier.state)
What are the differences?

The widget that is consuming the provider will behave differently in the two cases.
In the first case:
watch(counterProvider).state
The consumer will look at the entire counterProvider and it will be rebuilt if anything causes a NotifyProvider.
In second case:
watch(counterProvider.state)
The consumer is looking at the state variable only and it will only be rebuilt if the state changes and cause a NotifyProvider.

Related

Flutter BLoC variables best practice

Started recently using the BLoC approach for building apps, and one thing that is not clear is where to "keep" BLoC variables. I guess we can have these two options:
Declare a variable in the BLoC class; for example in my class I can do the following:
class ModulesBloc extends Bloc<ModulesEvent, ModulesState> {
late String myString;
}
And access it in my UI as follows:
BlocProvider.of<ModulesBloc>(context).myString;
Keep it as a state variable; for example I can declare my state class as follows:
class ModulesState extends Equatable {
const ModulesState({required this.myString});
final String myString;
#override
List<Object> get props => [myString];
}
And access it in my UI as follows:
BlocBuilder<ModulesBloc, ModulesState>(
builder: (BuildContext context, ModulesState modulesState) {
modulesState.myString;
}
)
Are there any performance penalties / state stability issues with any of the above approaches?
Thanks!
I am not sure there is an absolute answer but I can at least give my opinion.
In bloc you have 3 objects: bloc, event, state.
The state is the mutable part while the bloc is a description of the your problem (what states to emit for each event). As such, an immutable variable to describe your problem should be, in my opinion, placed inside the bloc. However, anything which might change is the state of your bloc (same as the state of your widget) and should as such be stored in the state.
Example:
You want to create an app where you can set timers. In this app you can have multiple timers, each of which will be identified by a name.
In this case:
your state will be an object containing a double variable called timeCount, which will be incremented each seconds for example.
You bloc will have a final field called name which will have to be set during the creation of the stopwatch.
Interestingly enough, if you want the bloc to also handle the stopwatch creation, you will have 2 states: the first empty, the second with a name and timeCount. See how naturally name became variable and is therefore found in the state now.

How to rebuild only a spesific item in the Listview?

I have an entity object, for simplicity lets call it now Todo. I want to have a widget where I can edit multiple of these todos simultaneously, something like EditableTodoList (and this would show a list of EditableTodos).
This widget would recieve a List<Todo> and also something like Function onTodoEdited(Todo).
I am trying to use immutable data, so when an EditableTodo is being edited (and the event gets propagated to the widget which holds the EditableTodoList), I'd create a new List<Todo> which contais the updated Todo, but then EditableTodoList would rebuild with all its EditableTodos inside.
How could I optimize this, that only the relevant EditableTodo is rebuilt? I don't want to hack around with mutating the prop original List<Todo> because that hides greatly an important detail.
Background: currently the state is inside a cubit and I am context.select-ing for every EditableTodo list item the corresponding Todo in the state, this way only the edited item rebuilds. I'd like to decouple this EditableTodoList from the cubit.
Theoretically I assume its not possible, as EditableTodoList is given new properties, so it has to rebuild, and that means all subtree gets rebuilt. EditableTodo cannot be const as its prop is not a constant.
But still, what would be the most elegant way of separating EditableTodoList from the cubit? As it only needs a List<Todo> to show, it should be possible somehow to optimize the rendering of not changed Todos
You should consider how widget, element and render trees work in order to be concerned. There is a great video about Flutter's rendering mechanism. When rebuilding a tree, framework compares the two versions of widgets by using only the key and runtime type values. This means that if you change string value of the Text widget, the same render object with mutated data will be used again. New render object won't be created, which means no unnecessary rebuilds will happen on the render tree. Rebuilding the widget tree is not expensive compared to the render tree.
There is a limit on minimizing the the build scope in the ListView. ListView itself should be rebuilt in order to update an item. So you can't target a spesific item to rebuild. But items can reactively rebuild themselves (eg. an item that listens to a stream can rebuild without effecting the others). Therefore immutable collections doesn't matter in this context since the framework doesn't care the value but key and runtime type. You can either pass a completely modified list or a list with just one element changed, it will have the same effect.

Updating a Provider from a StreamProvider

I have a StreamProvider connected to Firebase, and another Provider/ChangeNotifier with functions to process the data from StreamProvider in different ways depending on app state. For this reason, whenever new data comes from the StreamProvider, I would like to immediately send it to be stored in Provider/ChangeNotifier, where it can be easily processed when the app state changes.
The problem: I don't know the best way to send the result of StreamProvider to the Provider/ChangeNotifier.
My temporary solution: Within the StreamProvider function, I can send the result to the other Provider by doing something like below, but note that I have to use 'listen: false'. Otherwise, I get an error like "Tried to listen to a value exposed with provider, from outside of the widget tree".
This seems like it is not the best solution because I have to set a variable to Provider.of(context) at all the places where I need to Consume the Provider, even though I don't actually need the result of the StreamProvider in these widgets. But without these variables defined, the widgets depending on the StreamProvider and Provider would not rebuild.
I'd like to know if there is a better way to accomplish this.
From the stream defined for the StreamProvider:
return ref.snapshots().map((items) {
// Sending the result to the other Provider
var appData = Provider.of<AppData>(context, listen: false);
appData.setVariable(items);
return items;
}

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!

When to use Provider.of<X> vs. Consumer<X> in Flutter

I'm still wrapping my head around state-management techniques in flutter and am a bit confused about when and why to use Provider.of<X> vs. Consumer<X>. I understand (I think) from the documentation that when choosing between these two you would use Provider.of when we want access to the data, but you don't need the UI to change. So the following (taken from the docs) gets access to the data and updates the UI on new events:
return HumongousWidget(
// ...
child: AnotherMonstrousWidget(// <- This widget will rebuild on new data events
// ...
child: Consumer<CartModel>(
builder: (context, cart, child) {
return Text('Total price: ${cart.totalPrice}');
},
),
),
);
Whereas, where we only need the data on don't want to rebuild with UI, we'd use Provider.of<X> with the listen parameter set to false, as below:
Provider.of<CartModel>(context, listen: false).add(item); \\Widget won't rebuild
However, listen isn't required and so the following will run too:
Provider.of<CartModel>(context).add(item); \\listener optional
So this brings me to a few questions:
Is this the correct way to distinguish Provider.of<X> and Consumer<X>. Former doesn't update UI, latter does?
If listen isn't set to false will the widget be rebuilt by default or not rebuilt? What if listen is set to true?
Why have Provider.of with the option to rebuild the UI at all when we have Consumer?
It doesn't matter. But to explain things rapidly:
Provider.of is the only way to obtain and listen to an object.
Consumer, Selector, and all the *ProxyProvider calls Provider.of to work.
Provider.of vs Consumer is a matter of personal preference. But there's a few arguments for both
Provider.of
can be called in all the widgets lifecycle, including click handlers and didChangeDependencies
doesn't increase the indentation
Consumer
allows more granular widgets rebuilds
solves most BuildContext misuse
Provider.of<>
applying provider, whole widget will rebuild if listen true.
Consumer<>
using consumer only specifically allowed widget will rebuild.
For your questions:
Is this the correct way to distinguish Provider.of<X> and Consumer<X>. Former doesn't update UI, latter does?
Provider.of<X> depends on value of listen to trigger a new State.build to widgets and State.didChangeDependencies for StatefulWidget.
Consumer<X> always update UI, as it uses Provider.of<T>(context), where listen is true. See full source here.
If listen isn't set to false will the widget be rebuilt by default or not rebuilt? What if listen is set to true?
Default value is true, means will trigger a new State.build to widgets and State.didChangeDependencies for StatefulWidget. See full source here.
static T of<T>(BuildContext context, {bool listen = true}).
Why have Provider.of with the option to rebuild the UI at all when we have Consumer?
Pretty much covered by Rémi Rousselet's answer.
There should not be any performance concern by using it, moreover, we should use consumers if we want to change some specific widget only on screen. This is the best approach I can say in terms of coding practice.
return Container(
// ...
child: Consumer<PersonModel>(
builder: (context, person, child) {
return Text('Name: ${person.name}');
},
),
);
Like in the above example, we are only required to update the value of the Single Text Widget so add consumers there instead of Provider which is accessible to other widgets as well.
Note: Consumer or Provider update the only reference of your instance which widgets are using, if some widgets are not using then it will not re-drawn.
The widget Consumer doesn't do any fancy work. It just calls Provider.of in a new widget, and delegate its build implementation to [builder].
It's just syntactic sugar for Provider.of but the funny thing is I think Provider.of is simpler to use.
Look at this article for more clearance
https://blog.codemagic.io/flutter-tutorial-provider/
We have 3 things to understand here.
When you wrap Provider around a widget it sets up a reference to a widget tree and a variable whose changes you want to refer to.
using Provider.of(context) you can get access to the variable you want to monitor and make changes in it.
Provider.of(context) with and without listen gives you a reference to the above-declared Provider object and a widget tree where it can be accessed from. But as said by others, it rebuild the whole widget tree it sits on top of when listen is not false.
In the end, you can use consumer to monitor any changes that happened using the above step
The consumer acts like a more granular listener and be applied to a fixed widget to help avoid unnecessary rebuilds.
it's just a personal preference and depends on how you understand and use provider.
so the way i think of provider is it's just an object that is providing a ChangeNotifier Object that has Code and Data down the widget Tree.
So,I use :
Consumer<T>
When i want to listen to changes in Data and update/rebuild my UI according to those changes.
Provider.of<T>
When i just want to call the Code. with the listen parameter set to false.
if your problem is to know what the difference is, here is a track
Consumer
reload consumer-only child widgets
Provider.of (with listen true)
reload from the last buildcontext found in the tree
actually in a simple example
exemple1 provider.of
in exemple1 when I click on my container, his increase his size ( gesturedetector send a newvalue at h variable , h variable is in function named method in provider)
with 'provider.of' flutter rebuild at the first Buildcontext below #override
that mean the totality of tree are rebuild
exemple2 consumer
in exemple2 when I click on my container, his increase his size ( gesturedetector send a newvalue at h variable , h variable is in function named method in provider)but with consumer only sélectionned widget are rebuild
that mean one widget are rebuild the other widget doesn't "move"
I hope I could help you