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.

Matatias Situmorang
6 min readJun 21, 2023
Photo by Viktor Talashuk on Unsplash

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 the Child constructor.
  • Since we need context to the builder, we can call the builder inside the Widget 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 the Child 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 myMethodit 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.

preview

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)

--

--

Matatias Situmorang

Flutter developer | Blogger | Reach me on twitter @pmatatias_