Where should one keep final values?
In the StatefulWidget (my subclass of course) instance and access it from the State (subclass) via widget.thatFinalField, or
In the State itself. I've seen both approaches already.. Any pros and cons for each of them?
You should store final member fields (which are passed via constructor arguments) on the StatefulWidget and make them public.
The StatefulWidget's associated State should use only the default constructor (no arguments), and its member fields should be private (starting with a _) and mutable. Initialize them inline or in initState if expensive or async work is necessary.
This pattern allows the StatefulWidget to be recreated/rebuilt with new constructor arguments when its parents call setState, while re-using the previous State and letting it keep the values stored in its mutable member fields.
Related
I am passing the first object of a List myList[] from Class A to a stateful class B that is a separate page in my application. The passed object is therefore created as immutable since flutter prefers immutable classes and immutable classes need const constructors, variables in which must be final. In the B class, I later call a function that modifies the object from the List, and objects in a list aren't immutable. But since I have already passed the object, I can't see the updated value. I solved this issue by making: the value - non-final, the constructor - not const, and therefore the class - mutable. Another way I could solve it is to just use global variables instead.
My question is - Is there a way to reinstantiate class B? I.e., go to class A, navigate to page B by passing the (now updated) first object of List myList[] ?
Perhaps you could try using a State manager like Provider. From my understanding, you're trying to share variables across different widgets. Using a state management library seems to do exactly what you want.
Writing my first Flutter application and we need to use a PaginatedDataTable. The docs say the source field should
generally have a lifetime longer than the PaginatedDataTable
widget itself; it should be reused each time the PaginatedDataTable
constructor is called.
https://docs.flutter.io/flutter/material/PaginatedDataTable/source.html
What is the best way to manage this? Is there a common pattern? My initial thought is the singleton pattern but I come from the Java world so I'm not sure if this is correct.
Can you also explain why the DataTableSource should be reused? Thanks.
DataTableSource is the state of your Table. It contains all your table data and whether or not rows are selected.
It must be persisted somewhere because you'd loose all your selection and potential loaded data if you recreated your DataSource anew everytime.
This is especially true considering data is lazy loaded, and potentially comes from http call.
Ideally you'll want to store your DataSource inside a StatefulWidget or something similar (InheritedWidget, a Stream, whatever).
class MyTable extends StatefulWidget {
#override
_MyTableState createState() => new _MyTableState();
}
class _MyTableState extends State<MyTable> {
final myDataSource = new MyDataSource();
...
}
Let's say I need a user service which has all my user variables and functions, and another item service with all item variables and functions, etc etc. I want all this data and functions available throughout the project, passing the user data where it's needed, for example.
I suspect it has something to do with inherited widgets, but how does that work? Like, I see how I could use one inherited widget at the root level, but am I supposed to build a bunch of inherited widgets at the root-level for each service? Or just put ALL the data in the one top inherited widget? Seems like that may get messy. I have yet to see an example of this.
... or should I just be using a class with static variables and call on that where I need it?
See the link provided by Günter above. If you have thirty minutes watch Brian Egan's DartConf 18 presentation: Keep it Simple, State. Also see Brian's example code here. He shows the same app coded using InheritedWidget with a Controller, and with Flutter Redux.
Here's a example of a single InheritedWidget from a simple app of mine...
class StateContainer extends InheritedWidget {
final List<Membership> memberships;
final IntFunction getNextIndex;
final VoidMembershipFunction updateMembership;
final VoidIntFunction deleteMembership;
const StateContainer({
this.memberships,
this.getNextIndex,
this.updateMembership,
this.deleteMembership,
Widget child,
})
: super(child: child);
static StateContainer of(BuildContext context) {
return context.inheritFromWidgetOfExactType(StateContainer);
}
#override
bool updateShouldNotify(StateContainer oldWidget) {
return true;
}
}
I pass in my MaterialApp as the child. A high proportion of the tree then becomes stateless as it has access to the data and methods if the StateContainer. You could think of these as your model and controller in one. Build methods in the tree often start with...
#override
Widget build(BuildContext context) {
final StateContainer container = StateContainer.of(context);
to get access to them.
You are right that a single InheritedWidget becomes unwieldy quickly - which is where you might invest in exploring Redux - see the last 10 minutes of the talk. In my experience, the area that gets messiest quickest is the updateShouldNotify method when ends up comparing all the member variables. (In this simple example there is only one member variable, and setState only gets called when it changes, so it's trivially always true.)
I develop my own custom widget which is used in some other view. In this custom widget, I have one class property which stores information, let's say that this is a list which gains new items inside the widget. Now I want to get items from this list from the level of my main widget.
How to do that?
I don't want to create a variable like this: var customWidget = MyCustomWidget() and then, get the inside variable like customWidget.createState().myList - I think this is a terrible solution (and I'm not sure if it will work). Also passing a list to the constructor of my custom widget looks very ugly.
Is there any other way to get other widget's state?
First, bear in mind that in Flutter data can only be passed downward. It is by design not possible to access data of children, only parents (although there are some hacks around it).
From this point, there are 2 main solutions to pass data:
Add the wanted data to your widgets constructors.
I don't think there is much to say here. Easy to use. But boring when you want to pass one field to all your widget tree. Use this method only when the scope of a value is limited. If it's something like configurations or user details, go for the second solution.
class Bar extends StatelessWidget {
final String data;
Bar({this.data});
#override
Widget build(BuildContext context) {
return Text(data);
}
}
Using Flutter's BuildContext
Each widget has access to a BuildContext. This class allows one widget to fetch information from any of their ancestors using one of the following methods:
inheritFromWidgetOfExactType(Type)
ancestorStateOfType(TypeMatcher)
ancestorWidgetOfExactType(Type)
...
As a matter of facts, if there's a data that needs to be accessed many times, prefer inheritFromWidgetOfExactType.
This uses InheritedWidget; which are specifics kind of widgets that are extremely fast to access to.
See Flutter: How to correctly use an Inherited Widget? for more details on their usage
As a side note, there a third solution. Which is GlobalKey
I won't go into too many details, as this is more a hack than a proper solution. (see Builder versus GlobalKey)
But basically, it allows getting the state/context of any widgets outside of the build call. Be it parents or children.
on my side I implemented onChanged callback.
For example In my Widget :
class ObjectiveCardWidget extends StatefulWidget {
final Objective? objective;
final ValueChanged<Objective?>? onChanged;
ObjectiveCardWidget({this.objective, this.onChanged});
#override
State<ObjectiveCardWidget> createState() => _ObjectiveCardWidgetState();
}
when my data is updated un my custom widget I just called :
widget.onChanged!(newValue); // newvalue has been set previously
In my parentWidget I used onChanged as usual:
ObjectiveCardWidget(
objective: myObjective,
onChanged: (value) {
setState(() {
myObjective = value;
});
})
Hope it will help.
I am trying to initialize some event stream in a class. I want that stream to be final, but controlled by StreamController. I have tried following code:
import "dart:async";
class Dog {
final StreamController _onBarkController;
final Stream onBark;
Dog() :
_onBarkController = new StreamController(),
onBark = _onBarkController.stream;
}
But this code is illegal, because the access (even implicit) to this is forbidden in the initializer list.
Is there any way how to achieve this?
There isn't a great way to solve the general problem of needing to destructure some object into multiple final fields, which is basically what you're attempting here. But the good news is that usually you don't really need to. The two approaches I would recommend are factory constructors and not keeping derived state.
Factory constructors are great because you can perform arbitrary computation to create your arguments before calling the real constructor, which can usually only have an initializer list. In this case you can have a factory constructor create the StreamController and pass it and the stream to a private constructor.
Even better for you though, would be to not store the Stream in a field because you can get to it via the controller. I do this all the time with streams:
class Dog {
final StreamController _onBarkController = new StreamController();
Stream get onBark => _onBarkController.stream;
}
onBark is really a value derived from _onBarkController, so there's no need to store it.