// Tutorial //

How To Communicate Between Widgets with Flutter using VoidCallback and Function(x)

Published on October 2, 2019 · Updated on March 18, 2021
Default avatar
By PaulHalliday
Developer and author at DigitalOcean.
How To Communicate Between Widgets with Flutter using VoidCallback and Function(x)

Introduction

A good paradigm for your Flutter projects is separating your widgets into small, testable units that can be adaptable to their context.

Flutter offers VoidCallback and Function(x) (where x can be a different type) for callback-style events between child and parent widgets.

In this article, you will use callback-style events to communicate between widgets with Flutter.

Prerequisites

To follow along with this article, you will need:

This article was verified with Flutter v1.22.2, Android SDK v30.0.2, and Android Studio v4.1.

Step 1 — Setting Up the Project

Once you have your environment set up for Flutter, you can run the following to create a new application:

  1. flutter create flutter_widget_communication

Navigate to the new project directory:

  1. cd flutter_widget_communication

Using flutter create will produce a demo application that will display the number of times a button is clicked.

You will build upon the code generated to experiment with callback-style events.

Step 2 — Passing Data from a Parent Widget to a Child Widget

You will create a parent widget (CounterPage) and a child widget (Count). The count value from the parent will display in the child.

Open main.dart in your code editor and modify it to use CounterPage():

lib/main.dart
import 'package:flutter/material.dart';
import 'counter_page.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Widget Communication',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: CounterPage(),
    );
  }
}

This code will display the CounterPage.

Create a new counter_page.dart file and add the following lines of code:

lib/counter_page.dart
import 'package:flutter/material.dart';
import 'count.dart';

class CounterPage extends StatefulWidget {
  _CounterPageState createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  int count = 0;

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Widget Communication")),
      body: Center(
        child: Count(count),
      ),
    );
  }
}

This code will pass the count to a child widget.

Create a new count.dart file and add the following lines of code:

lib/count.dart
import 'package:flutter/material.dart';

class Count extends StatelessWidget {
  final int count;

  Count(this.count);

  
  Widget build(BuildContext context) {
    return Text("$count");
  }
}

Compile your code and have it run in an emulator:

Screenshot of the Flutter application displaying a count of zero

This will display the count (currently set to the number zero) on the screen.

Next, you will add a VoidCallback.

Step 3 — Using VoidCallback

For the purpose of this tutorial, you will want to create a Button that will register clicks and notify the parent CounterPage.

As you don’t want to return a value here, you will need to register a VoidCallback. You will also add braces to the items within the Count constructor to make them named parameters.

Revisit count.dart and add onCountSelected:

lib/count.dart
class Count extends StatelessWidget {
  final int count;
  final VoidCallback onCountSelected;

  Count({
     this.count,
    this.onCountSelected,
  });

  
  Widget build(BuildContext context) {
    return FlatButton(
      child: Text("$count"),
      onPressed: () => onCountSelected(),
    );
  }
}

Then, revisit counter_page.dart and listen for the onCountSelected callback:

lib/counter_page.dart
import 'package:flutter/material.dart';
import 'count.dart';

class CounterPage extends StatefulWidget {
  _CounterPageState createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  int count = 0;

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Widget Communication")),
      body: Center(
        child: Count(
          count: count,
          onCountSelected: () {
            print("Count was selected.");
          },
        )
      ),
    );
  }
}

Compile your code and have it run in an emulator. Interact with the button and observe the output in your console:

Output
Count was selected.

At this point, however, the count value will remain zero.

Next, you will add a Function(x).

Step 4 — Using Function(x)

VoidCallback is useful for callback events with no expected value. For scenarios where you want to return a value back to the parent, you will want to use Function(x).

Revisit count.dart and add Function(int) onCountChanged:

lib/count.dart
import 'package:flutter/material.dart';

class Count extends StatelessWidget {
  final int count;
  final VoidCallback onCountSelected;
  final Function(int) onCountChanged;

  Count({
     this.count,
     this.onCountChanged,
    this.onCountSelected,
  });

  
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        IconButton(
          icon: Icon(Icons.add),
          onPressed: () {
            onCountChanged(1);
          },
        ),
        FlatButton(
          child: Text("$count"),
          onPressed: () => onCountSelected(),
        ),
        IconButton(
          icon: Icon(Icons.remove),
          onPressed: () {
            onCountChanged(-1);
          },
        ),
      ],
    );
  }
}

Then, revisit counter_page.dart and listen for the onCountChange callback:

lib/counter_page.dart
import 'package:flutter/material.dart';
import 'count.dart';

class CounterPage extends StatefulWidget {
  _CounterPageState createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  int count = 0;

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Widget Communication")),
      body: Center(
        child: Count(
          count: count,
          onCountSelected: () {
            print("Count was selected.");
          },
          onCountChanged: (int val) {
            setState(() => count += val);
          },
        )
      ),
    );
  }
}

When a button is clicked, the change value is passed from the Count child widget to the CounterPage parent widget. Then, the addition between the val and count is performed and count is updated.

Compile your code and have it run in an emulator:

Screenshot of the Flutter application display an Add button, a Remove button, and a count value of 4

Interact with the Add (+) and Remove (-) buttons, the count value should increment and decrement respectively.

Conclusion

In this article, you learned how to use VoidCallback and Function(x) to use callback-style events to communicate between widgets with Flutter.

If you’d like to learn more about Flutter, check out our Flutter topic page for exercises and programming projects.

If you’ve enjoyed this tutorial and our broader community, consider checking out our DigitalOcean products which can also help you achieve your development goals.

Learn more here


About the authors
Default avatar
Developer and author at DigitalOcean.

Still looking for an answer?

Was this helpful?
1 Comments

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Thanks so much for this article! I was having such a hard time understanding how to receive data from a widget. You made it clear and easy to understand