美文网首页
Flutter-Focus树

Flutter-Focus树

作者: Magic旭 | 来源:发表于2020-04-22 16:37 被阅读0次

Focus(焦点)树

  1. 作为android开发者都知道,输入框乃是需要获取焦点才可以输入的。在flutter也不例外,Flutter View是以树的形式来构建的,那么Focus其实也是以树形结构来构建的。其中FocusScopeNode是根节点,拥有一组focus的父节点。那么下面就围绕flutter的焦点树来做个简单的介绍。
焦点API介绍
  1. FocusScopeNode:App的根节点(焦点树的根节点),拥有一组focus的父节点。FocusScopeNode的参数列表中包含:children(包括的全部焦点事件)、focusedChildren(请求焦点的焦点事件)等属性。
  2. FocusScope:作为一个Widget的焦点节点(父节点),管理该Widget下的所有children的焦点节点(子节点)。FocusScope属于1中的子焦点。
  3. Focus:创建Widget,作为FocusScope的子焦点,提供给自身child焦点。
  4. FocusNode:焦点自身事件,也是输入框组件需要的焦点事件。FocusNode构造函数中有debugLabel等参数,其中可以通过debugLabel在焦点树中找到对应的焦点事件。
    温馨提示:感兴趣可以去看focus_manager.dart类源码,给点耐心和时间看就可以了。
代码示例
//官网demo(解释FocusScope与Focus的用法)
@override
 Widget build(BuildContext context) {
    //创建FocusScope,自己想管理child之间焦点抢占逻辑,并且依附在根节点上(FocusScopeNode)
   return FocusScope(
     debugLabel: 'Scope',
     autofocus: true,
     child: DefaultTextStyle(
       //提供给child一个焦点事件。
       child: Focus(
         debugLabel: 'Button',
         child: Builder(
           builder: (BuildContext context) {
            //获取附近的Focus的焦点事件
            final FocusNode focusNode = Focus.of(context);
             final bool hasFocus = focusNode.hasFocus;
             return GestureDetector(
               onTap: () {
                 if (hasFocus) {
                    //取消焦点获取
                   focusNode.unfocus();
                 } else {
                    //当前Build Widget获取焦点。
                   focusNode.requestFocus();
                 }
               },
               child: *******,
             );
           },
         ),
       ),
    ),
   );
 }

//小白用法demo
///创建一个焦点事件,最好带上debugLabel,要不然找的时候一堆focus,不知道哪个Focus是哪个页面的,变成野指针类似了。
final FocusNode _verifyFocus = FocusNode(debugLabel: "verify");
///告诉焦点树的根节点,当前的_verifyFocus需要焦点,把焦点夺过来。
FocusScope.of(focusContext()).requestFocus(_verifyFocus);
///当Widget从树从被干掉后,也要记得把focus从焦点树中移除。(后续会补充FocusAttachment与FocusManager的讲解)
@override
  void dispose() {
    _verifyFocus.dispose();
    super.dispose();
  }

如何拥有自己的FocusScopeNode

方法一:

  1. 在创建FocusScope的时候就指定一个node,后续指定可以用这个FocusScopeNode来添加想要请求焦点的FocusNode。

方法二(麻烦点):

  1. 当你想拥有一个自己的FocusScopeNode的话,必须一同使用FocusScope + Builder,Builder组件应该是用来切换上下文,形成一个独立的context。因为在widget的build中的context都是根节点的context,如果你想自己的widget拥有一组独立的FocusScopeNode,就必须配合Builder一同使用。

  2. 当你拥有了自己的FocusScopeNode后,该widget所有焦点逻辑都应该有该FocusScopeNode来管理。而不是通过根节点的FocusScopeNode。

3.代码演示如下:

