How to check class parent from BuildContext? - flutter

I'm implementing a commerce app and the focus now is on the three components: CartPage, CatalogPage and ProductListItem. The ProductListItem component is reused on two pages, cart and catalog. So inside ProductListItem Widget I must know what class is your parent to return the specific component. So far I mean to use BuildContext object to get some information from parent. If no this way, I mean on use a static string to check the parent, passing it on constructor when create ProductListItem component.
class ProductListItem extends StatefulWidget {
final Product product;
ProductListItem(this.product);
#override
_ProductListItemState createState() => _ProductListItemState(product);
}
class _ProductListItemState extends State<ProductListItem> {
final Product product;
int amount = 1;
_ProductListItemState(this.product);
#override
Widget build(BuildContext context) {
// if (parent is Cart) return 1;
// else if (parent is Catalog) return 2;
}
}
I'm accepting suggestions for other changes too.

BuildContext objects are passed to WidgetBuilder functions (such as StatelessWidget.build), and are available from the State.context member. Some static functions (e.g. showDialog, Theme.of, and so forth) also take build contexts so that they can act on behalf of the calling widget, or obtain data specifically for the given context.
Each widget has its own BuildContext, which becomes the parent of the widget returned by the StatelessWidget.build or State.build function. (And similarly, the parent of any children for RenderObjectWidgets.)

Maybe you want get history of Navigator to check parent.
Currently Navigator not support to get history of Navigator. But Flutter team is will update this feature on Navigator 2.0, They are done and merge to master. Waiting for next release.
How to get _history from NavigatorState
Navigator 2.0
On your case, temporary, pass flag to Detail Page for Parent.

Related

Avoid no_logic_in_create_state warning when saving reference to State of StatefulWidget in Flutter

Using Flutter, I display a list of elements in an app.
I have a StatefulWidget (ObjectList) that holds a list of items in its State (ObjectListState).
The state has a method (_populateList) to update the list of items.
I want the list to be updated when a method (updateList) is called on the widget.
To achieve this, I save a reference (_state) to the state in the widget. The value is set in createState. Then the method on the state can be called from the widget itself.
class ObjectList extends StatefulWidget {
const ObjectList({super.key});
static late ObjectListState _state;
#override
State<ObjectList> createState() {
_state = ObjectListState();
return _state;
}
void updateList() {
_state._populateList();
}
}
class ObjectListState extends State<ObjectList> {
List<Object>? objects;
void _populateList() {
setState(() {
// objects = API().getObjects();
});
}
// ... return ListView in build
}
The problem is that this raises a no_logic_in_create_state warning. Since I'm not "passing data to State objects" I assume this is fine, but I would still like to avoid the warning.
Is there a way to do any of these?
Saving the reference to the state without violating no_logic_in_create_state.
Accessing the state from the widget, without saving the reference.
Calling the method of the state from the outside without going through the widget.
It make no sense to put the updateList() method in the widget. You will not be able to call it anyway. Widgets are part of a widget tree, and you do not store or use a reference to them (unlike in other frameworks, such as Qt).
To update information in a widget, use a StreamBuilder widget and create the widget to be updated in the build function, passing the updated list to as a parameter to the widget.
Then, you store the list inside the widget. Possibly this may then be implemented as a stateless widget.

How to get the State<> instance inside of its StatefulWidget?

I have an unusual use case where I'd like to add a getter to a StatefulWidget class that accesses its State instance. Something like this:
class Foo extends StatefulWidget {
Foo({super.key});
int get bar => SomethingThatGetsFooState.bar;
#override
State<Foo> createState() => _FooState();
}
class _FooState extends State<Foo> {
int bar = 42;
#override
Widget build(BuildContext context) {
return Container();
}
}
Does SomethingThatGetsFooState exist?
I wonder, if your approach is the right way.
Flutter's way isn't 'Ask something about its state'
Flutter is much more like this: 'The consumer of a Widget passes something to another Widget, which the other Widget e.g. calls in case of certain situations (e.g. value change).'
Approach 1
You map pass a Callback Function to Foo and pass that along to _FooState.
If something special happens inside _FooState, you may call the callback and thus pass some value back to the provider of the Callback.
Approach 2
Probably use a state management solution like Flutter Redux. Using Flutter Redux, you establish a state store somewhere at the top of the widget tree, e.g. in MaterialApp.
Then you subscribe to the store at certain other locations, where dependent widgets exist, which need to update accordingly.
In one project I created certain Action classes, which I send to certain so called reducers of those states, to perform a change on the state:
StoreProvider.of<EModel>(context).dispatch( SaveToFileAction())
This call finds the relevant EModel state and asks it to perform the SaveToFileAction().
This way, a calling Widget not even needs to know, who is responsible for the Action.
The responsible Widget can even be moved around the widget tree - and the application still works. The initiator of SaveToFileAction() and the receiver are decoupled. The receiver you told a coordination 'Tell me, if someone tried to ask me for something.'
Could your provide some further details? Describe the usage pattern?
#SteAp is correct for suggesting there's a code smell in my OP. Typically there's no need to access State thru its StatefulWidget. But as I responded to his post, I'm fleshing out the first pass at a state management package, so it's an unusual case. Once I get it working, I'll revisit.
Below worked without Flutter complaining.
class _IntHolder {
int value;
}
class Foo extends StatefulWidget {
Foo({super.key});
final _intHolder = _IntHolder();
int get bar => _intHolder.value;
#override
State<Foo> createState() => _FooState();
}
class _FooState extends State<Foo> {
int value = 42;
#override
Widget build(BuildContext context) {
widget._intHolder.value = value;
return Container();
}
}

