I am new to Flutter and reactive programming is also new thing for me.
Let's say I want to build a timer with Flutter.
I add a Scaffold with all the necessary stuff in it and I add a IconButton which starts the Stopwatch and Text which displays elapsed time. I also add Timer.periodic to periodically (every 0.5 second) update the text.
Text Widget controls it's own state by checking if Stopwatch is running and updating it's values.
So now let's say I want to have more complicated logic that changes the text based on some actions with other buttons which are the siblings of Text. However it is not possible to call setState of Text widget directly from sibling widgets. As I understand the point of reactive paradigm is that the state can be passed down the Tree. However if I make my Scaffold as StatefulWidget and update the state of the parent every 0.5 second it will redraw my entire Scaffold with all it's children. So eventually when the Scaffold gets big enough it will have to update everything instead of single Text widget.
Am I correct? And is there any solution to this. I read something about Streams and Sinks however it looks very complicated and I think that there should be another solution.
You don't need to rebuild the whole tree, if the state only changed in a sub widget, ideally you want to call set state in that widget so only that part of the tree (the one whose state changed) is rebuilt.
Streams aren't really that complicated, it's a good way for you to send messages between different components in your app, which is what you're trying to do here.
In your case you can also use a ValueNotifier to store state in the parent widget, or maybe an AnimationController, and send its listener down to the sub widget that needs be updated on change.
In any case, the state is lifted to a parent widget, which then becomes accessible to the sub widget through a listener, or a stream. When the listener triggers a signal, you rebuild the sub widget only.
Extract out widget and call setState() form that widget and it's don't render all the widget again
Related
I'm utterly confused regarding the question of state management in Flutter.
So far I have read that the states of widgets have to be nested as deep in the tree as possible (or rather as close to the affected widget as possible) for better performance.
But what if such a nested state widget (InheritedWidget for example) has another state widget somewhere above it? Does the state widget at the lower level not loose it's state when the state widget higher in the tree requests rebuild for all of its descendants?
With this line of thought have I came to the conclusion, that state widgets just have to be at the root in the end, but I guess I'm wrong somehow.
The first part of your question is correct -
If a widget's state changes, this might require all its children to redraw.
But this is precisely why it is important to nest state as deep down in the widget tree as possible!
Assume the contrary, that all state information is stored at the root of the widget tree, at the very top.
Now if any information changes, no matter how small, it will lead to a complete traversal of the widget tree, rebuilding everything in the worst case.
And aside from the tree traversal, your application will also become very memory intensive. If all state is stored at the root, flutter can never tell when it is okay to release some information from memory. If the user leaves some views and the views are dismissed from memory, the information for them will still be stored at the top. And the only way to check wether that information is still needed would be to once again check the whole tree - very expensive!
All of this can be mitigated by putting your state as close as possible to the widget that will consume it. Because then
If the state changes, only a small subtree of the whole widget tree has to be traversed - This is fast.
If a widget is dismissed, flutter can also release all of the state information that has been stored for it. This frees memory.
yes ! every state widget has its own state and they are all independent. if the state of widget X is updated, only widget X will be updated
let suppose that you have an application that sows a family tree. in widget A you get the gradfather from an API, when you click on it you will be redirected to widget B where you can find his childrens, when you click on one of his childrens you go to widget C which shows the childrens of the selected father in widget B, now let's supposse that you want to add one children to this father.
you call the add-children endpoint. the problem here is that widget A will not be updated.
one solution to this, and to understand the state tree logic, is to pass a functionthat updates widget A from widget A to widget B and pass it from widget B to widget C and call it when an update happens either on widget B or C or even on A so widget A gets updated and you got the updated family tree in widget A
So basically flutter have it's own state management that is called setState(() {}) itu will update the state of the screen where setState is called if i have a button class widget in it's own file if i press the button i want to change the button name to something else so the setState will update the state or variables in the button class/widget.
Now how if the button wants to update a state/variables in the different class but in same screen? Since setState only update it's own class, so you to give the button onTap property with function constructer like this
final Function onButtonTap;
then put it on onTap like
onTap:() {
widget.onButtonTap();
}
Then in the screen where you want to update the state just call onButtonTap then use setState there
Flutter widget update pattern is a little bit confusing when coming from "old/classic" win32.
For instance :
I have a widget "button" => I click on it => I update a cell in a widget "datatable".
With classic API (VCL, Net Forms, ...) I get the address of button (by name, id, ...) and call directly
datatable_address.cell[x,y] = new_value;
I understand that I have to use setState() but do I need to create an "event" in Datatable (to run its setState()) and fire up this event from my button ?
(BloC seems pretty close to Qt signals)
The difference is composition vs aggregation. The widgets themselves are immutable so you can't change the value inside a widget.
What you can do is create a new object with latest values. Flutter framework will take care of the rest. For example,
when you do setState in parent widget of both button as well as datatable, you build method of said widget is called. Now, you will create a new datatable object with updated cell values. Internally, flutter will handle this.
You can only do setState in State class which is not immutable which is linked to Stateful widget which themselves are immutable.
This architecture comes with its own set of problems like if you need to update some UI based on some button press, you will have to have the logic inside the common widget of both the affected widget and button. That's where Stream/Bloc comes into picture.
I have one ParentWidget having two child widget.
I want to pass some callback action from FirstChild() to SecondChild() Widget without rebuild ParentWidget()like setState((){}).
I want to do because ParentWidget() has many widget. And this callback action continuously happen.
And continuously setState((){}) is not viable option for ParentWidget()
For example
setState((){}) call from FirstChild() than I want to rebuild SecondChild() without rebuild ParentWidget()
StatefulWidget is only useful in very simple business logic cases. I highly recommend that you have a look at Riverpod, where you can access a "state" from any widget, regardless of who its parent widget is. And because of how Riverpod is designed, it will not trigger any unnecessary rebuilds.
Sticking with StatefulWidget, you will need to use something like InheritedWidget as a parent of both widgets, and then have them both access it.
You can get help with state management solutions like Provider or Riverpod.
I may write an example code if you use Riverpod, but generally speaking, in such case, you need to place the state outside the Parent widget into some external variable. Then you can access and share it between both Children without rebuilding Parent.
I'm fairly new to Riverpod but it seems that using a ConsumerWidget as the body of a screen is a bad practice because the screen is rebuilt when not needed.
For example:
the main widget (the screen itself) is a ConsumerWidget
somewhere in the hierarchy I have a list of clickable buttons, for which I'm watching a ChangeNotifierProvider to update a selected index (only one button can be clicked at a time).
It seems that whenever I click one button to update the index (and change the color of the button), the main widget's Build method is called, along with the items in my list.
However, when using just a Consumer widget inside the itemBuilder method of my ListView, clicking one button no longer triggers the build method of the main widget.
So, is it considered a good practice to just use Consumer widgets where needed?
I'm discovering Flutter and I really love it.
I'm building a simple app where I have few instances of Statefull custom widget "Counter" on a Scafold's body, and I have a "Reset" button on the drawer.
What's the best way to call a "resetCounter" method on all "Counter" instances when tap on the "Reset" button ?
I've managed the other way (calling a callback method of a parent from a child), but I can't find the other way. The only solution I've found is by using GlobalKeys, but it doesn't seems appropriate.
Thanks
Indeed for your case callback function might be the right one. But when your app will grow the widget hierarchy will become more and more complex. For now you have Parent —> Child relationship and it’s quite easy to pass callback function, but when you’ll get Parent —> Widget A—> Widget B—> Child situation in order to pass callback from Parent to Child you’ll need to pass it through Widget A and Widget B as well.
For me, your problem sounds like a perfect candidate for reactive streams. You may create a Sink which will be used when user tap on Reset button and your Counter will observe this event and handle it with resetCounter method. You may learn more about reactive programming (and other ways to solve your problem) from this presentation.
There is another approach to extend ChangeNotifier, and add child as listeners. Once parent perform an action, you can call notifyChange to trigger child widgets callbacks.