Unverified Commit 9a263466 authored by Kate Lovett's avatar Kate Lovett Committed by GitHub

Fix update drag error that made NestedScrollView un-scrollable (#127718)

#### (plus some more docs)

Fixes https://github.com/flutter/flutter/issues/76760
Fixes https://github.com/flutter/flutter/issues/82391
Fixes https://github.com/flutter/flutter/issues/45619
Fixes #117316
Fixes #110956
Fixes #127282 
Fixes #32563
Fixes #46089 
Fixes #79077
Part of fixing #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. :)
parent 270b3d48
......@@ -197,6 +197,13 @@ class NestedScrollView extends StatefulWidget {
final ScrollController? controller;
/// {@macro flutter.widgets.scroll_view.scrollDirection}
///
/// This property only applies to the [Axis] of the outer scroll view,
/// composed of the slivers returned from [headerSliverBuilder]. Since the
/// inner scroll view is not directly configured by the [NestedScrollView],
/// for the axes to match, configure the scroll view of the [body] the same
/// way if they are expected to scroll in the same orientation. This allows
/// for flexible configurations of the NestedScrollView.
final Axis scrollDirection;
/// Whether the scroll view scrolls in the reading direction.
......@@ -210,6 +217,13 @@ class NestedScrollView extends StatefulWidget {
/// scrolls from top to bottom when [reverse] is false and from bottom to top
/// when [reverse] is true.
///
/// This property only applies to the outer scroll view, composed of the
/// slivers returned from [headerSliverBuilder]. Since the inner scroll view
/// is not directly configured by the [NestedScrollView]. For both to scroll
/// in reverse, configure the scroll view of the [body] the same way if they
/// are expected to match. This allows for flexible configurations of the
/// NestedScrollView.
///
/// Defaults to false.
final bool reverse;
......@@ -232,6 +246,22 @@ class NestedScrollView extends StatefulWidget {
/// [ScrollMetrics.maxScrollExtent] properties passed to that method. If that
/// invariant is not maintained, the nested scroll view may respond to user
/// scrolling erratically.
///
/// This property only applies to the outer scroll view, composed of the
/// slivers returned from [headerSliverBuilder]. Since the inner scroll view
/// is not directly configured by the [NestedScrollView]. For both to scroll
/// with the same [ScrollPhysics], configure the scroll view of the [body]
/// the same way if they are expected to match, or use a [ScrollBehavior] as
/// an ancestor so both the inner and outer scroll views inherit the same
/// [ScrollPhysics]. This allows for flexible configurations of the
/// NestedScrollView.
///
/// The [ScrollPhysics] also determine whether or not the [NestedScrollView]
/// can accept input from the user to change the scroll offset. For example,
/// [NeverScrollableScrollPhysics] typically will not allow the user to drag a
/// scroll view, but in this case, if one of the two scroll views can be
/// dragged, then dragging will be allowed. Configuring both scroll views with
/// [NeverScrollableScrollPhysics] will disallow dragging in this case.
final ScrollPhysics? physics;
/// A builder for any widgets that are to precede the inner scroll views (as
......@@ -845,17 +875,18 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
if (!_outerPosition!.haveDimensions) {
return;
}
double maxInnerExtent = 0.0;
bool innerCanDrag = false;
for (final _NestedScrollPosition position in _innerPositions) {
if (!position.haveDimensions) {
return;
}
maxInnerExtent = math.max(
maxInnerExtent,
position.maxScrollExtent - position.minScrollExtent,
);
innerCanDrag = innerCanDrag
// This refers to the physics of the actual inner scroll position, not
// the whole NestedScrollView, since it is possible to have different
// ScrollPhysics for the inner and outer positions.
|| position.physics.shouldAcceptUserOffset(position);
}
_outerPosition!.updateCanDrag(maxInnerExtent);
_outerPosition!.updateCanDrag(innerCanDrag);
}
Future<void> animateTo(
......@@ -1438,9 +1469,16 @@ class _NestedScrollPosition extends ScrollPosition implements ScrollActivityDele
coordinator.updateCanDrag();
}
void updateCanDrag(double totalExtent) {
context.setCanDrag(physics.allowUserScrolling &&
(totalExtent > (viewportDimension - maxScrollExtent) || minScrollExtent != maxScrollExtent));
void updateCanDrag(bool innerCanDrag) {
// This is only called for the outer position
assert(coordinator._outerPosition == this);
context.setCanDrag(
// This refers to the physics of the actual outer scroll position, not
// the whole NestedScrollView, since it is possible to have different
// ScrollPhysics for the inner and outer positions.
physics.shouldAcceptUserOffset(this)
|| innerCanDrag,
);
}
@override
......@@ -1756,17 +1794,9 @@ class RenderSliverOverlapAbsorber extends RenderSliver with RenderObjectWithChil
}
child!.layout(constraints, parentUsesSize: true);
final SliverGeometry childLayoutGeometry = child!.geometry!;
geometry = SliverGeometry(
geometry = childLayoutGeometry.copyWith(
scrollExtent: childLayoutGeometry.scrollExtent - childLayoutGeometry.maxScrollObstructionExtent,
paintExtent: childLayoutGeometry.paintExtent,
paintOrigin: childLayoutGeometry.paintOrigin,
layoutExtent: math.max(0, childLayoutGeometry.paintExtent - childLayoutGeometry.maxScrollObstructionExtent),
maxPaintExtent: childLayoutGeometry.maxPaintExtent,
maxScrollObstructionExtent: childLayoutGeometry.maxScrollObstructionExtent,
hitTestExtent: childLayoutGeometry.hitTestExtent,
visible: childLayoutGeometry.visible,
hasVisualOverflow: childLayoutGeometry.hasVisualOverflow,
scrollOffsetCorrection: childLayoutGeometry.scrollOffsetCorrection,
);
handle._setExtents(
childLayoutGeometry.maxScrollObstructionExtent,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment