Child widget not updating parent widget when child parameter changes - flutter

I have a child stateful widget that isn't updating the parent widget when an update to one of the parameters occurs.
The parent widget (OtherListVIew) is used to display a list of items on a PaginatedGrid with each item having a checkbox. The problem is that when a user views the page and there are selected items that are retrieved by the _retrieveItems() the changes do not reflect since at this point the ui is already rendered on the screen with the original empty list. These retrieved values do not reflect on OtherListView (_selectedItems is then used to display the checked items).
In this child widget I have the code
List<String> _selectedItems= [];
#override
void didChangeDependencies() {
super.didChangeDependencies();
WidgetsBinding.instance.addPostFrameCallback((_) {
_retrieveItems(); // this function retrieves items and reinitializes _selectedItems
});
}
#override
Widget build(BuildContext context) {
return SomeListView(
key: _listViewKey,
selectedItems: _selectedItems,
);
}
SomeListView looks as below
class SomeListView extends OtherListView {
const SomeListView ({super.key, super.selectedItems});
#override
State<SomeListView > createState() => SomeListViewState();
}
class SomeListViewState extends OtherListViewState<SomeListViewState> {
// override some method here from OtherListView for customization purposes
#override
Widget build(BuildContext context) {
return super.build(context);
}
}
OtherListView is the parent class that contains the PaginatedGrid that renders the passed _selectedItems to the UI. Why is it that after the _selectedItems have been refetched they are not reflecting on OtherListView? Is this an issue with my state management?
I have attempted using didUpdateWidget under OtherListView and indeed I can see the new values under the newWidget but they are not rendered on the UI. What I'm I missing?
#override
void didUpdateWidget(covariant T oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.selectedItems != oldWidget.selectedItems) {
log.info('OtherListView selected items have been updated.');
}
}
OtherListView extends StatefulWidget {
final List<String>? selectedItems;
const OtherListView({Key? key,this.selectedItems})
: super(key: key);
// ...
}

honestly i don't understand your code but the problem you are facing the parameter is not updating because with setState it will only rebuild the parameter/state/variables that are in the same screen/class widget where the setState is called so you have to raise your parameter to the parent screen then pass it to the child with constructor and create a constructor in your child for function onTap for example then in the parent screen you use that onTap and called setState inside

Related

Flutter detect when value passed to the statefull widget changes

I have a statefull widget where am passing an integer to. I would like to execute a method when the value passed to the widget changes
class Test extends StatefulWidget {
final String item;
const Test({
Key? key,
required this.item,
}) : super(key: key);
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
List<String> hh = [];
#override
void initState() {
super.initState();
print("init state value is ${widget.item}");
}
#override
Widget build(BuildContext context) {
return Container();
}
void getDataItems() {
print("value passed is ${widget.item}");
setState(() {
hh = [widget.item];
});
}
}
So on the above i would like to detect when the value of string item passed to the widget changes and call the method getDataItems which will later update the list hh.
How can i detect when the value passed statefull widget has changed?
You can check the useEffect, which is based on the React hook useEffect.
Or you can hardcode it.
Create other integer: late int lastCalledInteger;
And check this in the build:
#override
Widget build(BuildContext context) {
if(lastCalledInteger != integer) {
getDataItems();
lastCalledInteger = integer;
}
return Container();
}
You can simply override didUpdateWidget on State to be informed of every change of passed in arguments. If you have multiple arguments you of course need to change what changed. For this didUpdateWidget provides you with the oldWidget as only parameter.
From the documentation:
Called whenever the widget configuration changes.
[...]
Override this method to respond when the widget changes (e.g., to
start implicit animations).

Does StatefulWidget not rebuild State every time it has new data?

