Kinda Code
Home/Flutter/Flutter: Moving TextFormField focus with soft keyboard

Flutter: Moving TextFormField focus with soft keyboard

Last updated: February 15, 2023

In Flutter, the user can shift the focus between multiple TextFormFields inside a Form by using the soft keyboard (not by tapping the TextFormFields). This article shows you 2 ways to do so.

FocusScope.of(context).nextFocus()

This method is simple to implement.

Example Preview

This sample app contains a form with some TextFormFields. When a user focuses on a TextFormField, the soft keyboard will show up and provide a Next button that can be used to move the focus to the next field.

Here’s how it works on iOS and Android:

The complete code

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

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // Remove the DEBUG banner
      debugShowCheckedModeBanner: false,
      title: 'KindaCode.com',
      theme: ThemeData(
        primarySwatch: Colors.amber,
      ),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('KindaCode.com')),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(25),
          child: Form(
            child: Column(children: [
              // Email text field
              TextFormField(
                textInputAction: TextInputAction.next,
                decoration: const InputDecoration(labelText: 'Email'),
                onEditingComplete: () => FocusScope.of(context).nextFocus(),
              ),
              const SizedBox(height: 25),
              // Name text field
              TextFormField(
                  textInputAction: TextInputAction.next,
                  decoration: const InputDecoration(labelText: 'Name'),
                  onEditingComplete: () => FocusScope.of(context).nextFocus()),
              const SizedBox(height: 25),
              // Address text field
              TextFormField(
                  textInputAction: TextInputAction.next,
                  decoration: const InputDecoration(labelText: 'Address'),
                  onEditingComplete: () => FocusScope.of(context).nextFocus()),
              const SizedBox(height: 25),
              // Job text field
              TextFormField(
                  textInputAction: TextInputAction.done,
                  decoration: const InputDecoration(labelText: 'Job'),
                  // This is the last text field so there is no need to move the focus
                  onEditingComplete: () => FocusScope.of(context).unfocus())
            ]),
          ),
        ),
      ),
    );
  }
}

FocusScope.of(context).requestFocus()

This method is more complicated than the first one, but it’s more customizable. You can drive the focus to any TextFormField you want to, not just the next one.

Example Preview

The Code

The complete code in main.dart with explanations:

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

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      // Hide the debug banner
      debugShowCheckedModeBanner: false,
      title: 'Kindacode.com',
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // Create a global key that uniquely identifies the Form widget
  final _formKey = GlobalKey<FormState>();

  // Focus nodes
  late FocusNode _nameFocusNode;
  late FocusNode _addressFocusNode;
  late FocusNode _jobFocusNode;

  // Create the focus nodes
  @override
  void initState() {
    _nameFocusNode = FocusNode();
    _addressFocusNode = FocusNode();
    _jobFocusNode = FocusNode();
    super.initState();
  }

  // Clean the focus nodes
  @override
  void dispose() {
    _nameFocusNode.dispose();
    _addressFocusNode.dispose();
    _jobFocusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          centerTitle: true,
          title: const Text(
            'Kindacode.com',
          ),
        ),
        body: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(25),
            child: Form(
              key: _formKey,
              child: Column(children: [
                // Email text field
                TextFormField(
                  textInputAction: TextInputAction.next,
                  decoration: const InputDecoration(labelText: 'Email'),
                  onFieldSubmitted: (_) =>
                      FocusScope.of(context).requestFocus(_nameFocusNode),
                ),
                const SizedBox(height: 25),
                // Name text field
                TextFormField(
                    textInputAction: TextInputAction.next,
                    decoration: const InputDecoration(labelText: 'Name'),
                    focusNode: _nameFocusNode,
                    onFieldSubmitted: (_) =>
                        FocusScope.of(context).requestFocus(_addressFocusNode)),
                const SizedBox(height: 25),
                // Address text field
                TextFormField(
                    textInputAction: TextInputAction.next,
                    decoration: const InputDecoration(labelText: 'Address'),
                    focusNode: _addressFocusNode,
                    onFieldSubmitted: (_) =>
                        FocusScope.of(context).requestFocus(_jobFocusNode)),
                const SizedBox(height: 25),
                // Job text field
                TextFormField(
                  textInputAction: TextInputAction.done,
                  focusNode: _jobFocusNode,
                  decoration: const InputDecoration(labelText: 'Job'),
                  // This is the last text field so there is no need to move the focus
                )
              ]),
            ),
          ),
        ));
  }
}

Conclusion

We’ve explored more than one technique to allow the user to shift the TextFormfield focus using the soft keyboard. If you’d like to learn more new and amazing things about Flutter, take a look at the following articles:

You can also check out our Flutter category page or Dart category page for the latest tutorials and examples.