When it comes to building engaging apps, creating an exceptional user experience is crucial. One way to achieve this is by incorporating interactive widgets that allow users to input their thoughts and ideas seamlessly. In this article, we'll dive into the world of app development and explore how to create a text input field with a submit button using Flutter.

Introduction

In our previous lessons, we've covered various aspects of building an interactive tile widget that displays user guesses. However, to make it truly functional, we need a way for users to input those guesses in the first place. This is where the power of callback functions comes into play.

Implementing Callback Functions

To allow users to type in their guesses, you'll create a dedicated widget named GuessInput. First, create the basic structure for your GuessInput widget that requires a callback function as an argument. Name the callback function onSubmitGuess. Add the following code to your main.dart file:

`dart

class GuessInput extends StatelessWidget {

GuessInput({super.key, required this.onSubmitGuess});

final void Function(String) onSubmitGuess;

@override

Widget build(BuildContext context) {

// You'll build the UI in the next steps.

return Container(); // Placeholder

}

}

`

The line final void Function(String) onSubmitGuess; declares a final member of the class called onSubmitGuess that has the type void Function(String). This function takes a single String argument (the user's guess) and doesn't return any value (denoted by void). This callback tells us that the logic that actually handles the user's guess will be written elsewhere. It's a good practice for interactive widgets to use callback functions to keep the widget that handles interactions reusable and decoupled from any specific functionality.

Building the Visual Parts of the Widget

Now, it's time to build the visual parts of this widget. This is what the widget will look like:

`dart

class GuessInput extends StatelessWidget {

GuessInput({super.key, required this.onSubmitGuess});

final void Function(String) onSubmitGuess;

@override

Widget build(BuildContext context) {

return Row(

children: [

Expanded(

child: Padding(

padding: const EdgeInsets.all(8.0),

child: TextField(

maxLength: 5,

decoration: InputDecoration(

border: OutlineInputBorder(

borderRadius: BorderRadius.all(Radius.circular(35)),

),

),

),

),

),

],

);

}

}

`

You have seen some of these widgets in previous lessons: Row and Padding. New, though, is the Expanded widget. When a child of a Row (or Column) is wrapped in Expanded, it tells that child to fill all the available space along the main axis (horizontal for Row, vertical for Column) that has't been taken by other children.

Handling Text with TextEditingController

Next, you need a way to manage the text that the user types into the input field. For this, use a TextEditingController.

`dart

class GuessInput extends StatelessWidget {

GuessInput({super.key, required this.onSubmitGuess});

final void Function(String) onSubmitGuess;

final TextEditingController _textEditingController = TextEditingController();

@override

Widget build(BuildContext context) {

return Row(

children: [

Expanded(

child: Padding(

padding: const EdgeInsets.all(8.0),

child: TextField(

maxLength: 5,

decoration: InputDecoration(

border: OutlineInputBorder(

borderRadius: BorderRadius.all(Radius.circular(35)),

),

),

controller: _textEditingController,

),

),

),

],

);

}

}

`

A TextEditingController is used to read, clear, and modify the text in a TextField. To use it, pass it into the TextField.

Capturing User Input

Now, when a user inputs text, you can capture it with the _textEditingController, but you'll need to know when to capture it. The simplest way to react to input is by using the TextField.onSubmitted argument. This argument accepts a callback, and the callback is triggered whenever the user presses the "Enter" key on the keyboard while the text field has focus.

For now, ensure that this works by adding the following callback to TextField.onSubmitted:

`dart

class GuessInput extends StatelessWidget {

GuessInput({super.key, required this.onSubmitGuess});

final void Function(String) onSubmitGuess;

final TextEditingController _textEditingController = TextEditingController();

@override

Widget build(BuildContext context) {

return Row(

children: [

Expanded(

child: Padding(

padding: const EdgeInsets.all(8.0),

child: TextField(

maxLength: 5,

decoration: InputDecoration(

border: OutlineInputBorder(

borderRadius: BorderRadius.all(Radius.circular(35)),

),

),

controller: _textEditingController,

onSubmitted: (String input) {

print(_textEditingController.text); // Temporary

},

),

),

),

],

);

}

}

`

In this case, you could print the input passed to the onSubmitted callback directly, but a better user experience clears the text after each guess.

By incorporating these interactive widgets and handling user input effectively, you'll be well on your way to crafting an exceptional app user experience that will keep users engaged for hours. Remember to focus on providing intuitive interfaces, clear feedback, and seamless interactions to create a truly immersive experience.