- 焦点树 Focus tree- 焦点节点树,通常稀疏地镜像小部件树,表示可以接收焦点的所有小部件。
- 焦点节点 Focus node - 焦点树中的单个节点。该节点可以接收焦点,并且当它是焦点链的一部分时被称为“具有焦点”。它仅在获得焦点时才参与处理关键事件。
- 主要焦点 Primary focus- 距具有焦点的焦点树根最远的焦点节点。这是焦点节点,关键事件开始传播到主焦点节点及其祖先。
- 焦点链 Focus chain- 焦点节点的有序列表,从主焦点节点开始,沿着焦点树的分支到达焦点树的根。
- 焦点范围 Focus scope- 一个特殊的焦点节点,其作用是包含一组其他焦点节点,并只允许这些节点接收焦点。它包含有关哪些节点先前集中在其子树中的信息。
- 焦点遍历 Focus traversal- 按照可预测的顺序从一个可聚焦节点移动到另一个可聚焦节点的过程。当用户按Tab键移至下一个可聚焦控件或字段时,通常会在应用程序中看到这种情况。
FocusScopeNode
这是表示焦点层次结构范围的一个节点,用于将一组具有焦点控制的节点分组。这使得可以在这些节点之间移动焦点,并确保只有在同一组中的节点之间移动焦点。
Focus
一个管理FocusNode 的小部件,允许轻松访问管理焦点,而无需管理节点,它可以将FocusScopeNode和FocusNode连接起来,以便它们可以相互通信并处理焦点。
FocusNode
FocusNode是一个表示单个具有焦点控制的控件的节点。由于 FocusNode 是长寿命对象,所以我们在使用完之后还需要清楚他们。
获取焦点
- requestFocus
requestFocus方法,该方法用于请求将焦点设置到当前节点或其子节点上。
如果传递了一个FocusNode参数,则该方法将请求将焦点设置到该节点上。如果该节点不是当前节点或其子孙节点,则会抛出一个异常。如果该节点是当前节点或其子孙节点,则会将焦点设置到该节点上,并且如果需要,将其子节点中的第一个可聚焦的控件设置为焦点。
如果没有传递FocusNode参数,则该方法将请求将焦点设置到当前节点或其子孙节点中的第一个可聚焦的控件上。
- _doRequestFocus方法
它是requestFocus的内部实现,用于实际请求将焦点设置到当前节点或其子孙节点上。
如果当前节点不能请求焦点,则不会进行任何操作。
如果当前节点还没有被添加到焦点树中,则会在下次添加到树中时请求焦点。
如果当前节点已经是焦点节点,则不会进行任何操作。否则,它将请求将焦点设置到当前节点或其子孙节点中的第一个可聚焦的控件上,并将当前节点标记为下一个需要聚焦的控件。
void requestFocus([FocusNode? node]) {
if (node != null) {
if (node._parent == null) {
_reparent(node);
}
assert(node.ancestors.contains(this), 'Focus was requested for a node that is not a descendant of the scope from which it was requested.');
node._doRequestFocus(findFirstFocus: true);
return;
}
_doRequestFocus(findFirstFocus: true);
}
// Note that this is overridden in FocusScopeNode.
void _doRequestFocus({required bool findFirstFocus}) {
assert(findFirstFocus != null);
if (!canRequestFocus) {
assert(_focusDebug('Node NOT requesting focus because canRequestFocus is false: $this'));
return;
}
// If the node isn't part of the tree, then we just defer the focus request
// until the next time it is reparented, so that it's possible to focus
// newly added widgets.
if (_parent == null) {
_requestFocusWhenReparented = true;
return;
}
_setAsFocusedChildForScope();
if (hasPrimaryFocus && (_manager!._markedForFocus == null || _manager!._markedForFocus == this)) {
return;
}
_hasKeyboardToken = true;
assert(_focusDebug('Node requesting focus: $this'));
_markNextFocus(this);
}
散焦
FocusNode.unfocus()
用于告诉节点“放弃焦点”,虽然它确实从节点上移除了焦点,但实际上不存在“取消焦点”所有节点的情况。
如果一个节点没有焦点,那么它必须将焦点传递到其他地方,因为总是有一个主要焦点。当节点调用时接收焦点的节点 unfocus()
要么是最近的节点FocusScopeNode
,要么是该范围内先前聚焦的节点。
所以希望在从节点中删除焦点时更好地控制焦点的去向,请显式聚焦另一个节点而不是调用unfocus()
,
自动对焦
autofocus: true,
键盘控制焦点
可以使用RawKeyboardListener对键盘事件进行控制
RawKeyboardListener(
{Key? key,
required FocusNode focusNode,
bool autofocus = false,
bool includeSemantics = true,
ValueChanged? onKey,
required Widget child}
)
- autofocus:如果该小部件在其范围内当前没有其他节点获得焦点时被选为初始焦点,则此autofocus属性将为 true。理想情况下,每个FocusScope中只有一个设置了自动对焦的小部件。如果有多个小部件设置了自动焦点,则添加到树中的第一个小部件将获得焦点。
- focusNode:此focusNode属性控制focusNode是否具有键盘焦点。FocusNode是形成焦点树的持久对象,焦点树表示层次结构中对焦点感兴趣的小部件。如果焦点节点是从Focus Widget的祖先传入的,以控制祖先子级的焦点,则可能需要创建焦点节点;或者,如果未使用 widget 子系统,或者如果Focus和FocusScope Widget提供的控制不足。
- bool includeSemantics:此includeSemantics属性包含此小部件中的语义信息。如果为 true,则此小部件将包含一个 Semantics 节点,该节点指示 Semantics.focusable 和 Semantics.focused 属性。通常不将此设置为 false,因为这可能会影响可访问性系统可用的语义信息。
默认值为 true。
- onKey:每当此小部件收到原始键盘事件时就会调用它。
- child:孩子只能有一个孩子。要布局多个子项,请让此小部件的子项是一个小部件,例如Row Widget、Column Widget或Stack Widget,它们具有 Children 的属性,然后将子项提供给该小部件。