颤动中的多向滚动
Posted
技术标签:
【中文标题】颤动中的多向滚动【英文标题】:Multidirectional scroll in flutter 【发布时间】:2019-05-18 20:52:03 【问题描述】:我来自 web 开发背景,并且习惯于创建一个同时具有 x 和 y 溢出的元素(允许向任何方向滚动)。我正在努力使用 Flutter 实现相同的功能。
查看文档,我找到了 SingleChildScrollView,但它只允许 Axis.horizontal 或 Axis.vertical,而不是两者。
所以我尝试了以下方法:
return SingleChildScrollView( // horizontal scroll widget
scrollDirection: Axis.horizontal,
child: SingleChildScrollView( // vertical scroll widget
scrollDirection: Axis.vertical,
child: ...content of the container etc...
)
);
这适用于 x 和 y,但它不允许 diagonal 滚动。
有没有办法实现对角滚动,或者有没有更好的材料小部件,我完全错过了?
谢谢
【问题讨论】:
【参考方案1】:我已经设法找到了一个解决方案,虽然它并不完美:
我创建了一个带有Offset _scrollOffset
的StatefulWidget,它使用带有Transform 类型的子元素的ClipRect。变换矩阵 (Matrix4.identity()..translate(_offset.dx, _offset.dy)
) 应用于变换。
GestureDetector
分配了一个 onPanUpdate 回调来更新滚动位置。 _scrollOffset += e.delta
。如果滚动位置太低或太高,这可以通过设置滚动位置来限制在小部件的边界内。
Animation 和 AnimationController 用于设置投掷速度。 onPanEnd 提供最后一个平移的速度,因此只需执行一个 Tween 并根据该速度进行抛掷。
动画在TapDown 上停止,因此用户可以停止滚动速度。
这样做的主要问题是它不能完美地模仿 android 或 ios 滚动速度,尽管我正在努力尝试使用 Flutter 提供的 ScrollSimulation 类使其更好地工作。
【讨论】:
InteractiveViewer 是另一种选择,它支持对角拖动(非滚动)手势。 api.flutter.dev/flutter/widgets/InteractiveViewer-class.html【参考方案2】: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();
【讨论】:
以上是关于颤动中的多向滚动的主要内容,如果未能解决你的问题,请参考以下文章