Can a StatelessWidget contain member variables?

I have a StatelessWidget that uses a ScopedModel to store its data. The widget basically presents a list of checkboxes and some buttons to save the state of the checked boxes.
Now I want to keep track of if the user has altered any of the checkboxes, i.e. checked/unchecked them since the widget was displayed. So I added something like this:
class MyCheckboxScreen extends StatelessWidget{
bool _hasBeenModified = false;
void _itemCheckedChange(bool checked, MyModel model){
_hasBeenModified = true;
// Code to change the model here
}
void _onCloseButton(){
if( _hasBeenModified ){
// Show a warning that there are modifications that will not be be saved
}
}
void _onSaveButton(Context context, MyModel model){
model.save();
Navigator.of(context).pop();
}
}
This seems to work, but my StatelessWidget now contains its own state.
The state isn't used to update the UI and redraw anything, it's only used to check if there are modifications to any checkbox when pressing the "Close" button.
Is it safe for a StatelessWidget to have this kind of internal state? Or could it be a problem? For example, could the widget be recreated unexpectedly, and the internal state lost?
I don't find the documentation to be very clear on this, it just says
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.
But this sounds like it only applies to state that affects the UI and requires rebuilding the widget.
Yes, a StatelessWidget can have mutable variables (your code compiles) but that's wrong.
A widget that does not require mutable state
This is taken from the documentation. Even if you have a single non-final variable, it means that something can actually be changed in your widget. It's not an immutable class. Ideally, you should use StatelessWidgets like this:
class MyWidget extends StatelessWidget {
final int a;
final String b;
const MyWidget(this.a, this.b);
}
Or something similar such as
class MyWidget extends StatelessWidget {
const MyWidget();
}
When you have non final variables, use a StatefulWidget. Your class has to clearly be a StatefulWidget:
// This is not final. It can be changed (and you will change it)
// so using the stateless way is wrong
bool _hasBeenModified = false;
void _itemCheckedChange(bool checked, MyModel model){
_hasBeenModified = true;
}
The UI doesn't matter actually because here's a matter of variables and mutability. Something is changing (bool _hasBeenModified) so it cannot be a StatelessWidget because it has to be used in all those cases where the state is immutable.
Are there any reasons why you don't use a stateful widget? Stateless widgets aren't intended to be used that way.. And without any benefits, I don’t understand why you overcomplicate things..

Flutter: display dynamic list

I've got a widget that displays a classes' list, but that list changes (the content changes) over time by other elements and interactions of the program. How can the DisplayListWidget detect this? Do the elements that change this list have to communicate with the displaylistwidget?
EDIT: I'm familiair with stateful widgets and setState () {}. It's just that the data changes in the background (e.g. by a timer) so there's no reference from bussiness logic classes to widgets to even call setState.
If you would like to notify (rebuild) widgets when data changes, check out provider package.
There are some other options:
BLoC (Business Logic Component) design pattern.
mobx
Redux
Good luck
Try wrapping the display widget in a state widget, and then whenever you update the list, call setState(() { //update list here })
ex.
// this is the widget that you'd nest directly into the rest of your tree
class FooList extends StatefulWidget {
FooListState createState() => FooListState();
}
// this is the state you will want to call setState on
class FooListState extends State<FooList> {
Widget build(BuildContext context) {
return DisplayListWidget [...]
}
}
I would recommend following this flutter tutorial where they dynamically update a list
And to learn more about StatefulWidgets and States check this out: https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html

Keep a list from provider up to date with AnimatedListView

I'm using the Provider library and have a class that extends ChangeNotifier. The class provides an UnmodifiableListView of historicalRosters to my widgets.
I want to create an AnimatedListView that displays these rosters. The animated list supposedly needs a separate list which will tell the widget when the widget to animate in or out new list items.
final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
This example uses a model to update both the list attached to the widget's state and the AnimateListState at the same time.
// Inside model
void insert(int index, E item) {
_items.insert(index, item);
_animatedList.insertItem(index);
}
I want to do the same thing but I'm having trouble thinking how. That is, what's a good way I can update the list provided by provider and also change the AnimatedListState.