Skip to content

Issue with FocusNode Navigation When TextFormFields Are Conditionally Hidden #170122

Open
@SantaLMP05

Description

@SantaLMP05

Steps to reproduce

I encountered a problem where I have 5 TextFormFields, and some of them are conditionally hidden based on user input or state. When a field is hidden, the focus navigation (using FocusNode or keyboard traversal) stops working or breaks unexpectedly — especially when trying to move focus to the next visible field.

Expected results

When a TextFormField is conditionally hidden (e.g., based on a boolean condition), the focus should automatically skip over the hidden field and move to the next visible, focusable widget. Focus traversal should remain seamless and not break, even when fields appear or disappear dynamically.

Actual results

When a TextFormField is conditionally hidden, the focus system attempts to move to that hidden field and breaks. As a result, focus is lost or stuck, and the user cannot continue navigating to the next visible field using keyboard traversal or programmatic focus shifting (FocusScope.of(context).requestFocus(...)). This disrupts expected form navigation behavior.

Code sample

Code sample
import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(home: FocusIssueDemo()));
}

class FocusIssueDemo extends StatefulWidget {
  const FocusIssueDemo({super.key});

  @override
  State<FocusIssueDemo> createState() => _FocusIssueDemoState();
}

class _FocusIssueDemoState extends State<FocusIssueDemo> {
  final FocusNode node1 = FocusNode();
  final FocusNode node2 = FocusNode();
  final FocusNode node3 = FocusNode();

  final TextEditingController controller1 = TextEditingController();
  final TextEditingController controller2 = TextEditingController();
  final TextEditingController controller3 = TextEditingController();

  bool showSecondField = true;

  @override
  void dispose() {
    node1.dispose();
    node2.dispose();
    node3.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Focus Issue Sample')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            TextFormField(
              focusNode: node1,
              controller: controller1,
              decoration: const InputDecoration(labelText: 'Field 1'),
              onFieldSubmitted: (_) {
                FocusScope.of(context).requestFocus(
                    showSecondField ? node2 : node3);
              },
            ),
            if (showSecondField)
              TextFormField(
                focusNode: node2,
                controller: controller2,
                decoration: const InputDecoration(labelText: 'Field 2 (optional)'),
                onFieldSubmitted: (_) {
                  FocusScope.of(context).requestFocus(node3);
                },
              ),
            TextFormField(
              focusNode: node3,
              controller: controller3,
              decoration: const InputDecoration(labelText: 'Field 3'),
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  showSecondField = !showSecondField;
                });
              },
              child: Text(showSecondField ? 'Hide Field 2' : 'Show Field 2'),
            )
          ],
        ),
      ),
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[Paste your output here]

Metadata

Metadata

Assignees

No one assigned

    Labels

    in triagePresently being triaged by the triage teamwaiting for customer responseThe Flutter team cannot make further progress on this issue until the original reporter responds

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions