Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
60a8b333
Unverified
Commit
60a8b333
authored
Nov 02, 2020
by
YeungKC
Committed by
GitHub
Nov 02, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix mouse wheel scroll miscontrol of ScrollPosition. (#66039)
parent
8291f481
Changes
6
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
514 additions
and
13 deletions
+514
-13
nested_scroll_view.dart
packages/flutter/lib/src/widgets/nested_scroll_view.dart
+89
-0
scroll_position.dart
packages/flutter/lib/src/widgets/scroll_position.dart
+16
-0
scroll_position_with_single_context.dart
.../lib/src/widgets/scroll_position_with_single_context.dart
+23
-0
scrollable.dart
packages/flutter/lib/src/widgets/scrollable.dart
+9
-11
nested_scroll_view_test.dart
packages/flutter/test/widgets/nested_scroll_view_test.dart
+344
-2
scrollable_test.dart
packages/flutter/test/widgets/scrollable_test.dart
+33
-0
No files found.
packages/flutter/lib/src/widgets/nested_scroll_view.dart
View file @
60a8b333
...
...
@@ -1072,6 +1072,63 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
goBallistic
(
0.0
);
}
void
pointerScroll
(
double
delta
)
{
assert
(
delta
!=
0.0
);
goIdle
();
updateUserScrollDirection
(
delta
<
0.0
?
ScrollDirection
.
forward
:
ScrollDirection
.
reverse
);
if
(
_innerPositions
.
isEmpty
)
{
// Does not enter overscroll.
_outerPosition
!.
applyClampedPointerSignalUpdate
(
delta
);
}
else
if
(
delta
>
0.0
)
{
// Dragging "up" - delta is positive
// Prioritize getting rid of any inner overscroll, and then the outer
// view, so that the app bar will scroll out of the way asap.
double
outerDelta
=
delta
;
for
(
final
_NestedScrollPosition
position
in
_innerPositions
)
{
if
(
position
.
pixels
<
0.0
)
{
// This inner position is in overscroll.
final
double
potentialOuterDelta
=
position
.
applyClampedPointerSignalUpdate
(
delta
);
// In case there are multiple positions in varying states of
// overscroll, the first to 'reach' the outer view above takes
// precedence.
outerDelta
=
math
.
max
(
outerDelta
,
potentialOuterDelta
);
}
}
if
(
outerDelta
!=
0.0
)
{
final
double
innerDelta
=
_outerPosition
!.
applyClampedPointerSignalUpdate
(
outerDelta
);
if
(
innerDelta
!=
0.0
)
{
for
(
final
_NestedScrollPosition
position
in
_innerPositions
)
position
.
applyClampedPointerSignalUpdate
(
innerDelta
);
}
}
}
else
{
// Dragging "down" - delta is negative
double
innerDelta
=
delta
;
// Apply delta to the outer header first if it is configured to float.
if
(
_floatHeaderSlivers
)
innerDelta
=
_outerPosition
!.
applyClampedPointerSignalUpdate
(
delta
);
if
(
innerDelta
!=
0.0
)
{
// Apply the innerDelta, if we have not floated in the outer scrollable,
// any leftover delta after this will be passed on to the outer
// scrollable by the outerDelta.
double
outerDelta
=
0.0
;
// it will go negative if it changes
for
(
final
_NestedScrollPosition
position
in
_innerPositions
)
{
final
double
overscroll
=
position
.
applyClampedPointerSignalUpdate
(
innerDelta
);
outerDelta
=
math
.
min
(
outerDelta
,
overscroll
);
}
if
(
outerDelta
!=
0.0
)
_outerPosition
!.
applyClampedPointerSignalUpdate
(
outerDelta
);
}
}
goBallistic
(
0.0
);
}
@override
double
setPixels
(
double
newPixels
)
{
assert
(
false
);
...
...
@@ -1386,6 +1443,32 @@ class _NestedScrollPosition extends ScrollPosition implements ScrollActivityDele
return
0.0
;
}
// Returns the amount of delta that was not used.
//
// Negative delta represents a forward ScrollDirection, while the positive
// would be a reverse ScrollDirection.
//
// The method doesn't take into account the effects of [ScrollPhysics].
double
applyClampedPointerSignalUpdate
(
double
delta
)
{
assert
(
delta
!=
0.0
);
final
double
min
=
delta
>
0.0
?
-
double
.
infinity
:
math
.
min
(
minScrollExtent
,
pixels
);
// The logic for max is equivalent but on the other side.
final
double
max
=
delta
<
0.0
?
double
.
infinity
:
math
.
max
(
maxScrollExtent
,
pixels
);
final
double
newPixels
=
(
pixels
+
delta
).
clamp
(
min
,
max
);
final
double
clampedDelta
=
newPixels
-
pixels
;
if
(
clampedDelta
==
0.0
)
return
delta
;
forcePixels
(
newPixels
);
didUpdateScrollPositionBy
(
clampedDelta
);
return
delta
-
clampedDelta
;
}
@override
ScrollDirection
get
userScrollDirection
=>
coordinator
.
userScrollDirection
;
...
...
@@ -1475,6 +1558,12 @@ class _NestedScrollPosition extends ScrollPosition implements ScrollActivityDele
return
coordinator
.
jumpTo
(
coordinator
.
unnestOffset
(
value
,
this
));
}
@override
void
pointerScroll
(
double
delta
)
{
return
coordinator
.
pointerScroll
(
delta
);
}
@override
void
jumpToWithoutSettling
(
double
value
)
{
assert
(
false
);
...
...
packages/flutter/lib/src/widgets/scroll_position.dart
View file @
60a8b333
...
...
@@ -761,6 +761,22 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
@override
void
jumpTo
(
double
value
);
/// Changes the scrolling position based on a pointer signal from current
/// value to delta without animation and without checking if new value is in
/// range, taking min/max scroll extent into account.
///
/// Any active animation is canceled. If the user is currently scrolling, that
/// action is canceled.
///
/// This method dispatches the start/update/end sequence of scrolling
/// notifications.
///
/// This method is very similar to [jumpTo], but [pointerScroll] will
/// update the [ScrollDirection].
///
// TODO(YeungKC): Support trackpad scroll, https://github.com/flutter/flutter/issues/23604.
void
pointerScroll
(
double
delta
);
/// Calls [jumpTo] if duration is null or [Duration.zero], otherwise
/// [animateTo] is called.
///
...
...
packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart
View file @
60a8b333
...
...
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:math'
as
math
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/physics.dart'
;
import
'package:flutter/rendering.dart'
;
...
...
@@ -202,6 +204,27 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
goBallistic
(
0.0
);
}
@override
void
pointerScroll
(
double
delta
)
{
assert
(
delta
!=
0.0
);
final
double
targetPixels
=
math
.
min
(
math
.
max
(
pixels
+
delta
,
minScrollExtent
),
maxScrollExtent
);
if
(
targetPixels
!=
pixels
)
{
goIdle
();
updateUserScrollDirection
(
-
delta
>
0.0
?
ScrollDirection
.
forward
:
ScrollDirection
.
reverse
);
final
double
oldPixels
=
pixels
;
forcePixels
(
targetPixels
);
didStartScroll
();
didUpdateScrollPositionBy
(
pixels
-
oldPixels
);
didEndScroll
();
goBallistic
(
0.0
);
}
}
@Deprecated
(
'This will lead to bugs.'
)
// ignore: flutter_deprecation_syntax, https://github.com/flutter/flutter/issues/44609
@override
void
jumpToWithoutSettling
(
double
value
)
{
...
...
packages/flutter/lib/src/widgets/scrollable.dart
View file @
60a8b333
...
...
@@ -3,7 +3,6 @@
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:math'
as
math
;
import
'dart:ui'
;
import
'package:flutter/gestures.dart'
;
...
...
@@ -624,9 +623,9 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
// SCROLL WHEEL
// Returns the
offset that should result from applying [event] to the current
//
position, taking min/max scroll extent
into account.
double
_targetScroll
Offset
ForPointerScroll
(
PointerScrollEvent
event
)
{
// Returns the
delta that should result from applying [event] with axis and
//
direction taken
into account.
double
_targetScroll
Delta
ForPointerScroll
(
PointerScrollEvent
event
)
{
double
delta
=
widget
.
axis
==
Axis
.
horizontal
?
event
.
scrollDelta
.
dx
:
event
.
scrollDelta
.
dy
;
...
...
@@ -635,15 +634,14 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
delta
*=
-
1
;
}
return
math
.
min
(
math
.
max
(
position
.
pixels
+
delta
,
position
.
minScrollExtent
),
position
.
maxScrollExtent
);
return
delta
;
}
void
_receivedPointerSignal
(
PointerSignalEvent
event
)
{
if
(
event
is
PointerScrollEvent
&&
_position
!=
null
)
{
final
double
targetScrollOffset
=
_targetScroll
Offset
ForPointerScroll
(
event
);
final
double
targetScrollOffset
=
_targetScroll
Delta
ForPointerScroll
(
event
);
// Only express interest in the event if it would actually result in a scroll.
if
(
targetScrollOffset
!=
position
.
pixels
)
{
if
(
targetScrollOffset
!=
0
)
{
GestureBinding
.
instance
!.
pointerSignalResolver
.
register
(
event
,
_handlePointerScroll
);
}
}
...
...
@@ -654,9 +652,9 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
if
(
_physics
!=
null
&&
!
_physics
!.
shouldAcceptUserOffset
(
position
))
{
return
;
}
final
double
targetScrollOffset
=
_targetScroll
Offset
ForPointerScroll
(
event
as
PointerScrollEvent
);
if
(
targetScrollOffset
!=
position
.
pixels
)
{
position
.
jumpTo
(
targetScrollOffset
);
final
double
targetScrollOffset
=
_targetScroll
Delta
ForPointerScroll
(
event
as
PointerScrollEvent
);
if
(
targetScrollOffset
!=
0
)
{
position
.
pointerScroll
(
targetScrollOffset
);
}
}
...
...
packages/flutter/test/widgets/nested_scroll_view_test.dart
View file @
60a8b333
This diff is collapsed.
Click to expand it.
packages/flutter/test/widgets/scrollable_test.dart
View file @
60a8b333
...
...
@@ -323,6 +323,39 @@ void main() {
expect
(
getScrollOffset
(
tester
),
0.0
);
});
testWidgets
(
'Holding scroll and Scroll pointer signal will update ScrollDirection.forward / ScrollDirection.reverse'
,
(
WidgetTester
tester
)
async
{
ScrollDirection
?
lastUserScrollingDirection
;
final
ScrollController
controller
=
ScrollController
();
await
pumpTest
(
tester
,
TargetPlatform
.
fuchsia
,
controller:
controller
);
controller
.
addListener
(()
{
if
(
controller
.
position
.
userScrollDirection
!=
ScrollDirection
.
idle
)
lastUserScrollingDirection
=
controller
.
position
.
userScrollDirection
;
});
await
tester
.
drag
(
find
.
byType
(
Viewport
),
const
Offset
(
0.0
,
-
20.0
),
touchSlopY:
0.0
);
expect
(
lastUserScrollingDirection
,
ScrollDirection
.
reverse
);
final
Offset
scrollEventLocation
=
tester
.
getCenter
(
find
.
byType
(
Viewport
));
final
TestPointer
testPointer
=
TestPointer
(
1
,
ui
.
PointerDeviceKind
.
mouse
);
// Create a hover event so that |testPointer| has a location when generating the scroll.
testPointer
.
hover
(
scrollEventLocation
);
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
20.0
)));
expect
(
lastUserScrollingDirection
,
ScrollDirection
.
reverse
);
await
tester
.
drag
(
find
.
byType
(
Viewport
),
const
Offset
(
0.0
,
20.0
),
touchSlopY:
0.0
);
expect
(
lastUserScrollingDirection
,
ScrollDirection
.
forward
);
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
20.0
)));
expect
(
lastUserScrollingDirection
,
ScrollDirection
.
forward
);
});
testWidgets
(
'Scrolls in correct direction when scroll axis is reversed'
,
(
WidgetTester
tester
)
async
{
await
pumpTest
(
tester
,
TargetPlatform
.
fuchsia
,
reverse:
true
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment