Is it ok to nest ConsumerWidgets in riverpods? - flutter

In Riverpods, is it ok to nest ConsumerWidgets? Something like this? I want to do so to keep my apis separated, but it feels like it might slow down performance?
class Foo1 extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
// watch something
return Container(
child: Foo2(),
);
}
}
class Foo2 extends ConsumerWidget {
#override
// watch something
Widget build(BuildContext context, ScopedReader watch) {
return Container();
}
}

This is completely and totally fine. It would not be strange if most or all widgets in your application were ConsumerWidgets.
As long as you only watch providers when/where needed, you have nothing to be concerned about.
There are many ways to optimize rebuilds. For example, creating providers that only expose one aspect of state your widget may depend on or using Consumer to only rebuild certain children when the value exposed by a provider changes. If you're using hooks, you can use select which is super handy for conditionally rebuilding.

Related

Flutter: Is it ok to use notifier constructor to pass initial value in riverpod?

I have a TaskEditor screen and a TaskEditorController which extends Notifier. State is a Task model. I want to make the default value of TaskEditorController as the Task I get as navigation params from another screen. To achieve that, this is what I am doing. Though this is working fine, I am little concerned over the practice.
class TaskEditorController extends Notifier<Task> {
TaskEditorController(this._initialTask);
final Task _initialTask;
#override
Task build() {
return _initialTask.copyWith();
}
}
final taskEditControllerProvider = NotifierProvider.autoDispose<TaskEditorController, Task>(
(() => throw UnimplementedError()));
And this how I am overriding it.
class TaskEditor extends HookConsumerWidget {
const TaskEditor(this._task);
final Task _task;
#override
Widget build(BuildContext context, WidgetRef ref) {
return ProviderScope(
overrides: [
taskEditControllerProvider
.overrideWith(() => TaskEditorController(_task))
],
This is "alright". There's nothing inherently wrong with doing this.
But this approach is a bit discouraged. Relying on overriding providers within the widget tree is considered as "advanced" and "should be avoided if possible".
Chances are there's a different solution to your problem which does not involve such practice.

Which is better when using provider instance in a new widget?

Let's say I've written my code as below.
I've got a provider called SampleProvider, and I'm using it in my main widget.
class SampleProvider extends ChangeNotifier {}
class MainWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
SampleProvider provider = Provider.of<SampleProvider>(context);
}
}
And then, I want to make a new widget and use this provider in the new widget.
There will be two choices.
First, I just instantiate another provider in the new widget as below.
class NewWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
SampleProvider provider = Provider.of<SampleProvider>(context);
}
}
Or, I can send it from the main widget to the new widget as a constructor parameter.
Like this:
class NewWidget extends StatelessWidget {
final SampleProvider provider;
NewWidget(this.provider);
#override
Widget build(BuildContext context) {
}
}
I guess the first option is better because flutter draws a widget based on its build context, but I'm not sure.
I've googled it quite long, but there was no success.
Can anybody tell me whether I am right or wrong? Or Do they have no difference?
Prefer the first solution, it's easier to refactor.
Suppose you need move NewWidget in your widget tree, you also need to modify the "paramter pass" code if you choose second solution, which is not necessary with first solution.
One of Provider pacakage's purpose is avoid passing parameter deep in the widget tree by the way.
Depend on preference not like first or second one.
Have an exception when obtaining Providers inside initState. What can I do?
This exception happens because you're trying to listen to a provider from a life-cycle that will never ever be called again.
It means that you either should use another life-cycle (build), or explicitly specify that you do not care about updates.
As such, instead of:
initState() {
super.initState();
print(context.watch<Foo>().value);
}
you can do:
Value value;
Widget build(BuildContext context) {
final value = context.watch<Foo>.value;
if (value != this.value) {
this.value = value;
print(value);
}
}
which will print value whenever it changes (and only when it changes).
Alternatively, you can do:
initState() {
super.initState();
print(context.read<Foo>().value);
}
SRC: https://github.com/rrousselGit/provider#i-have-an-exception-when-obtaining-providers-inside-initstate-what-can-i-do
Yes, I believe the first option is the better way, of the top of my head I can't think of any situation in which you would prefer the second option to the first.
If you don't use new widget as children of any other widget , first choice is better .
otherwise , second is better .

