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
Hide 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
...
@@ -1072,6 +1072,63 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
goBallistic
(
0.0
);
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
@override
double
setPixels
(
double
newPixels
)
{
double
setPixels
(
double
newPixels
)
{
assert
(
false
);
assert
(
false
);
...
@@ -1386,6 +1443,32 @@ class _NestedScrollPosition extends ScrollPosition implements ScrollActivityDele
...
@@ -1386,6 +1443,32 @@ class _NestedScrollPosition extends ScrollPosition implements ScrollActivityDele
return
0.0
;
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
@override
ScrollDirection
get
userScrollDirection
=>
coordinator
.
userScrollDirection
;
ScrollDirection
get
userScrollDirection
=>
coordinator
.
userScrollDirection
;
...
@@ -1475,6 +1558,12 @@ class _NestedScrollPosition extends ScrollPosition implements ScrollActivityDele
...
@@ -1475,6 +1558,12 @@ class _NestedScrollPosition extends ScrollPosition implements ScrollActivityDele
return
coordinator
.
jumpTo
(
coordinator
.
unnestOffset
(
value
,
this
));
return
coordinator
.
jumpTo
(
coordinator
.
unnestOffset
(
value
,
this
));
}
}
@override
void
pointerScroll
(
double
delta
)
{
return
coordinator
.
pointerScroll
(
delta
);
}
@override
@override
void
jumpToWithoutSettling
(
double
value
)
{
void
jumpToWithoutSettling
(
double
value
)
{
assert
(
false
);
assert
(
false
);
...
...
packages/flutter/lib/src/widgets/scroll_position.dart
View file @
60a8b333
...
@@ -761,6 +761,22 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
...
@@ -761,6 +761,22 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
@override
@override
void
jumpTo
(
double
value
);
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
/// Calls [jumpTo] if duration is null or [Duration.zero], otherwise
/// [animateTo] is called.
/// [animateTo] is called.
///
///
...
...
packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart
View file @
60a8b333
...
@@ -2,6 +2,8 @@
...
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'dart:math'
as
math
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/physics.dart'
;
import
'package:flutter/physics.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/rendering.dart'
;
...
@@ -202,6 +204,27 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
...
@@ -202,6 +204,27 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
goBallistic
(
0.0
);
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
@Deprecated
(
'This will lead to bugs.'
)
// ignore: flutter_deprecation_syntax, https://github.com/flutter/flutter/issues/44609
@override
@override
void
jumpToWithoutSettling
(
double
value
)
{
void
jumpToWithoutSettling
(
double
value
)
{
...
...
packages/flutter/lib/src/widgets/scrollable.dart
View file @
60a8b333
...
@@ -3,7 +3,6 @@
...
@@ -3,7 +3,6 @@
// found in the LICENSE file.
// found in the LICENSE file.
import
'dart:async'
;
import
'dart:async'
;
import
'dart:math'
as
math
;
import
'dart:ui'
;
import
'dart:ui'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/gestures.dart'
;
...
@@ -624,9 +623,9 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
...
@@ -624,9 +623,9 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
// SCROLL WHEEL
// SCROLL WHEEL
// Returns the
offset that should result from applying [event] to the current
// Returns the
delta that should result from applying [event] with axis and
//
position, taking min/max scroll extent
into account.
//
direction taken
into account.
double
_targetScroll
Offset
ForPointerScroll
(
PointerScrollEvent
event
)
{
double
_targetScroll
Delta
ForPointerScroll
(
PointerScrollEvent
event
)
{
double
delta
=
widget
.
axis
==
Axis
.
horizontal
double
delta
=
widget
.
axis
==
Axis
.
horizontal
?
event
.
scrollDelta
.
dx
?
event
.
scrollDelta
.
dx
:
event
.
scrollDelta
.
dy
;
:
event
.
scrollDelta
.
dy
;
...
@@ -635,15 +634,14 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
...
@@ -635,15 +634,14 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
delta
*=
-
1
;
delta
*=
-
1
;
}
}
return
math
.
min
(
math
.
max
(
position
.
pixels
+
delta
,
position
.
minScrollExtent
),
return
delta
;
position
.
maxScrollExtent
);
}
}
void
_receivedPointerSignal
(
PointerSignalEvent
event
)
{
void
_receivedPointerSignal
(
PointerSignalEvent
event
)
{
if
(
event
is
PointerScrollEvent
&&
_position
!=
null
)
{
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.
// 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
);
GestureBinding
.
instance
!.
pointerSignalResolver
.
register
(
event
,
_handlePointerScroll
);
}
}
}
}
...
@@ -654,9 +652,9 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
...
@@ -654,9 +652,9 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
if
(
_physics
!=
null
&&
!
_physics
!.
shouldAcceptUserOffset
(
position
))
{
if
(
_physics
!=
null
&&
!
_physics
!.
shouldAcceptUserOffset
(
position
))
{
return
;
return
;
}
}
final
double
targetScrollOffset
=
_targetScroll
Offset
ForPointerScroll
(
event
as
PointerScrollEvent
);
final
double
targetScrollOffset
=
_targetScroll
Delta
ForPointerScroll
(
event
as
PointerScrollEvent
);
if
(
targetScrollOffset
!=
position
.
pixels
)
{
if
(
targetScrollOffset
!=
0
)
{
position
.
jumpTo
(
targetScrollOffset
);
position
.
pointerScroll
(
targetScrollOffset
);
}
}
}
}
...
...
packages/flutter/test/widgets/nested_scroll_view_test.dart
View file @
60a8b333
...
@@ -2,11 +2,13 @@
...
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'dart:ui'
as
ui
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/gestures.dart'
show
DragStartBehavior
;
import
'package:flutter/gestures.dart'
show
DragStartBehavior
;
import
'package:flutter/material.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'../rendering/rendering_tester.dart'
;
import
'../rendering/rendering_tester.dart'
;
...
@@ -1430,6 +1432,123 @@ void main() {
...
@@ -1430,6 +1432,123 @@ void main() {
verifyGeometry
(
key:
appBarKey
,
paintExtent:
200.0
,
visible:
true
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
200.0
,
visible:
true
);
});
});
testWidgets
(
'float with pointer signal'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
appBarKey
=
GlobalKey
();
await
tester
.
pumpWidget
(
buildFloatTest
(
floating:
true
,
nestedFloat:
true
,
appBarKey:
appBarKey
,
));
final
Offset
scrollEventLocation
=
tester
.
getCenter
(
find
.
byType
(
NestedScrollView
));
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
);
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
56.0
,
visible:
true
);
// Scroll away the outer scroll view and some of the inner scroll view.
// We will not scroll back the same amount to indicate that we are
// floating in before reaching the top of the inner scrollable.
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
300.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsNothing
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
0.0
,
visible:
false
);
// The outer scrollable should float back in, inner should not change
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
50.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
50.0
,
visible:
true
);
// Float the rest of the way in.
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
150.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
56.0
,
visible:
true
);
});
testWidgets
(
'float expanded with pointer signal'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
appBarKey
=
GlobalKey
();
await
tester
.
pumpWidget
(
buildFloatTest
(
floating:
true
,
nestedFloat:
true
,
expanded:
true
,
appBarKey:
appBarKey
,
));
final
Offset
scrollEventLocation
=
tester
.
getCenter
(
find
.
byType
(
NestedScrollView
));
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
);
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
200.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
200.0
,
visible:
true
);
// Scroll away the outer scroll view and some of the inner scroll view.
// We will not scroll back the same amount to indicate that we are
// floating in before reaching the top of the inner scrollable.
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
300.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsNothing
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
0.0
,
visible:
false
);
// The outer scrollable should float back in, inner should not change
// On initial float in, the app bar is collapsed.
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
50.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
50.0
,
visible:
true
);
// The inner scrollable should receive leftover delta after the outer has
// been scrolled back in fully.
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
200.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
200.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
200.0
,
visible:
true
);
});
testWidgets
(
'only snap'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'only snap'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
appBarKey
=
GlobalKey
();
final
GlobalKey
appBarKey
=
GlobalKey
();
final
GlobalKey
<
NestedScrollViewState
>
nestedKey
=
GlobalKey
();
final
GlobalKey
<
NestedScrollViewState
>
nestedKey
=
GlobalKey
();
...
@@ -1814,6 +1933,130 @@ void main() {
...
@@ -1814,6 +1933,130 @@ void main() {
);
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
200.0
,
visible:
true
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
200.0
,
visible:
true
);
});
});
testWidgets
(
'float pinned with pointer signal'
,
(
WidgetTester
tester
)
async
{
// This configuration should have the same behavior of a pinned app bar.
// No floating should happen, and the app bar should persist.
final
GlobalKey
appBarKey
=
GlobalKey
();
await
tester
.
pumpWidget
(
buildFloatTest
(
floating:
true
,
pinned:
true
,
nestedFloat:
true
,
appBarKey:
appBarKey
,
));
final
Offset
scrollEventLocation
=
tester
.
getCenter
(
find
.
byType
(
NestedScrollView
));
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
);
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
56.0
,
visible:
true
);
// Scroll away the outer scroll view and some of the inner scroll view.
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
300.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
56.0
,
visible:
true
);
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
50.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
56.0
,
visible:
true
);
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
150.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
56.0
,
visible:
true
);
});
testWidgets
(
'float pinned expanded with pointer signal'
,
(
WidgetTester
tester
)
async
{
// Only the expanded portion (flexible space) of the app bar should float
// in and out.
final
GlobalKey
appBarKey
=
GlobalKey
();
await
tester
.
pumpWidget
(
buildFloatTest
(
floating:
true
,
pinned:
true
,
expanded:
true
,
nestedFloat:
true
,
appBarKey:
appBarKey
,
));
final
Offset
scrollEventLocation
=
tester
.
getCenter
(
find
.
byType
(
NestedScrollView
));
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
);
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
200.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
200.0
,
visible:
true
);
// Scroll away the outer scroll view and some of the inner scroll view.
// The expanded portion of the app bar should collapse.
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
300.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
56.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
56.0
,
visible:
true
);
// Scroll back some, the app bar should expand.
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
50.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsNothing
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
106.0
,
// 56.0 + 50.0
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
106.0
,
visible:
true
);
// Finish scrolling the rest of the way in.
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
150.0
)));
await
tester
.
pump
();
expect
(
find
.
text
(
'Test Title'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 1'
),
findsOneWidget
);
expect
(
find
.
text
(
'Item 5'
),
findsOneWidget
);
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
200.0
,
);
verifyGeometry
(
key:
appBarKey
,
paintExtent:
200.0
,
visible:
true
);
});
});
});
group
(
'Correctly handles 0 velocity inner ballistic scroll activity:'
,
()
{
group
(
'Correctly handles 0 velocity inner ballistic scroll activity:'
,
()
{
...
@@ -1961,6 +2204,105 @@ void main() {
...
@@ -1961,6 +2204,105 @@ void main() {
);
);
expect
(
nestedScrollView
.
currentState
!.
innerController
.
position
.
pixels
,
295.0
);
expect
(
nestedScrollView
.
currentState
!.
innerController
.
position
.
pixels
,
295.0
);
},
variant:
const
TargetPlatformVariant
(<
TargetPlatform
>{
TargetPlatform
.
iOS
,
TargetPlatform
.
macOS
}));
},
variant:
const
TargetPlatformVariant
(<
TargetPlatform
>{
TargetPlatform
.
iOS
,
TargetPlatform
.
macOS
}));
testWidgets
(
'Scroll pointer signal should not cause overscroll.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
await
tester
.
pumpWidget
(
buildTest
(
controller:
controller
));
final
Offset
scrollEventLocation
=
tester
.
getCenter
(
find
.
byType
(
NestedScrollView
));
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
(
controller
.
offset
,
20
);
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
-
40.0
)));
expect
(
controller
.
offset
,
0
);
await
tester
.
tap
(
find
.
text
(
'DD'
));
await
tester
.
pumpAndSettle
();
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
1000000.0
)));
expect
(
find
.
text
(
'ddd1'
),
findsOneWidget
);
});
testWidgets
(
'NestedScrollView basic scroll with pointer signal'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
buildTest
());
expect
(
find
.
text
(
'aaa2'
),
findsOneWidget
);
expect
(
find
.
text
(
'aaa3'
),
findsNothing
);
expect
(
find
.
text
(
'bbb1'
),
findsNothing
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
250
));
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
200.0
,
);
// Regression test for https://github.com/flutter/flutter/issues/55362
final
TestPointer
testPointer
=
TestPointer
(
1
,
ui
.
PointerDeviceKind
.
mouse
);
// The offset is the responsibility of innerPosition.
testPointer
.
hover
(
const
Offset
(
0
,
201
));
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
20.0
)));
await
tester
.
pump
(
const
Duration
(
milliseconds:
250
));
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
180.0
,
);
testPointer
.
hover
(
const
Offset
(
0
,
179
));
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
20.0
)));
await
tester
.
pump
(
const
Duration
(
milliseconds:
250
));
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
160.0
,
);
await
tester
.
sendEventToBinding
(
testPointer
.
scroll
(
const
Offset
(
0.0
,
20.0
)));
await
tester
.
pump
(
const
Duration
(
milliseconds:
250
));
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
AppBar
)).
size
.
height
,
140.0
,
);
});
// Related to https://github.com/flutter/flutter/issues/64266
testWidgets
(
'Holding scroll and Scroll pointer signal will update ScrollDirection.forward / ScrollDirection.reverse'
,
(
WidgetTester
tester
)
async
{
ScrollDirection
?
lastUserScrollingDirection
;
final
ScrollController
controller
=
ScrollController
();
await
tester
.
pumpWidget
(
buildTest
(
controller:
controller
));
controller
.
addListener
(()
{
if
(
controller
.
position
.
userScrollDirection
!=
ScrollDirection
.
idle
)
lastUserScrollingDirection
=
controller
.
position
.
userScrollDirection
;
});
await
tester
.
drag
(
find
.
byType
(
NestedScrollView
),
const
Offset
(
0.0
,
-
20.0
),
touchSlopY:
0.0
);
expect
(
lastUserScrollingDirection
,
ScrollDirection
.
reverse
);
final
Offset
scrollEventLocation
=
tester
.
getCenter
(
find
.
byType
(
NestedScrollView
));
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
(
NestedScrollView
),
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
);
});
}
}
class
TestHeader
extends
SliverPersistentHeaderDelegate
{
class
TestHeader
extends
SliverPersistentHeaderDelegate
{
...
...
packages/flutter/test/widgets/scrollable_test.dart
View file @
60a8b333
...
@@ -323,6 +323,39 @@ void main() {
...
@@ -323,6 +323,39 @@ void main() {
expect
(
getScrollOffset
(
tester
),
0.0
);
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
{
testWidgets
(
'Scrolls in correct direction when scroll axis is reversed'
,
(
WidgetTester
tester
)
async
{
await
pumpTest
(
tester
,
TargetPlatform
.
fuchsia
,
reverse:
true
);
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