Omatt
Omatt

Reputation: 10539

Flutter TextFormField not immediately in focus on onFocusChange

I'd like to detect TextFormField with Focus(onFocusChange(bool)), but when the TextFormField is in focus, the field isn't activated with the cursor as expected. I need to press tab again for the cursor to appear on the TextFormField. Any idea why the TextFormField doesn't get highlighted immediately on tab/next?

Form(
  child: Column(
    children: <Widget>[
      TextFormField(
        textInputAction: TextInputAction.next,
      ),
      Focus(
        onFocusChange: (bool inFocus){
          /// When tab is pressed, I expect the cursor to be on TextFormField
          debugPrint('TextFormField 2 in focus? $inFocus');
        },
        child: TextFormField(),
      ),
    ],
  ),
),

My current workaround on this issue is using a listener on FocusNode.

TextFormField(
  focusNode: _focusNode,
),

But this requires initialization and proper disposal.

final _focusNodePrice = FocusNode();

@override
void initState() {
  super.initState();
  _focusNode.addListener(_onFocusChange);
}

@override
dispose(){
  super.dispose();
  _focusNode.removeListener(_onFocusChange);
  _focusNode.dispose();
}

/// Handles the quirks of TextFormField requirements
void _onFocusChange() {
  debugPrint('TextFormField inFocus? ${_focusNode.hasFocus}');
}

Here's a minimal repro of the issue.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: FocusScope(
        child: Form(
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  TextFormField(
                    textInputAction: TextInputAction.next,
                    decoration: InputDecoration(
                        hintText: 'Hint goes here 1'),
                  ),
                  Focus(
                    onFocusChange: (bool inFocus){
                      debugPrint('TextFormField 2 in focus? $inFocus');
                    },
                    child: TextFormField(
                      textInputAction: TextInputAction.next,
                      decoration: InputDecoration(
                          hintText: 'Hint goes here 2'),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Upvotes: 0

Views: 1393

Answers (3)

Mahdi Sabbagh
Mahdi Sabbagh

Reputation: 11

Maybe this code can help you

FocusNode _passFocus;
FocusNode _repeatPassFocus;
String _newPass
Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
         TextFormField(
             focusNode: _passFocus,
              textInputAction: TextInputAction.next,
              onFieldSubmitted: (value) {
                fieldFocusChange(context, _passFocus, _repeatPassFocus);
              },
              initialValue: _newPass,
              keyboardType: TextInputType.text,
              onSaved: (val) => _newPass = val,  
            )
          TextFormField(
             focusNode: _repeatPassFocus,
              textInputAction: TextInputAction.next,
              initialValue: _repeatNewPass,
              keyboardType: TextInputType.text,
              onSaved: (val) => _repeatNewPass= val,  
            ),
       ],
    );

  @override
  void initState() {
    _passFocus = FocusNode();
    _repeatPassFocus = FocusNode();
    super.initState();
  }

void fieldFocusChange(
    BuildContext context, FocusNode currentFocus, FocusNode nextFocus) {
  currentFocus.unfocus();
  FocusScope.of(context).requestFocus(nextFocus);
}

  @override
  void dispose() {
    _passFocus.dispose();
    _repeatPassFocus.dispose();
    super.dispose();
  }

Upvotes: 0

Aby Bermen
Aby Bermen

Reputation: 232

Basically, the better approach to my previous post would be to create a FocusNode for every field you want to check, and use if (node1.hasFocus) as a condition when needed.

So you don't need the Focus widget in this case

Upvotes: 0

Aby Bermen
Aby Bermen

Reputation: 232

When you press tab or next the first time, the focus goes on the focus widget, then you press again and it goes on it's child, so you can just remove the focus widget

edit: if you need the Focus widget anyways, you can create a FocusNode and requestFocus as shown in your edited example below

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  FocusNode node1 = FocusNode();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: FocusScope(
        child: Form(
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  TextFormField(
                    textInputAction: TextInputAction.next,
                    decoration: InputDecoration(hintText: 'Hint goes here 1'),
                  ),
                  Focus(
                    onFocusChange: (bool inFocus) {
                      if (inFocus) FocusScope.of(context).requestFocus(node1);
                      debugPrint('TextFormField 2 in focus? $inFocus');
                    },
                    child: TextFormField(
                      focusNode: node1,
                      textInputAction: TextInputAction.next,
                      decoration: InputDecoration(hintText: 'Hint goes here 2'),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Upvotes: 1

Related Questions