• Viewport 源码解析


    class Viewport extends MultiChildRenderObjectWidget {
    Viewport({
    super.key,
    this.axisDirection = AxisDirection.down,
    this.crossAxisDirection,
    this.anchor = 0.0,
    required this.offset,
    this.center,
    this.cacheExtent,
    this.cacheExtentStyle = CacheExtentStyle.pixel,
    this.clipBehavior = Clip.hardEdge,
    List slivers = const [],
    })
    : assert(offset != null),
    assert(slivers != null),
    assert(center == null || slivers
    .where((Widget child) => child.key == center)
    .length == 1),
    assert(cacheExtentStyle != null),
    assert(cacheExtentStyle != CacheExtentStyle.viewport ||
    cacheExtent != null),
    assert(clipBehavior != null),
    super(children: slivers);

    ///滚动布局滚动的方向
    final AxisDirection axisDirection;

    ///item 在横轴上面的方向
    final AxisDirection? crossAxisDirection;

    ///用于指定滚动视图中的零滚动偏移的相对位置
    ///取值在[0.0-1.0]之间
    ///如果是 0.0 那么是在初始位置
    ///如果是 1.0 那么就是在结束位置
    final double anchor;

    ///窗口的偏移量
    final ViewportOffset offset;

    ///用于在自定义滚动视图(CustomScrollView)中指定一个子元素作为生长方向的"中心"点
    final Key? center;

    ///用于控制视图的缓存区域大小
    ///可用于在滚动方向上更多地缓存 item (用户不可见部分,即提前渲染缓存)
    ///但是如果设置过大容易造成新能损耗
    final double? cacheExtent;

    ///用于控制视口[Viewport] 的缓存区域[cacheExtent]的模式
    ///[CacheExtentStyle.pixel] 以像素模式
    ///[CacheExtentStyle.viewport】跟当前视图一样
    final CacheExtentStyle cacheExtentStyle;

    ///针对超出视图部分的裁切行为
    final Clip clipBehavior;

    ///获取交叉轴滚动方向
    ///通过交叉轴的方向来获取文本的展示方向
    ///
    /// 如果是[AxisDirection.up]或者[AxisDirection.down]
    /// 会根据[Directionality.of(context)]获取文本排列方向
    /// 最终来确定文本的展示
    static AxisDirection getDefaultCrossAxisDirection(BuildContext context,
    AxisDirection axisDirection) {
    assert(axisDirection != null);
    switch (axisDirection) {
    case AxisDirection.up:
    assert(debugCheckHasDirectionality(
    context,
    why: “to determine the cross-axis direction when the viewport has an ‘up’ axisDirection”,
    alternative: “Alternatively, consider specifying the ‘crossAxisDirection’ argument on the Viewport.”,
    ));
    return textDirectionToAxisDirection(Directionality.of(context));
    case AxisDirection.right:
    return AxisDirection.down;
    case AxisDirection.down:
    assert(debugCheckHasDirectionality(
    context,
    why: “to determine the cross-axis direction when the viewport has a ‘down’ axisDirection”,
    alternative: “Alternatively, consider specifying the ‘crossAxisDirection’ argument on the Viewport.”,
    ));
    return textDirectionToAxisDirection(Directionality.of(context));
    case AxisDirection.left:
    return AxisDirection.down;
    }
    }

    ///创建对应的 renderobject
    @override
    RenderViewport createRenderObject(BuildContext context) {
    return RenderViewport(
    axisDirection: axisDirection,
    crossAxisDirection: crossAxisDirection ??
    Viewport.getDefaultCrossAxisDirection(context, axisDirection),
    anchor: anchor,
    offset: offset,
    cacheExtent: cacheExtent,
    cacheExtentStyle: cacheExtentStyle,
    clipBehavior: clipBehavior,
    );
    }

    @override
    void updateRenderObject(BuildContext context, RenderViewport renderObject) {
    renderObject
    …axisDirection = axisDirection
    …crossAxisDirection = crossAxisDirection ??
    Viewport.getDefaultCrossAxisDirection(context, axisDirection)
    …anchor = anchor
    …offset = offset
    …cacheExtent = cacheExtent
    …cacheExtentStyle = cacheExtentStyle
    …clipBehavior = clipBehavior;
    }

    @override
    MultiChildRenderObjectElement createElement() => _ViewportElement(this);

    @override
    void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(EnumProperty(‘axisDirection’, axisDirection));
    properties.add(EnumProperty(
    ‘crossAxisDirection’, crossAxisDirection, defaultValue: null));
    properties.add(DoubleProperty(‘anchor’, anchor));
    properties.add(DiagnosticsProperty(‘offset’, offset));
    if (center != null) {
    properties.add(DiagnosticsProperty(‘center’, center));
    } else if (children.isNotEmpty && children.first.key != null) {
    properties.add(DiagnosticsProperty(
    ‘center’, children.first.key, tooltip: ‘implicit’));
    }
    properties.add(DiagnosticsProperty(‘cacheExtent’, cacheExtent));
    properties.add(DiagnosticsProperty(
    ‘cacheExtentStyle’, cacheExtentStyle));
    }
    }

    ///Viewport 对应的 Element
    class _ViewportElement extends MultiChildRenderObjectElement
    with NotifiableElementMixin, ViewportElementMixin {
    /// Creates an element that uses the given widget as its configuration.
    _ViewportElement(Viewport super.widget);

    ///当前是否正在 mount 或者更新
    bool _doingMountOrUpdate = false;

    ///center 的 key 对应的卡槽的 index
    int? _centerSlotIndex;

    @override
    RenderViewport get renderObject => super.renderObject as RenderViewport;

    @override
    void mount(Element? parent, Object? newSlot) {
    assert(!_doingMountOrUpdate);
    _doingMountOrUpdate = true;
    super.mount(parent, newSlot);
    _updateCenter();
    assert(_doingMountOrUpdate);
    _doingMountOrUpdate = false;
    }

    @override
    void update(MultiChildRenderObjectWidget newWidget) {
    assert(!_doingMountOrUpdate);
    _doingMountOrUpdate = true;
    super.update(newWidget);
    _updateCenter();
    assert(_doingMountOrUpdate);
    _doingMountOrUpdate = false;
    }

    ///更新 center 的 key 对应的数据
    void _updateCenter() {
    final Viewport viewport = widget as Viewport;
    if (viewport.center != null) {
    ///如果之前已经制定了 center 了
    int elementIndex = 0;

      ///遍历拿到 childrens 里面对应的 element
      ///把它对应的 renderobject 赋值给对应的 center key element
      ///并且记录它的下标为_centerSlotIndex
      for (final Element e in children) {
        if (e.widget.key == viewport.center) {
          renderObject.center = e.renderObject as RenderSliver?;
          break;
        }
        elementIndex++;
      }
      assert(elementIndex < children.length);
      _centerSlotIndex = elementIndex;
    } else if (children.isNotEmpty) {
      ///如果没有 centerkey 拿 children 的第一个 item 便是默认
      ///把它的 renderObject 赋值给当前的 renderObject.center
      ///并且记录它的下标为_centerSlotIndex为 0
      renderObject.center = children.first.renderObject as RenderSliver?;
      _centerSlotIndex = 0;
    } else {
      ///如果当前的 viewport 没有子元素
      ///那么清空 renderObject.center 以及 _centerSlotIndex
      renderObject.center = null;
      _centerSlotIndex = null;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    }

    ///在father 类[MultiChildRenderObjectElement]里面已经做了 insert 操作了
    ///[renderObject.insert(child, after: slot.value?.renderObject);]
    ///这里为什么要重新 for 循环遍历一遍
    ///是因为[slot]可能带有 center 属性
    ///而当列表数据更新的时候,[insertRenderObjectChild]必然会被调用
    ///这个时候需要重新赋值[renderObject.center]以便布局渲染为居中
    @override
    void insertRenderObjectChild(RenderObject child, IndexedSlot slot) {
    super.insertRenderObjectChild(child, slot);
    // Once [mount]/[update] are done, the renderObject.center will be updated
    // in [_updateCenter].
    if (!_doingMountOrUpdate && slot.index == _centerSlotIndex) {
    renderObject.center = child as RenderSliver?;
    }
    }

    ///移动
    @override
    void moveRenderObjectChild(RenderObject child, IndexedSlot oldSlot,
    IndexedSlot newSlot) {
    super.moveRenderObjectChild(child, oldSlot, newSlot);
    assert(_doingMountOrUpdate);
    }

    ///移除
    @override
    void removeRenderObjectChild(RenderObject child, Object? slot) {
    super.removeRenderObjectChild(child, slot);
    if (!_doingMountOrUpdate && renderObject.center == child) {
    renderObject.center = null;
    }
    }

    @override
    void debugVisitOnstageChildren(ElementVisitor visitor) {
    children.where((Element e) {
    final RenderSliver renderSliver = e.renderObject! as RenderSliver;
    return renderSliver.geometry!.visible;
    }).forEach(visitor);
    }
    }

  • 相关阅读:
    目标检测YOLO实战应用案例100讲-基于改进YOLOv5s的道路目标检测
    机器学习 - 决策树:技术全解与案例实战
    python函数进阶
    简单实现文字滚动效果-CSS版本
    COSCon'23 开源市集:共赴一场草坪上的开源派对
    Python AI 在几秒钟内为我生成了这些 Python 应用程序——它们有用吗?
    SparkSql读取外部Hql文件的公共类开发
    跟着 Guava 学 Java 之 集合工具类
    科学计算三维可视化笔记(第五周 交互界面)
    final 在 java 中有什么作用?
  • 原文地址:https://blog.csdn.net/weixin_28717693/article/details/132909253