Reputation: 6328
I have a StatefulWidget where there is a ListView holding several childs widget.
One of the child is a GridView
containing some items.
What I would want to achieve is to rebuild this GridView
child when a button is pressed from the Parent widget. The button is located in the bottomNavigationBar
in the Parent widget.
However, when I pressed the button, it should go to the _resetFilter()
method, which works. But the setState()
doesn't seem to update the GridView build()
method inside Child widget.
class ParentState extends State<Parent> {
// removed for brevity
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(...),
bottomNavigationBar: BottomAppBar(
child: new Row(
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 5.0),
child: SizedBox(
onPressed: () {
_resetFilter();
},
)
),
],
),
),
body: Container(
child: Form(
key: _formKey,
child: ListView(
children: <Widget>[
Column(
children: <Widget>[
Container(
child: Column(
children: <Widget>[
Container(...), // this works
Column(...),
Container(...), // this works
Container(
child: GridView.count(
// ...
children:
List.generate(oriSkills.length, (int i) {
bool isSkillExist = false;
if (_selectedSkills.contains(rc.titleCase)) {
isSkillExist = true;
} else {
isSkillExist = false;
}
return Child( // this doesn't work
id: oriSkills[i]['id'],
name: oriSkills[i]['description'],
skillSelect: isSkillExist, // this boolean showed correct value from the above logic
onChange: onSkillChange,
);
}),
),
),
],
),
)
],
)
],
)),
),
);
}
void _resetFilter() {
setState(() {
_theValue = 0.0;
searchC.text = "";
_selectedSkills = []; // this is the variable that I'd like the GridView to recreate from.
});
}
}
I tried to print one of the field name inside Child widget, but it always showing the old value instead of the new one.
Even after presing the button, it does passing correct value to ChildState
.
class ChildState extends State<Child> {
final String name;
final MyCallbackFunction onChange;
bool skillSelect;
double size = 60.0;
ChildState({this.name, this.skillSelect, this.onChange});
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
void setSkillLevel() {
setState(() {
if (skillSelect) {
skillSelect = false;
onChange(name, false);
} else {
skillSelect = true;
onChange(name, true);
}
});
}
Color _jobSkillSelect(bool select) {
print(select); // always print old state instead of new state
return select ? Color(MyColor.skillLvlOne) : Color(MyColor.skillDefault);
}
@override
Widget build(BuildContext context) {
return Container(
child: Column(children: <Widget>[
InkResponse(
onTap: setSkillLevel,
child: Container(
height: size,
width: size,
decoration: BoxDecoration(
image: DecorationImage(
colorFilter: ColorFilter.mode(_jobSkillSelect(skillSelect), BlendMode.color),
),
),
)),
]));
}
}
How can I update the Child widget to have the updated value from the Parent widget after reset button is pressed?
Upvotes: 1
Views: 2172
Reputation: 2537
You might want to pass the values to the actual Child class. Not to its state.
The class is whats rebuilding once your parent rebuilds. So the new values will be reflected.
So your Child
implementation should look something like this (don't forget to replace the onChange
Type to your custom Function.
class Child extends StatefulWidget {
final String name;
final Function(void) onChange;
final bool skillSelect;
final double size;
final Function(bool) onSkillLevelChanged;
const Child({Key key, this.name, this.onChange, this.skillSelect, this.size, this.onSkillLevelChanged}) : super(key: key);
@override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
Color _jobSkillSelect(bool select) {
print(select); // always print old state instead of new state
return select ? Color(MyColor.skillLvlOne) : Color(MyColor.skillDefault);
}
@override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
InkResponse(
onTap: () {
if (widget.onSkillLevelChanged != null) {
widget.onSkillLevelChanged(!widget.skillSelect);
}
},
child: Container(
height: widget.size,
width: widget.size,
decoration: BoxDecoration(
image: DecorationImage(
colorFilter: ColorFilter.mode(_jobSkillSelect(widget.skillSelect), BlendMode.color),
),
),
)),
],
),
);
}
}
In this case the Child ist not responsible anymore for managing its skillSelect
property. It simply calls a Function
on its parent. The parent then builds with a new skillSelect boolean.
So you might use this child like this:
return Child( // this doesn't work
id: oriSkills[i]['id'],
name: oriSkills[i]['description'],
skillSelect: oriSkills[i]['isSkillExist'],
onChange: onSkillChange,
onSkillLevelChanged: (newSkillLevel) {
setState(() {
oriSkills[i]['isSkillExist'] = newSkillLevel;
});
},
);
Upvotes: 3