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 Intents) won't be able to
handle any shortcut Intents 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 EditableTextStates to define handlers for shortcut Intents.
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 Intents 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:
除非另有说明,本文档之所提及适用于 Flutter 的最新稳定版本,本页面最后更新时间: 2024-04-04。 查看文档源码 或者 为本页面内容提出建议.