【问题标题】:Best way to allow a bit of overscroll in a CustomScrollView在 CustomScrollView 中允许过度滚动的最佳方法
【发布时间】:2021-06-06 01:33:59
【问题描述】:

我制作的 UI 通常从底部条一直滚动开始,因此它的顶部位于视图的顶部。而且:

  • 顶部需要一个额外的空白空间,以防用户想要将内容拉下以便他们可以在不将手从手机底部移动的情况下触及它(这是一个非常基本的人体工程学功能,我认为我们应该期望它很快就会变得很普遍,首先是应用程序将更多的关键功能移动到屏幕底部,例如,Firefox 的 url 栏。)(目前,我正在使用 appBar 条子,但我可以想象一个不使用它的完整解决方案)

  • 底部可能需要额外的空白空间,只要底部条子中的内容不够长,无法一直滚动。否则它会显得有问题和不规则。理想情况下,我会强加一个 minHeight 以使底部的条子始终至少与屏幕一样高,但它是条子,所以我很确定这是不可能的/丑陋的困难。

我现在正在考虑的途径是 ScrollPhysics 包装器,它修改了 ScrollMetrics 以便 maxExtentminExtent 更大。据我所知,这将允许CustomScrollView(鉴于此ScrollPhysics)过度滚动。不过感觉有点乱很高兴首先知道是什么决定了 maxExtentminExtent 并改变它。

【问题讨论】:

    标签: flutter flutter-layout


    【解决方案1】:

    由于缺乏更好的选择,我继续计划,并制作了我自己的自定义 ScrollPhysics 类,该类允许过度滚动给定的数量,extra

        return CustomScrollView(
            physics: _ExtraScrollPhysics(extra: 100 * MediaQuery.of(context).devicePixelRatio),
            ...
    

    _ExtraScrollPhysics 基本上只是一个扩展的AlwaysScrollable,所有的方法都将 ScrollMetrics 重载以将其内容复制到一个 ScrollMetric 中,而 minScrollExtent 已被 -extra 减少,然后将其传递给超类的方法版本。事实证明,我描述的用例不需要调整 maxScrollExtent 字段!

    这有一个缺点,顶部的过度滚动发光指示器出现在内容的顶部,而不是滚动视图的顶部,这看起来很糟糕。 It looks like this might be fixable,但我更喜欢这不是问题的方法。

    【讨论】:

      【解决方案2】:

      mako's solution 是一个很好的起点,但它不适用于鼠标滚轮滚动,只包括顶部的过度滚动,并没有实现发光指示器问题的解决方案。

      更通用的解决方案

      对于网络,使用Listener 检测PointerSignalEvents,并使用ScrollController 手动滚动列表。

      对于移动设备,不需要监听事件。

      按照 mako 的建议扩展 ScrollPhysics 类,但在 web 上使用 NeverScrollableScrollPhysics 以防止物理干扰手动滚动。要解决移动设备的发光指示器问题,请将您的 CustomScrollView 包裹在 ScrollConfiguration 中,由 nioncode 提供。

      GestureBinding.instance.pointerSignalResolver.register 用于防止滚动事件向上传播小部件树。

      例子

      import 'package:flutter/foundation.dart';
      import 'package:flutter/gestures.dart';
      import 'package:flutter/material.dart';
      import 'package:my_project/custom_glowing_overscroll_indicator.dart';
      import 'package:my_project/overscroll_physics.dart';
      
      class OverscrollList extends StatelessWidget {
      
        final ScrollController _scrollCtrl = ScrollController();
        final double _topOverscroll = 200;
        final double _bottomOverscroll = 200;
      
        void _scrollList(Offset offset) {
          _scrollCtrl.jumpTo(
            _scrollCtrl.offset + offset.dy,
          );
        }
      
        @override
        Widget build(BuildContext context) {
          return Container(
            height: 300,
            decoration: BoxDecoration(border: Border.all(width: 1)),
            child: Listener(
              onPointerSignal: (PointerSignalEvent event) {
                if (kIsWeb) {
                  GestureBinding.instance.pointerSignalResolver.register(event, (event) {
                    _scrollList((event as PointerScrollEvent).scrollDelta);
                  });
                }
              },
              child: ScrollConfiguration(
                behavior: OffsetOverscrollBehavior(
                  leadingPaintOffset: -_topOverscroll,
                  trailingPaintOffset: -_bottomOverscroll,
                ),
                child: CustomScrollView(
                  controller: _scrollCtrl,
                  physics: kIsWeb
                      ? NeverScrollableOverscrollPhysics(
                          overscrollStart: _topOverscroll,
                          overscrollEnd: _bottomOverscroll,
                        )
                      : AlwaysScrollableOverscrollPhysics(
                          overscrollStart: _topOverscroll,
                          overscrollEnd: _bottomOverscroll,
                        ),
                  slivers: [
                    SliverToBoxAdapter(
                      child: Container(width: 400, height: 100, color: Colors.blue),
                    ),
                    SliverToBoxAdapter(
                      child: Container(width: 400, height: 100, color: Colors.yellow),
                    ),
                    SliverToBoxAdapter(
                      child: Container(width: 400, height: 100, color: Colors.red),
                    ),
                    SliverToBoxAdapter(
                      child: Container(width: 400, height: 100, color: Colors.orange),
                    ),
                  ],
                ),
              ),
            ),
          );
        }
      }
      
      

      dartpad demo

      手机搜索结果:

      【讨论】:

        猜你喜欢
        • 2020-03-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-25
        • 2013-10-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多