说明
本文源码基于flutter 1.7.8
承接上一篇,这次着重来分析FocusNode,来看看焦点是怎么管理的
使用
/// 创建
final FocusNode focusNode = FocusNode();
/// 获得焦点
FocusScope.of(context).requestFocus(focusNode);
focusNode.requestFocus(); //直接使用这个会报错的
/// 失去焦点
focusNode.unfocus();
/// 判断是否获取到焦点
focusNode.hasFocus;
分析
先从结果来看,分析hasFocus这个方法
bool get hasPrimaryFocus => _manager?.primaryFocus == this;
bool get hasFocus {
if (_manager?.primaryFocus == null) {
return false;
}
if (hasPrimaryFocus) {
return true;
}
return _manager.primaryFocus.ancestors.contains(this);
}
这里有2个不太清楚,_manager和primaryFocus。
没关系,先记住这2个,然后来分析 FocusScope.of(context).requestFocus(focusNode)
获取焦点
//这个其实就是想获取一个最近的FocusScopeNode,不存在则返回根FocusScopeNode
static FocusScopeNode of(BuildContext context) {
final _FocusMarker marker = context.inheritFromWidgetOfExactType(_FocusMarker);
return marker?.notifier?.nearestScope ?? context.owner.focusManager.rootScope;
}
我们直接分析rootScope,其他的也和他相似
1. rootScope创建流程
在runApp的过程中会调用一下代码:
//创建
BuildOwner get buildOwner => _buildOwner;
final BuildOwner _buildOwner = BuildOwner();
//使用
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement);
}
BuildOwner内部会创建一个focusManager,顾名思义,焦点管理
class BuildOwner {
FocusManager focusManager = FocusManager();
...
}
在这个焦点管理的类里就有创建 rootScope对象
class FocusManager with DiagnosticableTreeMixin {
//给根节点的_manager赋值
FocusManager() {
rootScope._manager = this;
RawKeyboard.instance.addListener(_handleRawKeyEvent);
}
//根节点
final FocusScopeNode rootScope = FocusScopeNode(debugLabel: 'Root Focus Scope');
...
}
2. _manager的赋值
回调之前,咱们再来看requestFocus方法,后面的分析都以当前是rootScope为例子
void requestFocus([FocusNode node]) {
if (node != null) {
if (node._parent == null) {
//一个刚创建的空白node,需要去建立各个关系
_reparent(node);
}
//执行请求焦点操作
node._doRequestFocus();
return;
}
//未建立关系的node直接执行请求焦点操作会报错
_doRequestFocus();
}
_reparent里做了什么?
@mustCallSuper
void _reparent(FocusNode child) {
if (child._parent == this) {
return;
}
//node的祖先,第一个是FocusScopeNode的节点
final FocusScopeNode oldScope = child.enclosingScope;
final bool hadFocus = child.hasFocus;
//以前的_parent存在的话则移出当前node,因为需要更换parent
child._parent?._removeChild(child);
// rootScope的_children列表中添加当前node
_children.add(child);
//建立_parent关系,node 的 _parent为rootScope
child._parent = this;
//node更新FocusManager对象,就是传递_manager对象
child._updateManager(_manager);
if (hadFocus) {
_manager?.primaryFocus?._setAsFocusedChild();
}
if (oldScope != null && child.context != null && child.enclosingScope != oldScope) {
DefaultFocusTraversal.of(child.context, nullOk: true)?.changedScope(node: child, oldScope: oldScope);
}
}
上面的模型有些类似树的结构,父节点可以有多个子节点,而子节点只有一个父节点
node对象接替了rootScope中的_manager
void _updateManager(FocusManager manager) {
//node的_manager赋值
_manager = manager;
for (FocusNode descendant in descendants) {
descendant._manager = manager;
}
}
通过上面的分析可以得出,所有的FocusNode的_manager当映射关系建立后,都是rootScope中的_manager。也即_manager是唯一的
3. 获取焦点
上面的requestFocus方法中执行了_doRequestFocus方法
void _doRequestFocus() {
_setAsFocusedChild();
if (hasPrimaryFocus) {
return;
}
_hasKeyboardToken = true;
_markAsDirty(newFocus: this);
}
建立获取焦点的孩子集合
void _setAsFocusedChild() {
//this就是上面的node对象
FocusNode scopeFocus = this;
//这里进行了一次父类遍历
for (FocusScopeNode ancestor in ancestors.whereType()) {
ancestor._focusedChildren.remove(scopeFocus);
//将node移至尾端
ancestor._focusedChildren.add(scopeFocus);
scopeFocus = ancestor;
}
}
这个遍历的效果是,最近的FocusScopeNode添加了node,而上一层的FocusScopeNode添加了最近的FocusScopeNode
你可能奇怪_focusedChildren有什么用?和上边的_children有什么区别?
FocusNode get focusedChild {
return _focusedChildren.isNotEmpty ? _focusedChildren.last : null;
}
_focusedChildren列表的最后一个就是当前获取焦点的节点
4. primaryFocus的赋值
继续看_doRequestFocus中的另一个方法_markAsDirty
void _markAsDirty({FocusNode newFocus}) {
if (_manager != null) {
//经过上面的步骤,_manager有值了
_manager._dirtyNodes?.add(this);
//node传递过来
_manager._markNeedsUpdate(newFocus: newFocus);
} else {
newFocus?._setAsFocusedChild();
newFocus?._notify();
if (newFocus != this) {
_notify();
}
}
}
void _markNeedsUpdate({FocusNode newFocus}) {
//_nextFocus为当前的node对象
_nextFocus = newFocus ?? _nextFocus;
if (_haveScheduledUpdate) {
return;
}
_haveScheduledUpdate = true;
scheduleMicrotask(_applyFocusChange);
}
scheduleMicrotask是一个异步调用,_applyFocusChange请求焦点改变
void _applyFocusChange() {
_haveScheduledUpdate = false;
final FocusNode previousFocus = _primaryFocus;
if (_primaryFocus == null && _nextFocus == null) {
_nextFocus = rootScope;
}
//当前_primaryFocus 为 null , _nextFocus为node对象
if (_nextFocus != null && _nextFocus != _primaryFocus) {
//_primaryFocus值为node对象
_primaryFocus = _nextFocus;
final Set previousPath = previousFocus?.ancestors?.toSet() ?? {};
final Set nextPath = _nextFocus.ancestors.toSet();
_dirtyNodes.addAll(nextPath.difference(previousPath));
_dirtyNodes.addAll(previousPath.difference(nextPath));
_nextFocus = null;
}
if (previousFocus != _primaryFocus) {
if (previousFocus != null) {
_dirtyNodes.add(previousFocus);
}
if (_primaryFocus != null) {
_dirtyNodes.add(_primaryFocus);
}
}
for (FocusNode node in _dirtyNodes) {
node._notify();
}
_dirtyNodes.clear();
}
分析到这获取焦点基本完成,再来看失去焦点
void unfocus() {
//当前情况下是满足这个条件的
if (hasPrimaryFocus) {
//scope即为rootScope,因为当前情况下没有其他的映射关系
final FocusScopeNode scope = enclosingScope;
//焦点列表中移除node
scope._focusedChildren.remove(this);
_manager?._willUnfocusNode(this);
return;
}
if (hasFocus) {
_manager.primaryFocus.unfocus();
}
}
同时_primaryFocus也需要置空
void _willUnfocusNode(FocusNode node) {
if (_primaryFocus == node) {
_primaryFocus = null;
_dirtyNodes.add(node);
_markNeedsUpdate();
}
if (_nextFocus == node) {
_nextFocus = null;
_dirtyNodes.add(node);
_markNeedsUpdate();
}
}
最后
我们发现TextField中获取焦点并不是用的FocusScope.of(context).requestFocus(focusNode);
@override
void initState() {
super.initState();
widget.controller.addListener(_didChangeTextEditingValue);
_focusAttachment = widget.focusNode.attach(context);
widget.focusNode.addListener(_handleFocusChanged);
...
}
然后在build方法中调用reparent方法
@override
Widget build(BuildContext context) {
_focusAttachment.reparent();
super.build(context);
...
}
focusAttachment.reparent方法中和之前的基本类似
void reparent({FocusNode parent}) {
if (isAttached) {
assert(_node.context != null);
parent ??= Focus.of(_node.context, nullOk: true);
parent ??= FocusScope.of(_node.context);
assert(parent != null);
//建立映射关系
parent._reparent(_node);
}
}
调用
//直接使用 _doRequestFocus()
widget.focusNode.requestFocus();
总结
整个焦点体系是一个树,管理获取焦点的是一个列表
根节点是唯一的