In my recent flutter project I got a requirement to pass children states to parent state and move to the next screen based on children states.
As above image user can be select or deselect a row by clicking it. So I made a stateful widget which can be used multiple times.
class RowContent extends StatefulWidget {
final String question;
RowContent(this.question);
@override
_RowContentState createState() => _RowContentState(question);
}
class _RowContentState extends State<RowContent> {
String passedQuestion;
var selectedImage;
var unSelectedImage;
var currentImage;
_RowContentState(this.passedQuestion);
@override
void initState() {
super.initState();
selectedImage = AssetImage("assets/images/ic_check.png");
unSelectedImage = AssetImage("assets/images/ic_uncheck.png");
currentImage = unSelectedImage;
}
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
setState(() {
currentImage =
currentImage == selectedImage ? unSelectedImage : selectedImage;
});
},
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
left: 32.0,
right: 26.0,
),
child: Container(
height: 48,
width: 48,
decoration: new BoxDecoration(
image: new DecorationImage(
image: currentImage, fit: BoxFit.scaleDown),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 32.0),
child: Text(
passedQuestion
),
),
),
],
),
);
}
}
Now I want to go to the next screen only if user has selected all the 3 rows. This is where I wonder how can I pass the children states to the parent state so that parent can decide when to move to the next screen. for achive this I used callback methods.
Here is the code of parent component with callbacks.
class OpenReport extends StatefulWidget {
@override
_OpenReportState createState() => _OpenReportState();
}
class _OpenReportState extends State<OpenReport> {
int totalCount = 0;
callback(int value){
setState(() {
totalCount = totalCount + value;
debugPrint(totalCount.toString());
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: buildAppBar(context),
body: buildOpenReportContent(context),
);
}
AppBar buildAppBar(BuildContext context) {
return AppBar(
elevation: 0.0,
actions: <Widget>[
IconButton(
icon: Icon(
Icons.close,
),
onPressed: () {
if (Navigator.canPop(context)) {
Navigator.pop(context, true);
}
},
),
],
centerTitle: true,
);
}
Widget buildOpenReportContent(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
Container(
child: Padding(
padding: const EdgeInsets.only(
left: 80.0,
top: 28.0,
right: 80.0,
),
child: Text(
"",
textAlign: TextAlign.center,
),
),
),
Expanded(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 64.0, bottom: 32.0),
child: RowContent("Row 1 click to enable", callback),
),
RowContent("Row 2 click to enable", callback),
Padding(
padding: const EdgeInsets.only(top: 32.0),
child: RowContent("Row 3 click to enable", callback),
),
],
),
),
Container(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Container(
height: 48.0,
child: FlatButton(
onPressed: () {
if (totalCount == 3){
debugPrint("implement go to next screen");
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"go to next screen",
),
],
),
),
),
),
),
],
),
);
}
}
Here's how the child component changed.
class RowContent extends StatefulWidget {
final String question;
final Function(int) callback; // getting the parent function
RowContent(this.question, this.callback);
@override
_RowContentState createState() => _RowContentState(question);
}
class _RowContentState extends State<RowContent> {
String passedQuestion;
var selectedImage;
var unSelectedImage;
var currentImage;
_RowContentState(this.passedQuestion);
@override
void initState() {
super.initState();
selectedImage = AssetImage("assets/images/ic_check.png");
unSelectedImage = AssetImage("assets/images/ic_uncheck.png");
currentImage = unSelectedImage;
}
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
setState(() {
currentImage =
currentImage == selectedImage ? unSelectedImage : selectedImage;
widget.callback(currentImage == selectedImage ? 1 : -1); // calling parent function
});
},
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
left: 32.0,
right: 26.0,
),
child: Container(
height: 48,
width: 48,
decoration: new BoxDecoration(
image: new DecorationImage(
image: currentImage, fit: BoxFit.scaleDown),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 32.0),
child: Text(
passedQuestion,
),
),
),
],
),
);
}
}
This is how you can pass child state to parent state. There is another way to do this using global state obeject. Anyway hope someone find this useful.
Happy coding!!!