TextField FocusNode attach location change
Summary
#EditableText.focusNode
is now attached to a dedicated Focus
widget below EditableText
.
Context
#A text input field widget (TextField
, for example) typically owns a FocusNode
. When that FocusNode
is the primary focus of the app, events (such as key presses) are sent to the BuildContext
to which the FocusNode
is attached.
The FocusNode
also plays a roll in shortcut handling: The Shortcuts
widget translates key sequences into an Intent
, and tries to find the first suitable handler for that Intent
starting from the BuildContext
to which the FocusNode
is attached, to the root of the widget tree. This means an Actions
widget (that provides handlers for different Intent
s) won't be able to handle any shortcut Intent
s when the BuildContext
that has the primary focus is above it in the tree.
Previously for EditableText
, the FocusNode
was attached to the BuildContext
of EditableTextState
. Any Actions
widgets defined in EditableTextState
(which will be inflated below the BuildContext
of the EditableTextState
) couldn't handle shortcuts even when that EditableText
was focused, for the reason stated above.
Description of change
#EditableTextState
now creates a dedicated Focus
widget to host EditableText.focusNode
. This allows EditableTextState
s to define handlers for shortcut Intent
s. For instance, EditableText
now has a handler that handles the "deleteCharacter" intent when the DEL key is pressed.
This change does not involve any public API changes but breaks codebases relying on that particular implementation detail to tell if a FocusNode
is associated with a text input field.
This change does not break any builds but can introduce runtime issues, or cause existing tests to fail.
Migration guide
#The EditableText
widget takes a FocusNode
as a parameter, which was previously attached to its EditableText
's BuildContext
. If you are relying on runtime typecheck to find out if a FocusNode
is attached to a text input field or a selectable text field like so:
focusNode.context.widget is EditableText
(focusNode.context as StatefulElement).state as EditableTextState
Then please read on and consider following the migration steps to avoid breakages.
If you're not sure whether a codebase needs migration, search for is EditableText
, as EditableText
, is EditableTextState
, and as EditableTextState
and verify if any of the search results are doing a typecheck or typecast on a FocusNode.context
. If so, then migration is needed.
To avoid performing a typecheck, or downcasting the BuildContext
associated with the FocusNode
of interest, and depending on the actual capabilities the codebase is trying to invoke from the given FocusNode
, fire an Intent
from that BuildContext
. For instance, if you wish to update the text of the currently focused TextField
to a specific value, see the following example:
Code before migration:
final Widget? focusedWidget = primaryFocus?.context?.widget;
if (focusedWidget is EditableText) {
widget.controller.text = 'Updated Text';
}
Code after migration:
final BuildContext? focusedContext = primaryFocus?.context;
if (focusedContext != null) {
Actions.maybeInvoke(focusedContext, ReplaceTextIntent('UpdatedText'));
}
For a comprehensive list of Intent
s supported by the EditableText
widget, refer to the documentation of the EditableText
widget.
Timeline
#Landed in version: 2.6.0-12.0.pre
In stable release: 2.10.0
References
#API documentation:
Relevant PR:
Unless stated otherwise, the documentation on this site reflects the latest stable version of Flutter. Page last updated on 2024-04-04. View source or report an issue.