Invoke Child Method from Parent Class in Flutter & Vice Versa
How to invoke a method in the child widget from the parent widget or invoke a parent method from the child widget.
When you read the title, you might think “Man, this could be handled by state management”. Well, I’m not going to deny that. But in this article, we’ll explore how to execute methods from parent and child classes.
We will explore 2 parts:
- How to invoke a method in the PARENT class from the child class
- How to invoke a method in the CHILD class from the parent class.
The first topic is quite similar, and I think most of flutter developer often to use it. But the second is something like anti-patern.
Why do we need separate parent and child widget?
There are lots of benefits if your code is well separated. For me, separating the widgets is more readable and easier to maintain the code. Some other sources also say we can avoid unnecessary widget rebuilds by separating widgets into classes. That’s true, but in this case, it doesn’t apply.
It is because we pass the method as an argument to the child class. That will make it an inconstant object. So, when the parent widget is rebuilt, the child widget is also rebuilt.
How to invoke a method in the PARENT class from the child class
It’s easy to execute the parent method from the child class. What we need to do is use .call()
on the method we are going to execute.
// example call void method
methodName?.call(); // if its nullable variable, use ? sign
// example if method with argument
methodName?.call(args);
eg:
We want to call the update String
value in the parent class with methodFromParent
method in the Child widget.
child.dart
class Child extends StatelessWidget {
const Child({super.key, this.methodFromParent});
final Function(String val)? methodFromParent;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
methodFromParent?.call("Updated from child");
},
child: const Text("Update parent"),
);
}
}
parent.dart
class ParentWidget extends StatefulWidget {
const ParentWidget({super.key});
@override
State<ParentWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
String parentTxt = "Initial Text";
int count = 0;
void updateParentTxt(String param) {
count++;
parentTxt = '$param $count times';
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(parentTxt)),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
/// another child widget
Child(methodFromParent: updateParentTxt),
],
), // pass the method as argument
);
}
}
Full code and demo: DartPad
In that example, we can see the updateParentTxt
are in the parent class.
void updateParentTxt(String param) {
count++;
parentTxt = '$param $count times';
setState(() {});
}
We execute it by pressing the ElevatedButton from the child class. In the initial view, the Appbar text is initial Text
then after the child button is pressed, it changes to the time of button is pressed.
If you have many questions in your mind, please keep it and read the second topic. We will use complex examples at the end of this article.
😄😄😄
How to invoke a method in the CHILD class from the parent class.
We have 2 options to call a method from the parent class. The first is by using the GlobalKey
, and the second is by using custombuilder.
I like using builder
and we’ll explore it in this article. But if you are curious about GlobalKey
, you can find it in this question [link]
steps:
- define the
typedef
for the custom builder
typedef MyBuilder = void Function(BuildContext context, void Function() methodFromChild);
- use the builder as
arguments
in theChild
constructor. - Since we need
context
to the builder, we can call the builder inside theWidget build()
method
child.dart
class Child extends StatefulWidget {
final MyBuilder builder;
const Child ({Key? key, required this.builder}) : super(key: key);
@override
_ChildState createState() => _ChildState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
widget.builder.call(context, childMethod); // <<<<< call it here
return Text('Child widget');
}
void childMethod() {
print('test');
}
}
next…
- Initialize the method in the parent class: `
late void Function() myMethod;
and use the builder for theChild
widget. myMethod
will execute the method in the children widget.
parent.dart
class ParentWidget extends StatelessWidget {
late void Function() myMethod;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body:Column(
children: [
IconButton(
icon: Icon(Icons.help),
onPressed: () {
myMethod.call(); // << this will execute the methodFromChild
},
),
Child(
builder:
(BuildContext context, void Function() methodFromChild) {
myMethod = methodFromChild;
},
) ],
),
),
);
}
}
Full code and demo: DartPad
from the example above, when we press the IconButton, it will execute the myMethod
function. This means, we already assign the builder myMethod = methodFromChild;
In the child class, we have use childMethod
to the builder argument.
void childMethod() {
print('test');
}
now in the parent class, every time we call press the IconButton and execute myMethod
it will execute the childMethod
Honestly, it looks awesome to me. 😃
We can call a method from both sides. Parent class and Child class. Ok now let's see how it's implemented.
Here is one example to handle view on a desktop. The screen size is wider than mobile, it fits horizontally.
This is parent code looks like:
class ParentWidget extends StatefulWidget {
....
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Responsive View")),
body: Row(
children: [
Expanded( child: // some widgets in the parents,
Expanded( child: Child1()),
Expanded( child: Child2()),
.....
Full code and demo: DartPad
Demo result:
— Parent
, Child1
and Child2
are separated classes. It's not available to update the value from different classes.
— Case on Child2:
in this case, action from the user to press the button. After showing the dialog and fill the form, it will AUTOMATICALLY update the local value child2Txt
and also update the variable in the parent class.
After the dialog is closed, we can see
- in the Child widget 2:
Text from dialog : LOREM ipsum
- in the parent widget:
Text from Child 2: LOREM ipsum
in this case, we are implementing: Invoke method in the parent class from the child class by using: widget.methodFromParent?.call(dialgTxt);
— Case on Child1:
in this widget, we have TextField. This widget has its own state controller. As you can see, I can type all the TextField widgets.
How can we update the value to the parent class?
Here 2 options:
- Call method in parent class inside the
onChaged
property.
TextField(
onChanged: (val) {
widget.methodFromParent?.call(val);
},
)
Of course, this will update the value in the parent class. But as we know, onChanged
is listening to every single change in the TextField. Can you imagine, when I type Lorem
it will execute the method 5 times.
L
, Lo
, Lor
, Lore
, and last Lorem
I think it's really bad, since in the parent method, we also call the setState().
only type Lorem
we need to rebuild the widget 5 times.
In Child2, we call the parent method only when the dialog is closed.
- Call method in child class from the parent class
First, create a method to collect all the values in Child1 class:
void _localMethod() {
print("invoke method in Child 1");
final collectedString = [_ctlr.text, _ctlr2.text, _ctlr3.text];
widget.methodFromParent?.call(collectedString);
}
this method we collect all controller values and then call again the method in the parent.
We can collect all the data from the child class and save it to the database. No need to listen onChanged
value from the TextField widget.
Thank you for having the end. Clap 👏 and share if you like this article. Feel free to leave any comments. I would love to discuss more.
Star the code on Github gist to save it for later: call_method_parent_child.dart (github.com)