【问题标题】:Flutter scroll screen when pointer reaches edge当指针到达边缘时颤动滚动屏幕
【发布时间】:2021-07-05 04:07:03
【问题描述】:

我有一个包含可拖动项目的 GridView。当一个项目被拖到屏幕的顶部/底部时,我想在那个方向滚动 GridView。

目前我将每个可拖动项目都包装在一个 Listener 中,如下所示:

Listener(
      child: _wrap(widget.children[i], i),
      onPointerMove: (PointerMoveEvent event) {
        if (event.position.dy >= MediaQuery.of(context).size.height - 100) {
          // 120 is height of your draggable.
          widget.scrollController.animateTo(
            widget.scrollController.offset + 120,
            curve: Curves.easeOut,
            duration: const Duration(milliseconds: 200));
        }if (event.position.dy <= kToolbarHeight + MediaQueryData.fromWindow(window).padding.top + 100) {
          // 120 is height of your draggable.
          widget.scrollController.animateTo(
              widget.scrollController.offset - 120,
              curve: Curves.easeOut,
              duration: const Duration(milliseconds: 200));
        }
      }
    )

它可以工作,但滚动一点也不流畅,看起来有点迟钝。 我也需要它在网络上工作。

有人对此有更好的解决方案吗?

【问题讨论】:

  • 你能在 dartpad 中分享一个演示。直接检查问题会很有帮助。我知道的一件事通常是在网络结果为零的情况下返回的位置。

标签: flutter flutter-layout flutter-web flutter-gridview


【解决方案1】:

这就是我的解决方法。使用 TickerProviderStateMixin,您可以获得每帧调用一次回调的 Ticker,您可以在其中微调滚动偏移量以获得平滑滚动。我使用 Stack 将虚拟 DragTargets 添加到控制代码的列表区域的顶部和底部。我每个边缘使用两个,以允许不同的滚动速度。如果您想要更细粒度的控制,您可以使用 Listener 通过光标位置插入速度。

https://www.dartpad.dev/acb83fdbbbbb0fd765cd5afa414a8942

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(),
    body: Stack(
      children: [
        ListView.separated(
          controller: controller,
          itemCount: 50,
          itemBuilder: (context, index) {
            return buildLongPressDraggable(index);
          },
          separatorBuilder: (context, index) {
            return Divider();
          },
        ),
        Positioned(
            top: 0, left: 0, right: 0, height: 25, child: buildEdgeScroller(-10)),
        Positioned(
            top: 25, left: 0, right: 0, height: 25, child: buildEdgeScroller(-5)),
        Positioned(
            bottom: 25, left: 0, right: 0, height: 25, child: buildEdgeScroller(5)),
        Positioned(
            bottom: 0, left: 0, right: 0, height: 25, child: buildEdgeScroller(10)),
      ],
    ),
  );
}

Widget buildEdgeScroller(double offsetPerFrame) {
  return DragTarget<int>(
    builder: (context, candidateData, rejectedData) => Container(),
    onWillAccept: (data) {
      scrollTicker = this.createTicker((elapsed) {
        if (!controller.hasClients) {
          return;
        }
        final position = controller.position;
        if ((offsetPerFrame < 0 && position.pixels <= position.minScrollExtent) ||
            (offsetPerFrame > 0 && position.pixels >= position.maxScrollExtent)) {
          scrollTicker.stop();
          scrollTicker.dispose();
          scrollTicker = null;
        } else {
          controller.jumpTo(controller.offset + offsetPerFrame);
        }
      });
      scrollTicker.start();
      return false;
    },
    onLeave: (data) {
      scrollTicker?.stop();
      scrollTicker?.dispose();
      scrollTicker = null;
    },
  );
}

【讨论】:

  • 谢谢!我想过为此使用拖动目标,但不知道如何。我目前正在将我的应用程序迁移到空安全,但是一旦我完成了,我会尝试你的解决方案。我非常有信心它会正常工作:)
  • 像魅力一样工作!对于使用零安全的人,只需使用 Ticker? scrollTicker insted of Ticker scrollTicker 并在 buildEdgeScroller 方法中使用 scrollTicker!.stop()、scrollTicker!.dispose() 和 scrollTicker?.start()
猜你喜欢
  • 1970-01-01
  • 2011-07-01
  • 2013-08-04
  • 1970-01-01
  • 2011-09-08
  • 1970-01-01
  • 2012-06-07
  • 2012-12-14
  • 2020-02-10
相关资源
最近更新 更多