///方法一:创建FocusScope时候自定focusScopeNode,后续的FocusNode直接可以用
final FocusScopeNode _phoneFocusScope = FocusScopeNode(debugLabel: "phone_widgets");
@override
  Widget build(BuildContext context) {
    return FocusScope(
      autofocus: true,
      node: _phoneFocusScope,
      child:xxxxxxxx
)
//请求焦点时候,因为当前widget下的全部焦点在_phoneFocusScope下了,所以直接request就可以了。
_verifyFocus.requestFocus(); // or _phoneFocusScope.requestFocus(_verifyFocus); 

///方法二 : 不指定的话内部会自行生成一个
FocusScopeNode _phoneFocusScope;
return FocusScope(
      autofocus: true,
      child: Builder(builder: (BuildContext context){
      //这里假如不加入Builder,直接用FocusScope.of(context)是获取根节点的FocusScopeNode
      _phoneFocusScope = FocusScope.of(context);
      return xxxxx;  
    }
FocusScope.of与Focus.of区别
  1. FocusScope.of(context),通过context寻找树中最近的FocusScope。
  2. Focus.of(context),通过context寻找树中最近的focus。

如何查找对应的FocusNode。

所有的焦点的创建都已attach到根节点(FocusScopeNode)上,所以当我们要在某些场景下通过接口回调操作某个焦点事件时,例如TabView的切换我想把上个页面的焦点事件给取消。

普通FocusNode的形式
  1. 通过FocusScope.of(context)拿到整个焦点树的根节点,然后通过children去遍历所需要寻找的焦点。代码示例如下:
//这里的num是上个页面的焦点事件,当index == 1的时候就是切到下个页面了。应该把软键盘给收起来。所以要执行unfocus。
 _tabController.addListener(() {
      _autoRequestFocus();
    });

_autoRequestFocus() {
    FocusScope.of(context).children.where((FocusNode node) => node.debugLabel == "num").forEach((FocusNode node) {
      if (node.debugLabel == "num") {
        if (_tabController.index == 1) {
          //整个组的焦点都放弃掉,源码里面会给child
          node.unfocus();
        } else {
          FocusScope.of(context).requestFocus(node);
        }
      } else {
        node.unfocus();
      }
    });
}
FocusScope的形式
//创建FocusScopeNode,自己管理当前widget下xxxx布局的全部焦点
final FocusScopeNode _phoneFocusScope = FocusScopeNode(debugLabel: "phone_widgets");
return FocusScope(
      autofocus: true,
      node: _phoneFocusScope,
      child: xxxxx
)
//当widget不可见后,把抢占焦点行为给放弃
_tabController.addListener(() {
      _autoRequestFocus();
    });
_autoRequestFocus() {
    FocusScope.of(context).children.where((FocusNode node) => node.debugLabel == "phone_widgets").forEach((FocusNode node) {
      if (node.debugLabel == "phone_widgets") {
        if (_tabController.index == 1) {
          //里面会把child给remove掉,及全部child都失去焦点
          if(node.hasFocus) node.unfocus();
        } else {
          //该焦点组获取到焦点,至于焦点落在那个View上,看哪个焦点事件去请求。
          if(!node.hasFocus) FocusScope.of(context).requestFocus(node);
        }
      } else {
        node.unfocus();
      }
    });
  }
顺带知识点
  1. TabView中如果某个widget还存在焦点情况下,页切换至不可见也不会走dispose,应该类似与java的强引用一样,焦点树的根节点是一个强引用,不解除依赖关系页面不会被清除。

总结

  1. 上述都属于个人对源码解读+实践的总结,如有出错点欢迎指出。
  2. flutter还是多看看源码,源码入手比较简单。

相关文章

  • Flutter-Focus树

    Focus(焦点)树 作为android开发者都知道,输入框乃是需要获取焦点才可以输入的。在flutter也不例外...

  • 水彩过程 | 树树树树

    练习了一下树的画法,用毛笔勾树干,扇形笔画树叶还是又快又方便的。因为不是写实风格,只要把树的意象画出来就可以,所以...

  • 树·树

    ​如果有来生,要做一棵树,站成永恒,没有悲欢姿势,一半在尘土里安详。一半在风里飞扬,一半洒落阴凉,一半沐浴阳光。 ...

  • 树,树……

    树,树…… ——洛尔迦 树,树, 枯了又绿。 脸蛋美丽的姑娘 在那里摘橄榄。 风,塔楼上的求爱者, 拦腰把她...

  • 橄榄树树树

    在建班级群的时候,我顺手打了三个树——橄榄树树树。是的,这是橄榄树第三次起航。 第一次,在北京,我说,我愿意在无人...

  • 树,与树

    (第一次学着简书里文友看图写诗,2020的图,各位讲究着看吧) 文/三少爷的糖 一颗树站在山头 遥望着远方,朦胧中...

  • 树,与树

    我不是一个很喜欢女生哭闹的人。 哭闹,意味着理智被情感摧毁。 理智没了,沟通渠道也就关闭了。 没有了沟通,剩下的就...

  • 树和树

    我的家门前有一棵柏树,不是什么稀罕的树,但它却挺直腰杆儿,坚定的伫立在我家门前,望着远方,似乎在等什么人又不像在等...

  • 树树秋声

    余秋雨说:生命,是一树花开,或安静或热烈,或寂寞或璀璨。日子,就在岁月的年轮中渐次厚重,那些天真的、跃动的、抑或沉...

  • 短篇‖树树

    这是一条幽静的古道,两旁尽是残垣断壁,竟也有一些台阶通向几栋还算有顶篷的石质的建筑物。我和我的伙伴着级上了一段...

网友评论

      本文标题:Flutter-Focus树

      本文链接:https://www.haomeiwen.com/subject/gxwbihtx.html