Skip to content

Commit 9a26346

Browse files
authored
Fix update drag error that made NestedScrollView un-scrollable (flutter#127718)
#### (plus some more docs) Fixes flutter#76760 Fixes flutter#82391 Fixes flutter#45619 Fixes flutter#117316 Fixes flutter#110956 Fixes flutter#127282 Fixes flutter#32563 Fixes flutter#46089 Fixes flutter#79077 Part of fixing flutter#62833 This fixes (a bunch of) issues that have been reported differently over the years, but all have the same root cause. Sometimes the NestedScrollView would incorrectly calculate whether or not there is enough content to allow scrolling. This would only apply to drag scrolling (not mouse wheel scrolling for example). This did not relate to how the extent of the NestedScrollView is computed, but just the logic that enabled the actual drag gestures. This fixes that. :)
1 parent 270b3d4 commit 9a26346

File tree

2 files changed

+323
-22
lines changed

2 files changed

+323
-22
lines changed

packages/flutter/lib/src/widgets/nested_scroll_view.dart

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ class NestedScrollView extends StatefulWidget {
197197
final ScrollController? controller;
198198

199199
/// {@macro flutter.widgets.scroll_view.scrollDirection}
200+
///
201+
/// This property only applies to the [Axis] of the outer scroll view,
202+
/// composed of the slivers returned from [headerSliverBuilder]. Since the
203+
/// inner scroll view is not directly configured by the [NestedScrollView],
204+
/// for the axes to match, configure the scroll view of the [body] the same
205+
/// way if they are expected to scroll in the same orientation. This allows
206+
/// for flexible configurations of the NestedScrollView.
200207
final Axis scrollDirection;
201208

202209
/// Whether the scroll view scrolls in the reading direction.
@@ -210,6 +217,13 @@ class NestedScrollView extends StatefulWidget {
210217
/// scrolls from top to bottom when [reverse] is false and from bottom to top
211218
/// when [reverse] is true.
212219
///
220+
/// This property only applies to the outer scroll view, composed of the
221+
/// slivers returned from [headerSliverBuilder]. Since the inner scroll view
222+
/// is not directly configured by the [NestedScrollView]. For both to scroll
223+
/// in reverse, configure the scroll view of the [body] the same way if they
224+
/// are expected to match. This allows for flexible configurations of the
225+
/// NestedScrollView.
226+
///
213227
/// Defaults to false.
214228
final bool reverse;
215229

@@ -232,6 +246,22 @@ class NestedScrollView extends StatefulWidget {
232246
/// [ScrollMetrics.maxScrollExtent] properties passed to that method. If that
233247
/// invariant is not maintained, the nested scroll view may respond to user
234248
/// scrolling erratically.
249+
///
250+
/// This property only applies to the outer scroll view, composed of the
251+
/// slivers returned from [headerSliverBuilder]. Since the inner scroll view
252+
/// is not directly configured by the [NestedScrollView]. For both to scroll
253+
/// with the same [ScrollPhysics], configure the scroll view of the [body]
254+
/// the same way if they are expected to match, or use a [ScrollBehavior] as
255+
/// an ancestor so both the inner and outer scroll views inherit the same
256+
/// [ScrollPhysics]. This allows for flexible configurations of the
257+
/// NestedScrollView.
258+
///
259+
/// The [ScrollPhysics] also determine whether or not the [NestedScrollView]
260+
/// can accept input from the user to change the scroll offset. For example,
261+
/// [NeverScrollableScrollPhysics] typically will not allow the user to drag a
262+
/// scroll view, but in this case, if one of the two scroll views can be
263+
/// dragged, then dragging will be allowed. Configuring both scroll views with
264+
/// [NeverScrollableScrollPhysics] will disallow dragging in this case.
235265
final ScrollPhysics? physics;
236266

237267
/// A builder for any widgets that are to precede the inner scroll views (as
@@ -845,17 +875,18 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
845875
if (!_outerPosition!.haveDimensions) {
846876
return;
847877
}
848-
double maxInnerExtent = 0.0;
878+
bool innerCanDrag = false;
849879
for (final _NestedScrollPosition position in _innerPositions) {
850880
if (!position.haveDimensions) {
851881
return;
852882
}
853-
maxInnerExtent = math.max(
854-
maxInnerExtent,
855-
position.maxScrollExtent - position.minScrollExtent,
856-
);
883+
innerCanDrag = innerCanDrag
884+
// This refers to the physics of the actual inner scroll position, not
885+
// the whole NestedScrollView, since it is possible to have different
886+
// ScrollPhysics for the inner and outer positions.
887+
|| position.physics.shouldAcceptUserOffset(position);
857888
}
858-
_outerPosition!.updateCanDrag(maxInnerExtent);
889+
_outerPosition!.updateCanDrag(innerCanDrag);
859890
}
860891

861892
Future<void> animateTo(
@@ -1438,9 +1469,16 @@ class _NestedScrollPosition extends ScrollPosition implements ScrollActivityDele
14381469
coordinator.updateCanDrag();
14391470
}
14401471

1441-
void updateCanDrag(double totalExtent) {
1442-
context.setCanDrag(physics.allowUserScrolling &&
1443-
(totalExtent > (viewportDimension - maxScrollExtent) || minScrollExtent != maxScrollExtent));
1472+
void updateCanDrag(bool innerCanDrag) {
1473+
// This is only called for the outer position
1474+
assert(coordinator._outerPosition == this);
1475+
context.setCanDrag(
1476+
// This refers to the physics of the actual outer scroll position, not
1477+
// the whole NestedScrollView, since it is possible to have different
1478+
// ScrollPhysics for the inner and outer positions.
1479+
physics.shouldAcceptUserOffset(this)
1480+
|| innerCanDrag,
1481+
);
14441482
}
14451483

14461484
@override
@@ -1756,17 +1794,9 @@ class RenderSliverOverlapAbsorber extends RenderSliver with RenderObjectWithChil
17561794
}
17571795
child!.layout(constraints, parentUsesSize: true);
17581796
final SliverGeometry childLayoutGeometry = child!.geometry!;
1759-
geometry = SliverGeometry(
1797+
geometry = childLayoutGeometry.copyWith(
17601798
scrollExtent: childLayoutGeometry.scrollExtent - childLayoutGeometry.maxScrollObstructionExtent,
1761-
paintExtent: childLayoutGeometry.paintExtent,
1762-
paintOrigin: childLayoutGeometry.paintOrigin,
17631799
layoutExtent: math.max(0, childLayoutGeometry.paintExtent - childLayoutGeometry.maxScrollObstructionExtent),
1764-
maxPaintExtent: childLayoutGeometry.maxPaintExtent,
1765-
maxScrollObstructionExtent: childLayoutGeometry.maxScrollObstructionExtent,
1766-
hitTestExtent: childLayoutGeometry.hitTestExtent,
1767-
visible: childLayoutGeometry.visible,
1768-
hasVisualOverflow: childLayoutGeometry.hasVisualOverflow,
1769-
scrollOffsetCorrection: childLayoutGeometry.scrollOffsetCorrection,
17701800
);
17711801
handle._setExtents(
17721802
childLayoutGeometry.maxScrollObstructionExtent,

0 commit comments

Comments
 (0)