Flutter Widget Communication

Send Function from one widget to another

Ceren Yaşa
4 min readMar 9, 2021

In this article, we will examine the communication of the widgets in two different Dart files with Flutter. In a previous project, I had to save the texts I received from TextFields in many different Dart files to the database when the button in another Dart file was clicked. I wanted to share the results of many researches with you.
Now let’s get to the structure we will use to make this communication happen: We will use a structure named Function so that the data from one Dart file can be used in another file. As you know, a function is called a structure that gives output by performing some operations on an input. With the function structure in Flutter, functions such as the button click feature can be sent to other files and operations can be done in this file.
Let’s examine the codes in which the function structure is used. First, let’s start by creating a main.dart as usual. We created a Scaffold widget in our main file and moved the body of this widget to a different Dart file under the name HomepageBody.

import 'package:flutter/material.dart';
import 'homepage_body.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.teal,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 1,
title: Text(
"Widget Communication",
style: TextStyle(color: Colors.teal, fontWeight: FontWeight.bold),
),
),
body: HomepageBody(),
);
}
}

In order to increase the readability of the codes in the software, they should be written in the smallest parts that can be divided and without repeating. For this reason, the TextField and Button widgets that should be on the User Login screen are split into separate Dart files.

import 'package:flutter/material.dart';
import 'custom_text.dart';
import 'input_button.dart';
import 'input_field.dart';
class HomepageBody extends StatelessWidget {
String _email, _password;
@override
Widget build(BuildContext context) {
final height = MediaQuery.of(context).size.height;
return Stack(
children: [
Container(
child: Image.asset(
"images/bakcground.png",
fit: BoxFit.cover,
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.all(height * 0.01),
child: Icon(
Icons.account_circle_outlined,
size: 200,
color: Colors.teal,
),
),
CustomText(text: "Email"),
InputField(
flag: "mail",
onTap: (String text) {
_email = text;
}),
CustomText(
text: "Password",
),
InputField(
flag: "password",
onTap: (String text) {
_password = text;
}),
InputButton(
userEmail: _email,
userPassword: _password,
),
],
)
],
);
}
}

As seen in the HomepageBody widget, a TextField was designed to avoid duplication and a single widget was written for both email and password. But there is a problem here. How can we read texts entered in TextFields in different files from another file?

In this article, a function named Function has been used in order to be able to use texts between widgets in different files. With the Function, the onChange function that is checked each time the TextField is entered is sent to the widget that calls it. Any operation that would normally be done with the TextField’s onChange function can now be done by writing to the body of the onTap function in HomePageBody.

import 'package:flutter/material.dart';class InputField extends StatelessWidget {
final Function(String) onTap;
final String flag;
const InputField({Key key, @required this.onTap, @required this.flag})
: super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width * 0.1,
vertical: MediaQuery.of(context).size.height * 0.01),
child: TextField(
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(25),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(25),
borderSide: BorderSide(color: Colors.teal.shade300)),
hintText: flag == "mail" ? "example@gmail.com" : "password123",
border: OutlineInputBorder()),
onChanged: (value) => onTap(value),
),
);
}
}

Let’s examine the following code snippets to examine the implementation of this function. First, let’s define the constructor of the class that contains TextField and the onTap function from HomepageBody. We have to define the function we will get from the Constructor. As this function returns, we have to return the text received from TextField, so the following lines should be added to the code.

// In the InputField widget
final Function(String) onTap;
const InputField({Key key, @required this.onTap, @required this.flag})
: super(key: key);

Data from the onTap function must be assigned to a variable. This process is performed in the body of the onTap function written on HomepageBody for this process. For example, in the code given below, the email and password information received is assigned to the _email and _password variable.

// In the HomepageBody widget
InputField(
flag: "mail",
onTap: (String text) {
_email = text;
}),

Finally, in order to perform the Sign In operation, the _email and _password values ​​are sent to the button widget and verification operations are performed.

import 'package:flutter/material.dart';class InputButton extends StatelessWidget {
final user = {"Email": "trialmail@gmail.com", "Password": "123456"};
final userEmail, userPassword;
String message;
InputButton({Key key, @required this.userEmail, @required this.userPassword})
: super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(MediaQuery.of(context).size.width * 0.03),
child: RaisedButton(
onPressed: () {
if (userEmail == user["Email"] && userPassword == user["Password"]) {
message = "Succesful Sign In";
} else {
message = "Sign In Error";
}
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(message),
actions: [
RaisedButton(
onPressed: () => Navigator.of(context).pop(),
child: Text("OK"),
color: Colors.teal,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15)),
)
],
);
});
},
color: Colors.teal,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
child: Text(
"Sign In",
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold, fontSize: 20),
),
),
);
}
}

In this way, functions can be used by sending them to widgets in different files.

--

--