Focus相关
备注:文中忽略了部分逻辑的,该文只是为了理解focus和unfocus过程,及怎么显示键盘和收起键盘的
focus.pngEditableText
Focus相关的类都是配合EditableText使用的
///EditableTextState class
bool get _hasFocus => widget.focusNode.hasFocus;
由上面的流程图可以知道,EditableTextState initState的时候会监听widget.focusNode
,当接收到通知的时候最终会调用_openOrCloseInputConnectionIfNeeded
方法。在_openOrCloseInputConnectionIfNeeded
方法内会判断_hasFocus
,然后调用_openInputConnection()
或_closeInputConnectionIfNeeded()
-
_openInputConnection()
会调用TextInputConnect的show()
,然后调用TextInput的_show
。最终会通过channel调起原生代码显示键盘 -
_closeInputConnectionIfNeeded()
原理类似,最终会通过channel调起原生代码收起键盘
Focus相关
widget
-
class Focus extends StatefulWidget
,node对应的是FocusNode。一个小部件,它管理[FocusNode]以允许将键盘焦点赋予该小部件及其后代。 -
class FocusScope extends Focus
,node对应的是FocusScopeNode。FocusScope与Focus类似,但也可以作为其他Focus和FocusScope的边界将它们分组在一起。
两者的state的build方法中都会调用_focusAttachment.reparent();
以将FocusNode和前面的node树连接起来。同时,两者的state的build方法中都使用到class _FocusMarker extends InheritedNotifier<FocusNode>
Focus和FocusScope的InheritedWidget标记。
reparent
由上面流程图可知,在EditableTextState的build方法中会调用FocusAttachment的reparent方法,将最近_FocusMarker中的FocusNode或者BuildOwner中focusManager的rootScope设置为node的parent,则将parent赋值给node._parent,并将node添加到parent的_children数组中。
由以上流程图可知,调用this.requestFocus(focusNode)
方法,会将this设置为focusNode的parent。
requestFocus->_doRequestFocus
由流程图可知,对于FocusNode和FocusScopeNode,调用requestFocus方法,最终是分别调用各自的_doRequestFocus方法。_doRequestFocus方法中各自的逻辑如下:
-
FocusNode :调用_setAsFocusdChild方法,该方法的效果就如下图琐事,从图一变化到图二:
图1
图2.png
总结起来就是将this层级上的所有FocusScopeNode分别放置到各自上一级FocusScopeNode的_focusedChildren数组中最后面。则设置为focusedChild值
///FocusScopeNode class
FocusNode get focusedChild {
assert(_focusedChildren.isEmpty || _focusedChildren.last.enclosingScope == this, 'Focused child does not have the same idea of its enclosing scope as the scope does.');
return _focusedChildren.isNotEmpty ? _focusedChildren.last : null;
}
同时会调用_markAsDirty方法,在该方法内调node的_notify方法或者调_manager的_markNeedsUpdate
- FocusScopeNode:查找最近的非FocusScopeNode类型的focusedChild(应该就是FocusNode类型)调用_doRequestFocus方法,如果没有则尝试聚焦最后一个FocusScopeNode
FocusManager
///当前focus的node
FocusNode get primaryFocus => _primaryFocus;
FocusNode _primaryFocus;
///请求focus,但是还没有focus的node
FocusNode _nextFocus;
由以上流程图可知,当focus的时候,会先将请求focus的node传递给_nextFocus,让后在调用node._notify()之前将_nextFocus赋值给_primaryFocus,并将_nextFocus置空。
当unfocus的时候,会将_primaryFocus和_nextFocus都置空,同时还会调用node._notify()