颤振中的多向滚动

时间:2018-12-18 12:27:38

标签: flutter flutter-layout

我来自Web开发背景,习惯于创建同时具有x和y溢出的元素(允许向任何方向滚动)。我正在努力实现与Flutter相同的功能。

在文档中,我找到了SingleChildScrollView,但它仅允许Axis.horizo​​ntal或Axis.vertical,而不能同时使用这两种方法。

所以我尝试了以下方法:

{{1}}

这同时适用于x和y,但不允许 对角线 滚动。

有没有一种方法可以实现对角线滚动,或者我完全缺少更好的材质小部件?

谢谢

3 个答案:

答案 0 :(得分:0)

听起来像是条子的情况。 这个similar question的答案可能会对您有所帮助。

答案 1 :(得分:0)

我设法找到了一个解决方案,尽管它并不完美:

我创建了一个Offset _scrollOffset的StatefulWidget,该StatefulWidget使用ClipRect和一个Transform类型的子代。变换矩阵(Matrix4.identity()..translate(_offset.dx, _offset.dy))应用于变换。

GestureDetector分配了一个onPanUpdate回调以更新滚动位置。 _scrollOffset += e.delta。只要将滚动位置设置得太低或太高,就可以将其限制在小部件的边界上。

Animation和AnimationController用于设置击打速度。 onPanEnd提供了最后一个声相的速度,因此只需执行基于该速度的弹射补间即可。

动画在onTapDown上停止,因此用户可以停止滚动速度。

主要问题是,它并不能完美地模仿Android或iOS的滚动速度,尽管我正在努力尝试使用Flutter提供的ScrollSimulation类使其更好地工作。

答案 2 :(得分:0)

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

class FreeScrollView extends StatefulWidget {
  final Widget child;
  final ScrollPhysics physics;

  const FreeScrollView({Key? key, this.physics = const ClampingScrollPhysics(), required this.child}) : super(key: key);

  @override
  State<FreeScrollView> createState() => _FreeScrollViewState();
}

class _FreeScrollViewState extends State<FreeScrollView> {
  final ScrollController _verticalController = ScrollController();
  final ScrollController _horizontalController = ScrollController();
  final Map<Type, GestureRecognizerFactory> _gestureRecognizers = <Type, GestureRecognizerFactory>{};

  @override
  void initState() {
    super.initState();
    _gestureRecognizers[PanGestureRecognizer] = GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>(
        () => PanGestureRecognizer(),
        (instance) => instance
          ..onDown = _handleDragDown
          ..onStart = _handleDragStart
          ..onUpdate = _handleDragUpdate
          ..onEnd = _handleDragEnd
          ..onCancel = _handleDragCancel
          ..minFlingDistance = widget.physics.minFlingDistance
          ..minFlingVelocity = widget.physics.minFlingVelocity
          ..maxFlingVelocity = widget.physics.maxFlingVelocity
          ..velocityTrackerBuilder = ScrollConfiguration.of(context).velocityTrackerBuilder(context)
          ..dragStartBehavior = DragStartBehavior.start);
  }

  @override
  Widget build(BuildContext context) => Stack(children: [
        SingleChildScrollView(
            scrollDirection: Axis.horizontal,
            controller: _horizontalController,
            physics: widget.physics,
            child: SingleChildScrollView(
                scrollDirection: Axis.vertical, // ignore: avoid_redundant_argument_values
                controller: _verticalController,
                physics: widget.physics,
                child: widget.child)),
        Positioned.fill(
            child: RawGestureDetector(
          gestures: _gestureRecognizers,
          behavior: HitTestBehavior.opaque,
          excludeFromSemantics: true,
        )),
      ]);

  Drag? _horizontalDrag;
  Drag? _verticalDrag;
  ScrollHoldController? _horizontalHold;
  ScrollHoldController? _verticalHold;

  void _handleDragDown(DragDownDetails details) {
    _horizontalHold = _horizontalController.position.hold(() => _horizontalHold = null);
    _verticalHold = _verticalController.position.hold(() => _verticalHold = null);
  }

  void _handleDragStart(DragStartDetails details) {
    _horizontalDrag = _horizontalController.position.drag(details, () => _horizontalDrag = null);
    _verticalDrag = _verticalController.position.drag(details, () => _verticalDrag = null);
  }

  void _handleDragUpdate(DragUpdateDetails details) {
    _horizontalDrag?.update(DragUpdateDetails(
        sourceTimeStamp: details.sourceTimeStamp,
        delta: Offset(details.delta.dx, 0),
        primaryDelta: details.delta.dx,
        globalPosition: details.globalPosition));
    _verticalDrag?.update(DragUpdateDetails(
        sourceTimeStamp: details.sourceTimeStamp,
        delta: Offset(0, details.delta.dy),
        primaryDelta: details.delta.dy,
        globalPosition: details.globalPosition));
  }

  void _handleDragEnd(DragEndDetails details) {
    _horizontalDrag
        ?.end(DragEndDetails(velocity: details.velocity, primaryVelocity: details.velocity.pixelsPerSecond.dx));
    _verticalDrag
        ?.end(DragEndDetails(velocity: details.velocity, primaryVelocity: details.velocity.pixelsPerSecond.dy));
  }

  void _handleDragCancel() {
    _horizontalHold?.cancel();
    _horizontalDrag?.cancel();
    _verticalHold?.cancel();
    _verticalDrag?.cancel();
  }
}