我来自Web开发背景,习惯于创建同时具有x和y溢出的元素(允许向任何方向滚动)。我正在努力实现与Flutter相同的功能。
在文档中,我找到了SingleChildScrollView,但它仅允许Axis.horizontal或Axis.vertical,而不能同时使用这两种方法。
所以我尝试了以下方法:
{{1}}
这同时适用于x和y,但不允许 对角线 滚动。
有没有一种方法可以实现对角线滚动,或者我完全缺少更好的材质小部件?
谢谢
答案 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();
}
}