likes
comments
collection
share

UME: Widget信息获取

作者站长头像
站长
· 阅读数 30

昨天介绍了主要是通过RenderObject实现的,至于什么是RenderObject就不多介绍了可以去看下flutter的三棵树

UME: Widget信息获取

首先在WidgetInfoInspector中有一段获取点击事件的代码

void _inspectAt(Offset? position) {
  final List<RenderObject> selected =
      HitTest.hitTest(position, edgeHitMargin: 2.0);
  setState(() {
    selection.candidates = selected;
  });
}

然后我们看下HitTest类中实现

class HitTest {
  // all of RenderObjects of current point
  static List<RenderObject> hitTest(
    Offset? position, {
    double edgeHitMargin = 0.0,
  }) {
    final dynamic ignorePointer = rootKey.currentContext!.findRenderObject();
    final RenderObject userRender = ignorePointer.child;

    bool _hitTestHelper(
      List<RenderObject> hits,
      List<RenderObject> edgeHits,
      Offset? position,
      RenderObject object,
      Matrix4 transform,
    ) {
      bool hit = false;
      final Matrix4? inverse = Matrix4.tryInvert(transform);
      if (inverse == null) {
        return false;
      }
      final Offset localPosition =
          MatrixUtils.transformPoint(inverse, position!);

      final List<DiagnosticsNode> children = object.debugDescribeChildren();
      for (int i = children.length - 1; i >= 0; i -= 1) {
        final DiagnosticsNode diagnostics = children[i];
        assert(diagnostics != null);
        if (diagnostics.style == DiagnosticsTreeStyle.offstage ||
            diagnostics.value is! RenderObject) continue;
        final RenderObject child = diagnostics.value as RenderObject;
        final Rect? paintClip = object.describeApproximatePaintClip(child);
        if (paintClip != null && !paintClip.contains(localPosition)) continue;

        final Matrix4 childTransform = transform.clone();
        object.applyPaintTransform(child, childTransform);
        if (_hitTestHelper(hits, edgeHits, position, child, childTransform))
          hit = true;
      }

      final Rect bounds = object.semanticBounds;
      if (bounds.contains(localPosition)) {
        hit = true;
        if (!bounds.deflate(edgeHitMargin).contains(localPosition))
          edgeHits.add(object);
      }
      if (hit) hits.add(object);
      return hit;
    }

    final List<RenderObject> regularHits = <RenderObject>[];
    final List<RenderObject> edgeHits = <RenderObject>[];

    _hitTestHelper(regularHits, edgeHits, position, userRender,
        userRender.getTransformTo(null));
    double _area(RenderObject object) {
      final Size size = object.semanticBounds.size;
      return size.width * size.height;
    }

    regularHits
        .sort((RenderObject a, RenderObject b) => _area(a).compareTo(_area(b)));
    final Set<RenderObject> hits = Set<RenderObject>();
    hits.addAll(edgeHits);
    hits.addAll(regularHits);
    return hits.toList();
  }
}

通过rootKey.currentContext!.findRenderObject()获取到RenderObject, 然后通过_hitTestHelper方法做了一个递归把所有包含点击点的renderObject都加入到list并返回

这个时候我们返回到_inspectAt方法中,将list获取的所有元素赋值给了selection.candidates

InspectorSelection中写了candidates的set方法,将candidates中的第一个元素取了出来

set candidates(List<RenderObject> value) {
  _candidates = value;
  _index = 0;
  _computeCurrent();
}

void _computeCurrent() {
  if (_index < candidates.length) {
    _current = candidates[index];
    _currentElement = (_current?.debugCreator as DebugCreator?)?.element;
  } else {
    _current = null;
    _currentElement = null;
  }
}

万事俱备,目前已经拿到了当前点击的RenderObject,那么如何显示出来,在WidgetInfoInspector的build中添加了Overlay进行显示,并把刚才写好的selection传入

Widget build(BuildContext context) {
  List<Widget> children = <Widget>[];
  GestureDetector gesture = GestureDetector(
    onTap: _handleTap,
    onPanDown: _handlePanDown,
    onPanEnd: _handlePanEnd,
    behavior: HitTestBehavior.opaque,
    child: IgnorePointer(
        child: Container(
            width: MediaQuery.of(context).size.width,
            height: MediaQuery.of(context).size.height)),
  );
  children.add(gesture);
  children.add(InspectorOverlay(selection: selection));
  return Stack(children: children, textDirection: TextDirection.ltr);
}

在Overlay中有个_SelectionInfo类会将selection.current的file及line取出,加上selection.currentElement的组件类型及size,组成显示的信息

UME: Widget信息获取

转载自:https://juejin.cn/post/7025965713424662565
评论
请登录