The widget TrainsPage is added to the build graph in main.dart, when the corresponding menu button is clicked. This is done twice: once when _routes is empty and a second time when _routes is filled.
Widget pageSelector() {
if (_selectedIndex == 2) {
return new TrainsPage(routes: _routes);
} else
return Text("");
}
In TrainsPage.dart, I have the code for the stateful widget TrainsPage.
class TrainsPage extends StatefulWidget {
const TrainsPage({Key? key, required this.routes}) : super(key: key);
final List<RSRoute> routes;
#override
_TrainsPageState createState() => _TrainsPageState();
}
class _TrainsPageState extends State<TrainsPage> {
List<RSRoute> _routes = List.empty();
#override
void initState() {
super.initState();
this._routes = new List<RSRoute>.from(widget.routes);
Now, the second time, TrainsPage gets called in main.dart (now with routes filled), initState() of _TrainsPageState is not called, which is responsible to read the data in routes. And because routes was empty the first time, there is nothing in display on the trains page.
Why does TrainsPage not rebuild _TrainsPageState, when it clearly got new data in the constructor?
This is exactly why the State exists : to keep the state of the current context alive even when the widget is rebuild.
If it was recreated each time the statefull widget is rebuild it could not keep the state of its own variables.
class MyWidget extends StatelessWidget {
var _someStateVariable = 0;
#override
void build(BuildContext context){
// here an action that increment _someStateVariable
}
}
Here _someStateVariable would be reset to 0 at each rebuild. Or if we wanted a StateFullWidget in the first place it's because we'll update this variable later and want to keep its updated value through the multiple widget rebuilds.
If you don't have such state variable to maintain maybe you don't need a StateFullWidget here.
Now to the solution to your problem : you can override didUpdateWidget instead of initstate since it will be called at each widget rebuild :
#override
void didUpdateWidget() {
didUpdateWidget();
_routes = new List<RSRoute>.from(widget.routes);
}

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();

getting height of a widget in flutter

I have made a list of widgets
List<Widget> my_list = [Text("Hello World"),Text("Hello Flutter")];
And I want to make a Widget which takes argument as list of widgets only in its constructor, and I have to do something using this widgets in which I need the size of each widget. Below is my Widget
class MyCustomWidget extends StatefulWidget{
List<Widget> widget_list;
MyCustomWidget(this.widget_list);
#override
_MyCustomWidgetState createState() => _MyCustomWidgetState();
}
class _MyCustomWidgetState extends State<MyCustomWidget>{
List<double> widget_height;
#override
Widget build(BuildContext context) {
return null;
}
}
In this list(widget_height) I have to save the height of each widget that is in the list(widget_list).
How to do this.
Is there any method like widget_list[0].getHeight()?
Edit: I can't use global key. Refer comment section for more detail.
And I need all this data before rendering the object. So maybe we can override initState() method to get all this data betfore rendering.
Maybe I an not correct in the last part about rendering.

Flutter - animation widget, trigger animation only once but get data upates on widget

I came across the following problem already twice within my app:
Inside a StatefulWidget I have a widget that contains an animation and additionally displays other dynamic data. The animation is triggered after the animation widget has been built. However, after the animation (the drawing of a figure) has been played, I want to maintain it in the same drawn state. The rest of the dynamic data of the widget should be always rebuilt when the parent StatefulWidget's build method is called.
Below, there is a simple example of what I try to achieve:
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, #required this.data}) : super(key: key);
CustomData data;
#override
State<StatefulWidget> createState() => _MyState();
}
Now, I had two ideas of how to implement the state:
1) Initialize the animation widget in initState() and "manually" handle data updates within didUpdateWidget().
class _MyState extends State<MyStatefulWidget> {
AnimationWidget _animationWidget;
#override
void initState() {
super.initState();
_animationWidget = AnimationWidget(displayData: widget.data);
// starts the animation when the widget is built (?)
WidgetsBinding.instance
.addPostFrameCallback((_) => _animationWidget.startAnimation());
}
#override
Widget build(BuildContext context) {
return Column(
children: [
_animationWidget,
... // other content
],
... // column size etc.
);
}
#override
void didUpdateWidget(SackenNotebookContent oldWidget) {
super.didUpdateWidget(oldWidget);
if(dataChanged(oldWidget.data, widget.data))
setState(() {
_animationWidget = AnimationWidget(displayData: widget.data);
});
}
}
2) To create the animation widget within the build() method. But then I already have doubts about when/where to trigger the animation to start. One way would be to trigger it in didUpdateWidget(). But again, the animation would then be triggered on every rebuild of the widget...
class _MyState extends State<MyStatefulWidget> {
#override
Widget build(BuildContext context) {
var animationWidget = AnimationWidget(displayData: widget.data);
// but where to call animationWidget.startAnimation() ?!
// when I call it here I get a null pointer exception...
return Column(
children: [
animationWidget,
... // other content
],
... // column size etc.
);
}
}
Has anyone got an idea of how I could handle such a problem in flutter?