From 944de73b30da91dd9e1166257b8d6a44bba3cd37 Mon Sep 17 00:00:00 2001 From: Stefan Schaller Date: Tue, 12 Mar 2024 07:40:00 +0100 Subject: [PATCH 1/6] add more values to adjust the snaping --- lib/scrollable_bottom_sheet.dart | 96 +++++++++++++++----------------- 1 file changed, 44 insertions(+), 52 deletions(-) diff --git a/lib/scrollable_bottom_sheet.dart b/lib/scrollable_bottom_sheet.dart index c08f11c..88de3ed 100644 --- a/lib/scrollable_bottom_sheet.dart +++ b/lib/scrollable_bottom_sheet.dart @@ -8,6 +8,9 @@ typedef ScrollableBottomSheetBuilder = Widget Function( ScrollController scrollController, ); +const double _kMinFlingVelocity = 600.0; +const double _kCompleteFlingVelocity = 4000.0; + class ScrollableBottomSheet extends StatefulWidget { final double maxHeight; final double minHeight; @@ -22,17 +25,18 @@ class ScrollableBottomSheet extends StatefulWidget { final Color borderColor; final Color backgroundColor; - - final bool canDrag; final List? shadows; + final double minFlingVelocity; + + final double completeFlingVelocity; + const ScrollableBottomSheet({ super.key, required this.maxHeight, required this.minHeight, required this.builder, this.snapPositions = const [], - this.canDrag = true, this.animationDuration = const Duration(milliseconds: 350), this.onSizeChanged, this.initialPosition, @@ -40,6 +44,8 @@ class ScrollableBottomSheet extends StatefulWidget { required this.borderColor, required this.backgroundColor, this.shadows, + this.minFlingVelocity = _kMinFlingVelocity, + this.completeFlingVelocity = _kCompleteFlingVelocity, }); @override @@ -50,16 +56,14 @@ class ScrollableBottomSheet extends StatefulWidget { } } -class ScrollableBottomSheetState extends State - with SingleTickerProviderStateMixin { +class ScrollableBottomSheetState extends State with SingleTickerProviderStateMixin { final _scrollController = ScrollController(); late AnimationController _animationController; final _velocityTracker = VelocityTracker.withKind(PointerDeviceKind.touch); var _scrollingEnabled = false; var _isScrollingBlocked = false; - Tween get _sizeTween => - Tween(begin: widget.minHeight, end: widget.maxHeight); + Tween get _sizeTween => Tween(begin: widget.minHeight, end: widget.maxHeight); bool get _isPanelOpen => _animationController.value == 1.0; @@ -74,9 +78,7 @@ class ScrollableBottomSheetState extends State _animationController = AnimationController( vsync: this, duration: widget.animationDuration, - value: widget.initialPosition == null - ? 0.0 - : _pixelToValue(widget.initialPosition!), + value: widget.initialPosition == null ? 0.0 : _pixelToValue(widget.initialPosition!), )..addListener(_notifyScrollListeners); } @@ -89,22 +91,15 @@ class ScrollableBottomSheetState extends State @override Widget build(BuildContext context) { - final borderRadius = - BorderRadius.vertical(top: Radius.circular(widget.borderRadiusTop)); + final borderRadius = BorderRadius.vertical(top: Radius.circular(widget.borderRadiusTop)); return Listener( - onPointerDown: widget.canDrag - ? (p) => _velocityTracker.addPosition(p.timeStamp, p.position) - : null, - onPointerMove: widget.canDrag - ? (p) { - _velocityTracker.addPosition(p.timeStamp, p.position); - _onDragUpdate(p.delta.dy); - } - : null, - onPointerUp: widget.canDrag - ? (p) => _onGestureEnd(_velocityTracker.getVelocity()) - : null, + onPointerDown: (event) => _velocityTracker.addPosition(event.timeStamp, event.position), + onPointerMove: (event) { + _velocityTracker.addPosition(event.timeStamp, event.position); + _onDragUpdate(event); + }, + onPointerUp: (event) => _onGestureEnd(_velocityTracker.getVelocity()), child: MediaQuery.removePadding( context: context, removeTop: true, @@ -133,8 +128,7 @@ class ScrollableBottomSheetState extends State ); }, child: Builder( - builder: (context) => - widget.builder(context, _scrollController), + builder: (context) => widget.builder(context, _scrollController), ), ), ), @@ -145,7 +139,9 @@ class ScrollableBottomSheetState extends State // region drag updates - void _onDragUpdate(double dy) { + void _onDragUpdate(PointerMoveEvent event) { + final dy = event.delta.dy; + // only slide the panel if scrolling is not enabled if (!_scrollingEnabled && !_isScrollingBlocked) { _animationController.value -= dy / (widget.maxHeight - widget.minHeight); @@ -154,18 +150,9 @@ class ScrollableBottomSheetState extends State // if the panel is open and the user hasn't scrolled, we need to determine // whether to enable scrolling if the user swipes up, or disable closing and // begin to close the panel if the user swipes down - if (_isPanelOpen && - _scrollController.hasClients && - _scrollController.offset <= 0) { + if (_isPanelOpen && _scrollController.hasClients && _scrollController.offset <= 0) { setState(() => _scrollingEnabled = dy < 0); } - - //update scrolling if panel is open - if (_isPanelOpen) { - double scrollOffset = _scrollController.offset; - final scrollTo = scrollOffset -= dy; - _scrollController.jumpTo(scrollTo); - } } // endregion @@ -188,19 +175,25 @@ class ScrollableBottomSheetState extends State // the panel if they swipe up on the scrollable if (_isPanelOpen && _scrollingEnabled) return; - final dyVelocity = velocity.pixelsPerSecond.dy; - final visualVelocity = -dyVelocity / (widget.maxHeight - widget.minHeight); - - final newPosition = - _findNearestRelativeSnapPoint(target: _animationController.value); - - switch (newPosition) { - case 1.0: - open(); - case 0.0: - close(); - default: - _flingPanelToPosition(newPosition, visualVelocity); + final scrollPixelPerSeconds = velocity.pixelsPerSecond.dy; + final flingVelocity = -scrollPixelPerSeconds / (widget.maxHeight - widget.minHeight); + + final nearestSnapPoint = _findNearestRelativeSnapPoint(target: _animationController.value); + + if (scrollPixelPerSeconds > widget.completeFlingVelocity) { + if (flingVelocity.isNegative) { + _flingPanelToPosition(0.0, flingVelocity); + } else { + _flingPanelToPosition(1.0, flingVelocity); + } + } else { + if (scrollPixelPerSeconds > widget.minFlingVelocity) { + _flingPanelToPosition(nearestSnapPoint, flingVelocity); + } else { + final pixels = _sizeTween.transform(nearestSnapPoint); + + animateTo(pixels: pixels, duration: widget.animationDuration); + } } } @@ -246,8 +239,7 @@ class ScrollableBottomSheetState extends State } Future animateToNearestSnapPoint() { - final newPosition = - _findNearestRelativeSnapPoint(target: _animationController.value); + final newPosition = _findNearestRelativeSnapPoint(target: _animationController.value); return animateTo( pixels: _sizeTween.transform(newPosition), duration: widget.animationDuration, From c3b8bce50014619aa1cdcd2d3bca4d9895ce0803 Mon Sep 17 00:00:00 2001 From: Stefan Schaller Date: Tue, 12 Mar 2024 08:44:32 +0100 Subject: [PATCH 2/6] add more values to adjust the snaping --- example/lib/main.dart | 2 +- lib/gesture_listener.dart | 69 +++++++++++++++++++++++++ lib/scrollable_bottom_sheet.dart | 89 +++++++++++++++++++++++++------- 3 files changed, 139 insertions(+), 21 deletions(-) create mode 100644 lib/gesture_listener.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 9a3fb5a..c92e610 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -10,7 +10,7 @@ class MainApp extends StatelessWidget { @override Widget build(BuildContext context) { - final maxHeight = MediaQuery.of(context).size.height - kToolbarHeight; + final maxHeight = MediaQuery.of(context).size.height - kToolbarHeight - 100; return MaterialApp( home: Scaffold( appBar: AppBar( diff --git a/lib/gesture_listener.dart b/lib/gesture_listener.dart new file mode 100644 index 0000000..29b2689 --- /dev/null +++ b/lib/gesture_listener.dart @@ -0,0 +1,69 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/gestures.dart'; + +class GestureListener extends StatefulWidget { + final void Function(DragUpdateDetails) onVerticalDragUpdate; + final void Function(DragEndDetails) onVerticalDragEnd; + final void Function() onVerticalDragCancel; + + final Widget child; + + const GestureListener({ + super.key, + required this.onVerticalDragUpdate, + required this.onVerticalDragEnd, + required this.onVerticalDragCancel, + required this.child, + }); + + @override + State createState() => _GestureListenerState(); +} + +class _GestureListenerState extends State { + final _velocityTracker = VelocityTracker.withKind(PointerDeviceKind.touch); + + @override + Widget build(BuildContext context) { + return Listener( + onPointerDown: (event) { + _velocityTracker.addPosition(event.timeStamp, event.position); + }, + onPointerMove: (event) { + _velocityTracker.addPosition(event.timeStamp, event.position); + + final delta = event.delta; + final primaryDelta = delta.dy; + + /// ⚠️ If not assign the dx to 0, an assertion is not working. + final offset = Offset(0, primaryDelta); + + final details = DragUpdateDetails( + globalPosition: event.position, + delta: offset, + localPosition: event.localPosition, + sourceTimeStamp: event.timeStamp, + primaryDelta: primaryDelta, + ); + + widget.onVerticalDragUpdate(details); + }, + onPointerUp: (event) { + final velocity = _velocityTracker.getVelocity(); + + final pixelsPerSecondY = velocity.pixelsPerSecond.dy; + + /// ⚠️ If not assign the dx to 0, an assertion is not working. + final offset = Offset(0, pixelsPerSecondY); + final details = DragEndDetails( + velocity: Velocity(pixelsPerSecond: offset), + primaryVelocity: pixelsPerSecondY, + ); + + widget.onVerticalDragEnd(details); + }, + onPointerCancel: (event) => widget.onVerticalDragCancel(), + child: widget.child, + ); + } +} diff --git a/lib/scrollable_bottom_sheet.dart b/lib/scrollable_bottom_sheet.dart index 88de3ed..ce514dd 100644 --- a/lib/scrollable_bottom_sheet.dart +++ b/lib/scrollable_bottom_sheet.dart @@ -2,6 +2,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/physics.dart'; import 'package:non_uniform_border/non_uniform_border.dart'; +import 'package:tapped_bottom_sheet/gesture_listener.dart'; typedef ScrollableBottomSheetBuilder = Widget Function( BuildContext context, @@ -59,10 +60,13 @@ class ScrollableBottomSheet extends StatefulWidget { class ScrollableBottomSheetState extends State with SingleTickerProviderStateMixin { final _scrollController = ScrollController(); late AnimationController _animationController; - final _velocityTracker = VelocityTracker.withKind(PointerDeviceKind.touch); - var _scrollingEnabled = false; + var _isScrollingEnabled = false; var _isScrollingBlocked = false; + Drag? _drag; + + ScrollHoldController? _hold; + Tween get _sizeTween => Tween(begin: widget.minHeight, end: widget.maxHeight); bool get _isPanelOpen => _animationController.value == 1.0; @@ -93,13 +97,10 @@ class ScrollableBottomSheetState extends State with Singl Widget build(BuildContext context) { final borderRadius = BorderRadius.vertical(top: Radius.circular(widget.borderRadiusTop)); - return Listener( - onPointerDown: (event) => _velocityTracker.addPosition(event.timeStamp, event.position), - onPointerMove: (event) { - _velocityTracker.addPosition(event.timeStamp, event.position); - _onDragUpdate(event); - }, - onPointerUp: (event) => _onGestureEnd(_velocityTracker.getVelocity()), + return GestureListener( + onVerticalDragUpdate: (details) => _onDragUpdate(details), + onVerticalDragEnd: (details) => _onDragEnd(details), + onVerticalDragCancel: () => _handleDragCancel(), child: MediaQuery.removePadding( context: context, removeTop: true, @@ -139,19 +140,43 @@ class ScrollableBottomSheetState extends State with Singl // region drag updates - void _onDragUpdate(PointerMoveEvent event) { - final dy = event.delta.dy; + void _onDragUpdate(DragUpdateDetails details) { + final delta = details.delta; + final primaryDelta = delta.dy; + + if (_isScrollingEnabled && _isPanelOpen) { + // _drag might be null if the drag activity ended and called _disposeDrag. + assert(_hold == null || _drag == null); + _drag?.update(details); + if (_scrollController.position.pixels <= 0 && details.primaryDelta! > 0) { + setState(() => _isScrollingEnabled = false); + _handleDragCancel(); + if (_scrollController.position.pixels != 0.0) { + _scrollController.position.setPixels(0.0); + } + } + return; + } // only slide the panel if scrolling is not enabled - if (!_scrollingEnabled && !_isScrollingBlocked) { - _animationController.value -= dy / (widget.maxHeight - widget.minHeight); + if (!_isScrollingEnabled && !_isScrollingBlocked) { + _animationController.value -= primaryDelta / (widget.maxHeight - widget.minHeight); } // if the panel is open and the user hasn't scrolled, we need to determine // whether to enable scrolling if the user swipes up, or disable closing and // begin to close the panel if the user swipes down if (_isPanelOpen && _scrollController.hasClients && _scrollController.offset <= 0) { - setState(() => _scrollingEnabled = dy < 0); + final scrollingEnabled = primaryDelta < 0; + + setState(() => _isScrollingEnabled = scrollingEnabled); + + if (scrollingEnabled) { + final startDetails = + DragStartDetails(sourceTimeStamp: details.sourceTimeStamp, globalPosition: details.globalPosition); + _hold = _scrollController.position.hold(_disposeHold); + _drag = _scrollController.position.drag(startDetails, _disposeDrag); + } } } @@ -160,12 +185,12 @@ class ScrollableBottomSheetState extends State with Singl // region animation and scroll void _onScroll() { - if (!_scrollingEnabled || _isScrollingBlocked) { + if (!_isScrollingEnabled || _isScrollingBlocked) { _scrollController.jumpTo(0); } } - void _onGestureEnd(Velocity velocity) { + void _onDragEnd(DragEndDetails details) { if (_isScrollingBlocked) return; // let the current animation finish before starting a new one @@ -173,9 +198,15 @@ class ScrollableBottomSheetState extends State with Singl // if scrolling is allowed and the panel is open, we don't want to close // the panel if they swipe up on the scrollable - if (_isPanelOpen && _scrollingEnabled) return; + if (_isPanelOpen && _isScrollingEnabled) { + assert(_hold == null || _drag == null); + _drag?.end(details); + assert(_drag == null); - final scrollPixelPerSeconds = velocity.pixelsPerSecond.dy; + return; + } + + final scrollPixelPerSeconds = details.velocity.pixelsPerSecond.dy; final flingVelocity = -scrollPixelPerSeconds / (widget.maxHeight - widget.minHeight); final nearestSnapPoint = _findNearestRelativeSnapPoint(target: _animationController.value); @@ -223,7 +254,7 @@ class ScrollableBottomSheetState extends State with Singl // region panel options Future close() async { - setState(() => _scrollingEnabled = false); + setState(() => _isScrollingEnabled = false); await _scrollController.animateTo( 0.0, @@ -260,7 +291,7 @@ class ScrollableBottomSheetState extends State with Singl // Reset the initial state, since we had some issues in the full state of the booking summary setState(() { - _scrollingEnabled = false; + _isScrollingEnabled = false; _isScrollingBlocked = false; }); } @@ -281,6 +312,24 @@ class ScrollableBottomSheetState extends State with Singl final size = _sizeTween.transform(_animationController.value); widget.onSizeChanged!.call(_animationController.value, size); } + + void _handleDragCancel() { + // _hold might be null if the drag started. + // _drag might be null if the drag activity ended and called _disposeDrag. + assert(_hold == null || _drag == null); + _hold?.cancel(); + _drag?.cancel(); + assert(_hold == null); + assert(_drag == null); + } + + void _disposeHold() { + _hold = null; + } + + void _disposeDrag() { + _drag = null; + } } double _findClosestPosition({ From 2277c2e089dce3b5a7e34736b258750dca5e8134 Mon Sep 17 00:00:00 2001 From: Stefan Schaller Date: Tue, 12 Mar 2024 09:02:51 +0100 Subject: [PATCH 3/6] format code --- lib/scrollable_bottom_sheet.dart | 37 +++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/lib/scrollable_bottom_sheet.dart b/lib/scrollable_bottom_sheet.dart index ce514dd..18ac22a 100644 --- a/lib/scrollable_bottom_sheet.dart +++ b/lib/scrollable_bottom_sheet.dart @@ -57,7 +57,8 @@ class ScrollableBottomSheet extends StatefulWidget { } } -class ScrollableBottomSheetState extends State with SingleTickerProviderStateMixin { +class ScrollableBottomSheetState extends State + with SingleTickerProviderStateMixin { final _scrollController = ScrollController(); late AnimationController _animationController; var _isScrollingEnabled = false; @@ -67,7 +68,8 @@ class ScrollableBottomSheetState extends State with Singl ScrollHoldController? _hold; - Tween get _sizeTween => Tween(begin: widget.minHeight, end: widget.maxHeight); + Tween get _sizeTween => + Tween(begin: widget.minHeight, end: widget.maxHeight); bool get _isPanelOpen => _animationController.value == 1.0; @@ -82,7 +84,9 @@ class ScrollableBottomSheetState extends State with Singl _animationController = AnimationController( vsync: this, duration: widget.animationDuration, - value: widget.initialPosition == null ? 0.0 : _pixelToValue(widget.initialPosition!), + value: widget.initialPosition == null + ? 0.0 + : _pixelToValue(widget.initialPosition!), )..addListener(_notifyScrollListeners); } @@ -95,7 +99,8 @@ class ScrollableBottomSheetState extends State with Singl @override Widget build(BuildContext context) { - final borderRadius = BorderRadius.vertical(top: Radius.circular(widget.borderRadiusTop)); + final borderRadius = + BorderRadius.vertical(top: Radius.circular(widget.borderRadiusTop)); return GestureListener( onVerticalDragUpdate: (details) => _onDragUpdate(details), @@ -129,7 +134,8 @@ class ScrollableBottomSheetState extends State with Singl ); }, child: Builder( - builder: (context) => widget.builder(context, _scrollController), + builder: (context) => + widget.builder(context, _scrollController), ), ), ), @@ -160,20 +166,24 @@ class ScrollableBottomSheetState extends State with Singl // only slide the panel if scrolling is not enabled if (!_isScrollingEnabled && !_isScrollingBlocked) { - _animationController.value -= primaryDelta / (widget.maxHeight - widget.minHeight); + _animationController.value -= + primaryDelta / (widget.maxHeight - widget.minHeight); } // if the panel is open and the user hasn't scrolled, we need to determine // whether to enable scrolling if the user swipes up, or disable closing and // begin to close the panel if the user swipes down - if (_isPanelOpen && _scrollController.hasClients && _scrollController.offset <= 0) { + if (_isPanelOpen && + _scrollController.hasClients && + _scrollController.offset <= 0) { final scrollingEnabled = primaryDelta < 0; setState(() => _isScrollingEnabled = scrollingEnabled); if (scrollingEnabled) { - final startDetails = - DragStartDetails(sourceTimeStamp: details.sourceTimeStamp, globalPosition: details.globalPosition); + final startDetails = DragStartDetails( + sourceTimeStamp: details.sourceTimeStamp, + globalPosition: details.globalPosition); _hold = _scrollController.position.hold(_disposeHold); _drag = _scrollController.position.drag(startDetails, _disposeDrag); } @@ -207,9 +217,11 @@ class ScrollableBottomSheetState extends State with Singl } final scrollPixelPerSeconds = details.velocity.pixelsPerSecond.dy; - final flingVelocity = -scrollPixelPerSeconds / (widget.maxHeight - widget.minHeight); + final flingVelocity = + -scrollPixelPerSeconds / (widget.maxHeight - widget.minHeight); - final nearestSnapPoint = _findNearestRelativeSnapPoint(target: _animationController.value); + final nearestSnapPoint = + _findNearestRelativeSnapPoint(target: _animationController.value); if (scrollPixelPerSeconds > widget.completeFlingVelocity) { if (flingVelocity.isNegative) { @@ -270,7 +282,8 @@ class ScrollableBottomSheetState extends State with Singl } Future animateToNearestSnapPoint() { - final newPosition = _findNearestRelativeSnapPoint(target: _animationController.value); + final newPosition = + _findNearestRelativeSnapPoint(target: _animationController.value); return animateTo( pixels: _sizeTween.transform(newPosition), duration: widget.animationDuration, From ed59458469541eef4db973f2ee2d931b05e9d7e4 Mon Sep 17 00:00:00 2001 From: Stefan Schaller Date: Tue, 12 Mar 2024 17:51:09 +0100 Subject: [PATCH 4/6] add canDrag again --- lib/gesture_listener.dart | 15 ++++++++++++++- lib/scrollable_bottom_sheet.dart | 9 +++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/gesture_listener.dart b/lib/gesture_listener.dart index 29b2689..3cbda4a 100644 --- a/lib/gesture_listener.dart +++ b/lib/gesture_listener.dart @@ -6,10 +6,13 @@ class GestureListener extends StatefulWidget { final void Function(DragEndDetails) onVerticalDragEnd; final void Function() onVerticalDragCancel; + final bool canDrag; + final Widget child; const GestureListener({ super.key, + required this.canDrag, required this.onVerticalDragUpdate, required this.onVerticalDragEnd, required this.onVerticalDragCancel, @@ -27,9 +30,13 @@ class _GestureListenerState extends State { Widget build(BuildContext context) { return Listener( onPointerDown: (event) { + if (!widget.canDrag) return; + _velocityTracker.addPosition(event.timeStamp, event.position); }, onPointerMove: (event) { + if (!widget.canDrag) return; + _velocityTracker.addPosition(event.timeStamp, event.position); final delta = event.delta; @@ -49,6 +56,8 @@ class _GestureListenerState extends State { widget.onVerticalDragUpdate(details); }, onPointerUp: (event) { + if (!widget.canDrag) return; + final velocity = _velocityTracker.getVelocity(); final pixelsPerSecondY = velocity.pixelsPerSecond.dy; @@ -62,7 +71,11 @@ class _GestureListenerState extends State { widget.onVerticalDragEnd(details); }, - onPointerCancel: (event) => widget.onVerticalDragCancel(), + onPointerCancel: (event) { + if (!widget.canDrag) return; + + widget.onVerticalDragCancel(); + }, child: widget.child, ); } diff --git a/lib/scrollable_bottom_sheet.dart b/lib/scrollable_bottom_sheet.dart index 18ac22a..8b83650 100644 --- a/lib/scrollable_bottom_sheet.dart +++ b/lib/scrollable_bottom_sheet.dart @@ -25,6 +25,8 @@ class ScrollableBottomSheet extends StatefulWidget { final Color borderColor; + final bool canDrag; + final Color backgroundColor; final List? shadows; @@ -38,6 +40,7 @@ class ScrollableBottomSheet extends StatefulWidget { required this.minHeight, required this.builder, this.snapPositions = const [], + this.canDrag = true, this.animationDuration = const Duration(milliseconds: 350), this.onSizeChanged, this.initialPosition, @@ -103,6 +106,7 @@ class ScrollableBottomSheetState extends State BorderRadius.vertical(top: Radius.circular(widget.borderRadiusTop)); return GestureListener( + canDrag: widget.canDrag, onVerticalDragUpdate: (details) => _onDragUpdate(details), onVerticalDragEnd: (details) => _onDragEnd(details), onVerticalDragCancel: () => _handleDragCancel(), @@ -182,8 +186,9 @@ class ScrollableBottomSheetState extends State if (scrollingEnabled) { final startDetails = DragStartDetails( - sourceTimeStamp: details.sourceTimeStamp, - globalPosition: details.globalPosition); + sourceTimeStamp: details.sourceTimeStamp, + globalPosition: details.globalPosition, + ); _hold = _scrollController.position.hold(_disposeHold); _drag = _scrollController.position.drag(startDetails, _disposeDrag); } From ec25c375ed152205d413d58046712cb31fd21ef6 Mon Sep 17 00:00:00 2001 From: Stefan Schaller Date: Tue, 12 Mar 2024 18:02:52 +0100 Subject: [PATCH 5/6] fix check --- lib/scrollable_bottom_sheet.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/scrollable_bottom_sheet.dart b/lib/scrollable_bottom_sheet.dart index 8b83650..00130bc 100644 --- a/lib/scrollable_bottom_sheet.dart +++ b/lib/scrollable_bottom_sheet.dart @@ -158,7 +158,9 @@ class ScrollableBottomSheetState extends State // _drag might be null if the drag activity ended and called _disposeDrag. assert(_hold == null || _drag == null); _drag?.update(details); - if (_scrollController.position.pixels <= 0 && details.primaryDelta! > 0) { + if (_scrollController.hasClients && + _scrollController.position.pixels <= 0 && + details.primaryDelta! > 0) { setState(() => _isScrollingEnabled = false); _handleDragCancel(); if (_scrollController.position.pixels != 0.0) { From c2e681c6fe4091f3966e1f22458837311d35df9e Mon Sep 17 00:00:00 2001 From: Julian Bissekkou Date: Tue, 12 Mar 2024 18:17:32 +0100 Subject: [PATCH 6/6] update dcos --- .fvm/flutter_sdk | 1 + lib/gesture_listener.dart | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 120000 .fvm/flutter_sdk diff --git a/.fvm/flutter_sdk b/.fvm/flutter_sdk new file mode 120000 index 0000000..6f06b69 --- /dev/null +++ b/.fvm/flutter_sdk @@ -0,0 +1 @@ +/Users/julianbissekkou/fvm/versions/3.16.9 \ No newline at end of file diff --git a/lib/gesture_listener.dart b/lib/gesture_listener.dart index 3cbda4a..1e7a7c7 100644 --- a/lib/gesture_listener.dart +++ b/lib/gesture_listener.dart @@ -42,7 +42,8 @@ class _GestureListenerState extends State { final delta = event.delta; final primaryDelta = delta.dy; - /// ⚠️ If not assign the dx to 0, an assertion is not working. + /// ⚠️ If not assign the dx to 0, an assertion + /// in the constructor of [DragUpdateDetails] is thrown. final offset = Offset(0, primaryDelta); final details = DragUpdateDetails( @@ -62,7 +63,8 @@ class _GestureListenerState extends State { final pixelsPerSecondY = velocity.pixelsPerSecond.dy; - /// ⚠️ If not assign the dx to 0, an assertion is not working. + /// ⚠️ If not assign the dx to 0, an assertion + /// in the constructor of [DragEndDetails] is thrown. final offset = Offset(0, pixelsPerSecondY); final details = DragEndDetails( velocity: Velocity(pixelsPerSecond: offset),