ListView-仅允许双手指滚动并禁用单手指滚动Flutter

问题描述

我正在使用Flutter ListView,并且当用户使用两根手指时,需要让用户滚动 。如果用户仅使用一根手指,则应该禁用滚动。

我不知道如何实现此目的://我已经尝试过:我试图对ScrollPhysics进行子类化,并重写shouldAcceptUserOffset或applyPhysicsToUserOffset,但是没有一个可行。我还尝试过通读文档,但没有发现任何相关内容

非常感谢您的任何建议!

解决方法

深入研究Flutter源代码(虽然很多代码,但是看起来很漂亮),我终于找到了一个hack。这对于我的iOS模拟器和Android手机非常有效。

备注:

  • 如果您还想用一根手指滚动,而只想修复this bug(用两根手指滚动,滚动速度快两倍),则将onlyScrollableWhenMultiFinger: true中的MultiFingerListViewController设置为下面的示例。

使用步骤:

  1. MultiFingerListViewParent添加为列表视图的父项。
  2. 设置ListView的physics

示例:

class TestDoubleFingerScrollV2 extends StatefulWidget {
  @override
  _TestDoubleFingerScrollV2State createState() => _TestDoubleFingerScrollV2State();
}

class _TestDoubleFingerScrollV2State extends State<TestDoubleFingerScrollV2> {
  final controller = MultiFingerListViewController(onlyScrollableWhenMultiFinger: true);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text('TestDoubleFingerScrollV2')),body: MultiFingerListViewParent(
          controller: controller,child: ListView.builder(
              physics: MultiFingerScrollPhysics(controller: controller),itemCount: 100,itemBuilder: (context,index) =>
                  Container(height: 60,color: Colors.green.withAlpha((index * 50) % 255),child: Text('$index'))),));
  }
}

来源:

import 'package:flutter/material.dart';
import 'package:flutter_proj/components/finger_aware_listener.dart';
import 'package:flutter_proj/components/mux_listener.dart';

class MultiFingerListViewController {
  final bool onlyScrollableWhenMultiFinger;
  final _fingerAwareListener = FingerAwareListener();

  bool _lastShouldScrollable = false;

  MultiFingerListViewController({@required this.onlyScrollableWhenMultiFinger});

  bool _updateShouldScrollable() {
    _lastShouldScrollable =
        (!onlyScrollableWhenMultiFinger) || _fingerAwareListener.info.isCurrentRunMultiFingerUpToNow;
    return _lastShouldScrollable;
  }
}

class MultiFingerListViewParent extends StatelessWidget {
  final MultiFingerListViewController controller;
  final Widget child;

  const MultiFingerListViewParent({Key key,this.child,this.controller}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MuxListener(
      onPointer: controller._fingerAwareListener.handlePointer,child: child,);
  }
}

// This sub-class should have some boilerplate code,e.g. look at [NeverScrollableScrollPhysics] to see it
/// NOTE (WARN): This is a HACK,VIOLATING what the comments said for `applyPhysicsToUserOffset`. But works well for me.
class MultiFingerScrollPhysics extends ScrollPhysics {
  final MultiFingerListViewController controller;

  const MultiFingerScrollPhysics({ScrollPhysics parent,@required this.controller}) : super(parent: parent);

  @override
  MultiFingerScrollPhysics applyTo(ScrollPhysics ancestor) {
    return MultiFingerScrollPhysics(controller: controller,parent: buildParent(ancestor));
  }

  /// NOTE This **HACK** is actually **VIOLATING** what the comment says!
  /// The comment in [ScrollPhysics.applyPhysicsToUserOffset] says:
  /// "This method must not adjust parts of the offset that are entirely within
  ///  the bounds described by the given `position`."
  /// In addition,when looking at [BouncingScrollPhysics.applyPhysicsToUserOffset],/// we see they directly return the original `offset` when `!position.outOfRange`
  double applyPhysicsToUserOffset(ScrollMetrics position,double offset) {
    if (controller._updateShouldScrollable()) {
      // When k fingers are dragging,the speed is actually *k* times the normal speed. So we divide it by k.
      // (see https://github.com/flutter/flutter/issues/11884)
      final currNumFinger = controller._fingerAwareListener.info.currentRunSeenPointers.length;
      return offset / currNumFinger;
    } else {
      return 0.0;
    }
  }

  @override
  Simulation createBallisticSimulation(ScrollMetrics position,double velocity) {
    // When this method is called,the fingers seem to all *have left* the screen. Thus we cannot calculate the
    // current information,but should use the previous cache.
    if (controller._lastShouldScrollable) {
      return super.createBallisticSimulation(position,velocity);
    } else {
      return null;
    }
  }
}

这取决于其他几个小组件(非常简短的代码),我把它们放在here上。

希望有帮助!如果您有更好的解决方案,请告诉我:)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...