Flutter Widget building & structure

It seems there is a lots of way to build Widget in Flutter. It is a bit overwhelming to understand the right way of doing it. Can anyone explain me the difference between defining a variable inside/outside the build function as follow:
Inside class
class Login extends StatelessWidget {
TextFormField username() {
return TextFormField();
}
#override
Widget build(BuildContext context) {
return Scaffold();
}
}
Inside build function
class Login extends StatelessWidget {
#override
Widget build(BuildContext context) {
TextFormField username() {
return TextFormField();
}
return Scaffold();
}
And also is there is any difference between defining the variable as follow:
class Login extends StatelessWidget {
final username = TextFormField();
TextFormField username() {
return TextFormField();
}
#override
Widget build(BuildContext context) {
return Scaffold();
}
}
Thanks a lot for your help.
Jonathan.
There are several ways to do anything within the programming world. With Flutter / Dart it would be no different.
However, there are more "orthodox" ways of doing things, which will certainly save you from structural problems.
1- Declaration of an instance.
When you have a class that is not a widget, you usually use common instances of classes.
String name = "Jonny";
TextEditingController controller = TextEditingController();
When you declare widgets, there are 3 ways to do this, but only 1 is recommended.
The first is to store the widget in a variable (just like we did above).
final text = Text ('Pablo');
Build widget (BuildContext context) {
return Scaffold (body: text);
The second, and least recommended of all, is to create a method that gives you an instance of that widget
Widget text () {
return Text ('Pablo');
}
Build widget (BuildContext context) {
return Scaffold (body: text ());
This second approach is less recommended, because you will manufacture a new instance every time the method is called. If this widget has a heavy rendering, you are throwing resources in the trash.
The third (and highly recommended, I would tell you to stick to it and forget about it any other way) is nesting widgets in the tree:
Build widget (BuildContext context) {
return Scaffold (body: Text ('Pablo'));
This ensures that the same instance of Text () remains on the tree, and consequently if it were a much larger widget, this would be the most resource-efficient approach.
What if my tree becomes giant? how to avoid the ripple effect?
Simple, create new StatelessWidgets to break the tree's cascading effect.
Build widget (BuildContext context) {
return Scaffold (body: MyText());
class MyText extends StatelessWidget {
Build widget (BuildContext context) {
return Container (child:
Center (child: Text ('Pablo')),
);
}
}
Note: This was done for example purposes only, you must componentize reusable, or componentizable widgets, to avoid the effect:
aa
aaa
aaaa
aaaaa
aaaaaa
aaaaaaa
aaaaaaa
But if you have a few lines of code you don't have to worry about it as much.

Trying to access state from widget, probably using wrong design

I'm learning flutter and trying to make a kind of MutableImage widget. The idea is to make a MutableImage StatefulWidget that would rebuild when a new image is provided. I try to avoid rebuilding the whole widget tree each time the image is changed because that seems overkill, and I plan to update the image several times per second. So I want to rebuild only that MutableImage widget.
So here is the code I have, with comments to explain where I'm stuck :
class MutableImage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MutableImageState();
}
void updateImage(List<int> bytes) {
// !!!!!! Would like to call this method here, but state is not available from Widget, which means I want to do something wrong, but not sure exactly how I should do it...
// this.state.updateImage(bytes);
}
}
class MutableImageState extends State<MutableImage> {
List<int> _bytes;
void updateImage(List<int> bytes) {
setState(() {
_bytes=bytes;
});
}
#override
Widget build(BuildContext context) {
if ((_bytes==null)||(_bytes.length==0)) {
return Center(child: CircularProgressIndicator());
}
return Image.memory(_bytes);
}
}
Then the idea was to use this widget like this for example in another stateful widget
MutableImage _mutableImage;
#override
Widget build(BuildContext context) {
if (_mutableImage == null) _mutableImage=MutableImage();
return : Row( //using Row as an example, the idea is that the mutable Image is deep into a tree of widgets, and I want to rebuild only _mutableImage when image changes, not all widgets.
children : <Widget>[
child0, child1, _mutableImage, child3, child4
]
);
}
void updateImage(List<int> bytes) {
_mutableImage?.updateImage(bytes);
}
So is there a good way to do this ? I'm quite confused, thx for any help/hint.
This is a place for an application of a GlobalKey. In the parent Widget of MutableImage make a global key and pass that to MutableImage. With that key you can access MutableImage state by using .currentState on the key and calling updateImage.
You'll have to add key as an argument of the MutableImage constructor and call super(key: key). updateImage should also be moved the the state of MutableImage.
Key:
final GlobalKey<MutableImageState> _imageKey = GlobalKey<MutableImageState>();
Pass the key:
MutableImage(key: _imageKey);
Access the state:
_imageKey.currentState.updateImage();

Does whole subtree rebuilds on setState in flutter

I am new to flutter and really wondering if all the subtree of widgets gets rebuild when we call setState.
Subtree here means all the widget tree below that widget (including that widget as root node).
When we call setState function, the build method is called on the root node of the subtree, which triggers the build methods on its child. Say a branch (here MyWidget1) of a subtree (a child of that widget) is independent of the state variables. I noticed that even independent branches are rebuilt on setState called in the parent node.
class _MyAppState extends State<MyApp> {
int count=0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: <Widget>[ MyWidget1(),MyWidget2(count),],),
floatingActionButton: FloatingActionButton(onPressed: ()=>setState((){count++;}),),
);
}
}
class MyWidget1 extends StatelessWidget {
#override
Widget build(BuildContext context) { print("widget builds 1");
return Container(height: 100, color: Colors.orange,);
}
}
class MyWidget2 extends StatelessWidget {
final int count;
MyWidget2(this.count);
#override
Widget build(BuildContext context) { print("widget builds 2");
return Text(count.toString());
}
}
Here we can see that MyWidget1 is independent of the state variable (here count), so generally, setState should have no impact on it.
I was wondering if there should be any optimization to avoid that useless build of MyWidget1 on the call of setState function. As the tree below MyWidget1 can be too big, that too will be rebuild again.
My Questions:
Is it Ok for this Independent Widget (here MyWidget1) to build again on setState?
Is there a better way to deal with this situation to avoid its rebuild.
Note: I have read this question
In this question, there is a way to avoid useless build by creating an instance of the independent branch outside the build method,
My doubt is :
Is this the WAY to deal with this situation or some other better way or this situation isn't that big at all as tree builds in O(n) time (which I think shouldn't be the answer because building tree might be O(n) operation but it may include many time-consuming operations which may not be optimization friendly to call again and again uselessly).
Yes, MyWidget1 is rebuilt upon that setState. Just trust the code. After you call setState, build is called, which calls the constructor of MyWidget1. After each setState, the entire subtree is rebuilt. Old widgets are thrown away. States are not thrown away, though. State instances live on, they are not recreated (see didUpdateWidget).
So, yes. After each setState, the entire subtree is rebuilt.
This is OK, don't worry.
The widget classes here are very lightweight classes. Dart's garbage collector is optimized to instantiate many such objects and throw them away together.
This tree that you get to recreate again and again is just a facade. There are two more parallel trees that are not lightweight and are not recreated. Your widget trees are diff'ed together to find how the actual ui elements should be modified by the system.
Why all this trouble, you may ask. Because creating trees is easy and maintaining them is difficult. This reactive declarative framework lets us get away with only creating the tree and not maintaining it.
There are some resources about Flutter internals that you can read more about this. One such resource is this video: https://www.youtube.com/watch?v=996ZgFRENMs
class _MyAppState extends State<MyApp> {
int count=0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: <Widget>[ const MyWidget1(),MyWidget2(count),],),
floatingActionButton: FloatingActionButton(onPressed: ()=>setState((){count++;}),),
);
}
}
class MyWidget1 extends StatelessWidget {
const MyWidget1();
#override
Widget build(BuildContext context) { print("widget builds 1");
return Container(height: 100, color: Colors.orange,);
}
}
class MyWidget2 extends StatelessWidget {
final int count;
MyWidget2(this.count);
#override
Widget build(BuildContext context) { print("widget builds 2");
return Text(count.toString());
}
}
when the constructor starts with a "const" keyword, which allows
you to cache and reuse the widget.
When calling the constructor to initiate the widget, use the "const" keyword. By calling with the "const" keyword, the widget does not rebuild when any parent widgets change
their state in the tree. If you omit the "const" keyword, the widget will be build every time the parent
widget redraws.