Flutter源码解析-TextField(2) -FocusNode焦点

说明

本文源码基于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();

总结

整个焦点体系是一个树,管理获取焦点的是一个列表
根节点是唯一的

你可能感兴趣的:(Flutter源码解析-TextField(2) -FocusNode焦点)