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
09e400ea
Unverified
Commit
09e400ea
authored
Aug 17, 2022
by
Callum Moffat
Committed by
GitHub
Aug 17, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Don't disable pointer interaction during trackpad scroll (#106890)
parent
9a4a9f7e
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
214 additions
and
7 deletions
+214
-7
nested_scroll_view.dart
packages/flutter/lib/src/widgets/nested_scroll_view.dart
+7
-3
scroll_activity.dart
packages/flutter/lib/src/widgets/scroll_activity.dart
+6
-2
scroll_position_with_single_context.dart
.../lib/src/widgets/scroll_position_with_single_context.dart
+1
-1
scroll_activity_test.dart
packages/flutter/test/widgets/scroll_activity_test.dart
+81
-0
controller.dart
packages/flutter_test/lib/src/controller.dart
+82
-1
test_pointer.dart
packages/flutter_test/lib/src/test_pointer.dart
+37
-0
No files found.
packages/flutter/lib/src/widgets/nested_scroll_view.dart
View file @
09e400ea
...
...
@@ -1382,6 +1382,7 @@ class _NestedScrollPosition extends ScrollPosition implements ScrollActivityDele
metrics
,
simulation
,
context
.
vsync
,
activity
?.
shouldIgnorePointer
??
true
,
);
case
_NestedBallisticScrollActivityMode
.
inner
:
return
_NestedInnerBallisticScrollActivity
(
...
...
@@ -1389,9 +1390,10 @@ class _NestedScrollPosition extends ScrollPosition implements ScrollActivityDele
this
,
simulation
,
context
.
vsync
,
activity
?.
shouldIgnorePointer
??
true
,
);
case
_NestedBallisticScrollActivityMode
.
independent
:
return
BallisticScrollActivity
(
this
,
simulation
,
context
.
vsync
);
return
BallisticScrollActivity
(
this
,
simulation
,
context
.
vsync
,
activity
?.
shouldIgnorePointer
??
true
);
}
}
...
...
@@ -1463,7 +1465,8 @@ class _NestedInnerBallisticScrollActivity extends BallisticScrollActivity {
_NestedScrollPosition
position
,
Simulation
simulation
,
TickerProvider
vsync
,
)
:
super
(
position
,
simulation
,
vsync
);
bool
shouldIgnorePointer
,
)
:
super
(
position
,
simulation
,
vsync
,
shouldIgnorePointer
);
final
_NestedScrollCoordinator
coordinator
;
...
...
@@ -1499,9 +1502,10 @@ class _NestedOuterBallisticScrollActivity extends BallisticScrollActivity {
this
.
metrics
,
Simulation
simulation
,
TickerProvider
vsync
,
bool
shouldIgnorePointer
,
)
:
assert
(
metrics
.
minRange
!=
metrics
.
maxRange
),
assert
(
metrics
.
maxRange
>
metrics
.
minRange
),
super
(
position
,
simulation
,
vsync
);
super
(
position
,
simulation
,
vsync
,
shouldIgnorePointer
);
final
_NestedScrollCoordinator
coordinator
;
final
_NestedScrollMetrics
metrics
;
...
...
packages/flutter/lib/src/widgets/scroll_activity.dart
View file @
09e400ea
...
...
@@ -244,6 +244,7 @@ class ScrollDragController implements Drag {
_lastDetails
=
details
,
_retainMomentum
=
carriedVelocity
!=
null
&&
carriedVelocity
!=
0.0
,
_lastNonStationaryTimestamp
=
details
.
sourceTimeStamp
,
_kind
=
details
.
kind
,
_offsetSinceLastStop
=
motionStartDistanceThreshold
==
null
?
null
:
0.0
;
/// The object that will actuate the scroll view as the user drags.
...
...
@@ -424,6 +425,8 @@ class ScrollDragController implements Drag {
onDragCanceled
?.
call
();
}
/// The type of input device driving the drag.
final
PointerDeviceKind
?
_kind
;
/// The most recently observed [DragStartDetails], [DragUpdateDetails], or
/// [DragEndDetails] object.
dynamic
get
lastDetails
=>
_lastDetails
;
...
...
@@ -483,7 +486,7 @@ class DragScrollActivity extends ScrollActivity {
}
@override
bool
get
shouldIgnorePointer
=>
true
;
bool
get
shouldIgnorePointer
=>
_controller
?.
_kind
!=
PointerDeviceKind
.
trackpad
;
@override
bool
get
isScrolling
=>
true
;
...
...
@@ -526,6 +529,7 @@ class BallisticScrollActivity extends ScrollActivity {
super
.
delegate
,
Simulation
simulation
,
TickerProvider
vsync
,
this
.
shouldIgnorePointer
,
)
{
_controller
=
AnimationController
.
unbounded
(
debugLabel:
kDebugMode
?
objectRuntimeType
(
this
,
'BallisticScrollActivity'
)
:
null
,
...
...
@@ -576,7 +580,7 @@ class BallisticScrollActivity extends ScrollActivity {
}
@override
bool
get
shouldIgnorePointer
=>
true
;
final
bool
shouldIgnorePointer
;
@override
bool
get
isScrolling
=>
true
;
...
...
packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart
View file @
09e400ea
...
...
@@ -141,7 +141,7 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
assert
(
hasPixels
);
final
Simulation
?
simulation
=
physics
.
createBallisticSimulation
(
this
,
velocity
);
if
(
simulation
!=
null
)
{
beginActivity
(
BallisticScrollActivity
(
this
,
simulation
,
context
.
vsync
));
beginActivity
(
BallisticScrollActivity
(
this
,
simulation
,
context
.
vsync
,
activity
?.
shouldIgnorePointer
??
true
));
}
else
{
goIdle
();
}
...
...
packages/flutter/test/widgets/scroll_activity_test.dart
View file @
09e400ea
...
...
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/gestures.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
...
...
@@ -127,6 +128,86 @@ void main() {
await
tester
.
pump
();
expect
(
find
.
text
(
'Page 9'
),
findsOneWidget
);
});
testWidgets
(
'Pointer is not ignored during trackpad scrolling.'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
controller
=
ScrollController
();
int
?
lastTapped
;
int
?
lastHovered
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
ListView
(
controller:
controller
,
children:
List
<
Widget
>.
generate
(
30
,
(
int
i
)
{
return
SizedBox
(
height:
100.0
,
child:
MouseRegion
(
onHover:
(
PointerHoverEvent
event
)
{
lastHovered
=
i
;
},
child:
GestureDetector
(
onTap:
()
{
lastTapped
=
i
;
},
child:
Text
(
'
$i
'
)
)
));
})
)
));
final
TestGesture
touchGesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
touch
);
// ignore: avoid_redundant_argument_values
// Try mouse hovering while scrolling by touch
await
touchGesture
.
down
(
tester
.
getCenter
(
find
.
byType
(
ListView
)));
await
tester
.
pump
();
await
touchGesture
.
moveBy
(
const
Offset
(
0
,
200
));
await
tester
.
pump
();
final
TestGesture
hoverGesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
hoverGesture
.
addPointer
(
location:
tester
.
getCenter
(
find
.
text
(
'3'
))
);
await
hoverGesture
.
moveBy
(
const
Offset
(
1
,
1
));
await
hoverGesture
.
removePointer
(
location:
tester
.
getCenter
(
find
.
text
(
'3'
))
);
await
tester
.
pumpAndSettle
();
expect
(
controller
.
position
.
activity
?.
shouldIgnorePointer
,
isTrue
);
// Pointer is ignored for touch scrolling.
expect
(
lastHovered
,
isNull
);
await
touchGesture
.
up
();
await
tester
.
pump
();
// Try mouse clicking during inertia after scrolling by touch
await
tester
.
fling
(
find
.
byType
(
ListView
),
const
Offset
(
0
,
-
200
),
1000
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
controller
.
position
.
activity
?.
shouldIgnorePointer
,
isTrue
);
// Pointer is ignored following touch scrolling.
await
tester
.
tap
(
find
.
text
(
'3'
),
warnIfMissed:
false
);
expect
(
lastTapped
,
isNull
);
await
tester
.
pumpAndSettle
();
controller
.
jumpTo
(
0
);
await
tester
.
pump
();
final
TestGesture
trackpadGesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
trackpad
);
// Try mouse hovering while scrolling with a trackpad
await
trackpadGesture
.
panZoomStart
(
tester
.
getCenter
(
find
.
byType
(
ListView
)));
await
tester
.
pump
();
await
trackpadGesture
.
panZoomUpdate
(
tester
.
getCenter
(
find
.
byType
(
ListView
)),
pan:
const
Offset
(
0
,
200
));
await
tester
.
pump
();
await
hoverGesture
.
addPointer
(
location:
tester
.
getCenter
(
find
.
text
(
'3'
))
);
await
hoverGesture
.
moveBy
(
const
Offset
(
1
,
1
));
await
hoverGesture
.
removePointer
(
location:
tester
.
getCenter
(
find
.
text
(
'3'
))
);
await
tester
.
pumpAndSettle
();
expect
(
controller
.
position
.
activity
?.
shouldIgnorePointer
,
isFalse
);
// Pointer is not ignored for trackpad scrolling.
expect
(
lastHovered
,
equals
(
3
));
await
trackpadGesture
.
panZoomEnd
();
await
tester
.
pump
();
// Try mouse clicking during inertia after scrolling with a trackpad
await
tester
.
trackpadFling
(
find
.
byType
(
ListView
),
const
Offset
(
0
,
-
200
),
1000
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
controller
.
position
.
activity
?.
shouldIgnorePointer
,
isFalse
);
// Pointer is not ignored following trackpad scrolling.
await
tester
.
tap
(
find
.
text
(
'3'
));
expect
(
lastTapped
,
equals
(
3
));
await
tester
.
pumpAndSettle
();
});
}
class
PageView62209
extends
StatefulWidget
{
...
...
packages/flutter_test/lib/src/controller.dart
View file @
09e400ea
...
...
@@ -388,6 +388,7 @@ abstract class WidgetController {
Offset
initialOffset
=
Offset
.
zero
,
Duration
initialOffsetDelay
=
const
Duration
(
seconds:
1
),
bool
warnIfMissed
=
true
,
PointerDeviceKind
deviceKind
=
PointerDeviceKind
.
touch
,
})
{
return
flingFrom
(
getCenter
(
finder
,
warnIfMissed:
warnIfMissed
,
callee:
'fling'
),
...
...
@@ -398,6 +399,7 @@ abstract class WidgetController {
frameInterval:
frameInterval
,
initialOffset:
initialOffset
,
initialOffsetDelay:
initialOffsetDelay
,
deviceKind:
deviceKind
,
);
}
...
...
@@ -417,11 +419,12 @@ abstract class WidgetController {
Duration
frameInterval
=
const
Duration
(
milliseconds:
16
),
Offset
initialOffset
=
Offset
.
zero
,
Duration
initialOffsetDelay
=
const
Duration
(
seconds:
1
),
PointerDeviceKind
deviceKind
=
PointerDeviceKind
.
touch
,
})
{
assert
(
offset
.
distance
>
0.0
);
assert
(
speed
>
0.0
);
// speed is pixels/second
return
TestAsyncUtils
.
guard
<
void
>(()
async
{
final
TestPointer
testPointer
=
TestPointer
(
pointer
??
_getNextPointer
(),
PointerDeviceKind
.
touch
,
null
,
buttons
);
final
TestPointer
testPointer
=
TestPointer
(
pointer
??
_getNextPointer
(),
deviceKind
,
null
,
buttons
);
const
int
kMoveCount
=
50
;
// Needs to be >= kHistorySize, see _LeastSquaresVelocityTrackerStrategy
final
double
timeStampDelta
=
1000000.0
*
offset
.
distance
/
(
kMoveCount
*
speed
);
double
timeStamp
=
0.0
;
...
...
@@ -445,6 +448,84 @@ abstract class WidgetController {
});
}
/// Attempts a trackpad fling gesture starting from the center of the given
/// widget, moving the given distance, reaching the given speed. A trackpad
/// fling sends PointerPanZoom events instead of a sequence of touch events.
///
/// {@macro flutter.flutter_test.WidgetController.tap.warnIfMissed}
///
/// {@macro flutter.flutter_test.WidgetController.fling}
///
/// A fling is essentially a drag that ends at a particular speed. If you
/// just want to drag and end without a fling, use [drag].
Future
<
void
>
trackpadFling
(
Finder
finder
,
Offset
offset
,
double
speed
,
{
int
?
pointer
,
int
buttons
=
kPrimaryButton
,
Duration
frameInterval
=
const
Duration
(
milliseconds:
16
),
Offset
initialOffset
=
Offset
.
zero
,
Duration
initialOffsetDelay
=
const
Duration
(
seconds:
1
),
bool
warnIfMissed
=
true
,
})
{
return
trackpadFlingFrom
(
getCenter
(
finder
,
warnIfMissed:
warnIfMissed
,
callee:
'fling'
),
offset
,
speed
,
pointer:
pointer
,
buttons:
buttons
,
frameInterval:
frameInterval
,
initialOffset:
initialOffset
,
initialOffsetDelay:
initialOffsetDelay
,
);
}
/// Attempts a fling gesture starting from the given location, moving the
/// given distance, reaching the given speed. A trackpad fling sends
/// PointerPanZoom events instead of a sequence of touch events.
///
/// {@macro flutter.flutter_test.WidgetController.fling}
///
/// A fling is essentially a drag that ends at a particular speed. If you
/// just want to drag and end without a fling, use [dragFrom].
Future
<
void
>
trackpadFlingFrom
(
Offset
startLocation
,
Offset
offset
,
double
speed
,
{
int
?
pointer
,
int
buttons
=
kPrimaryButton
,
Duration
frameInterval
=
const
Duration
(
milliseconds:
16
),
Offset
initialOffset
=
Offset
.
zero
,
Duration
initialOffsetDelay
=
const
Duration
(
seconds:
1
),
})
{
assert
(
offset
.
distance
>
0.0
);
assert
(
speed
>
0.0
);
// speed is pixels/second
return
TestAsyncUtils
.
guard
<
void
>(()
async
{
final
TestPointer
testPointer
=
TestPointer
(
pointer
??
_getNextPointer
(),
PointerDeviceKind
.
trackpad
,
null
,
buttons
);
const
int
kMoveCount
=
50
;
// Needs to be >= kHistorySize, see _LeastSquaresVelocityTrackerStrategy
final
double
timeStampDelta
=
1000000.0
*
offset
.
distance
/
(
kMoveCount
*
speed
);
double
timeStamp
=
0.0
;
double
lastTimeStamp
=
timeStamp
;
await
sendEventToBinding
(
testPointer
.
panZoomStart
(
startLocation
,
timeStamp:
Duration
(
microseconds:
timeStamp
.
round
())));
if
(
initialOffset
.
distance
>
0.0
)
{
await
sendEventToBinding
(
testPointer
.
panZoomUpdate
(
startLocation
,
pan:
initialOffset
,
timeStamp:
Duration
(
microseconds:
timeStamp
.
round
())));
timeStamp
+=
initialOffsetDelay
.
inMicroseconds
;
await
pump
(
initialOffsetDelay
);
}
for
(
int
i
=
0
;
i
<=
kMoveCount
;
i
+=
1
)
{
final
Offset
pan
=
initialOffset
+
Offset
.
lerp
(
Offset
.
zero
,
offset
,
i
/
kMoveCount
)!;
await
sendEventToBinding
(
testPointer
.
panZoomUpdate
(
startLocation
,
pan:
pan
,
timeStamp:
Duration
(
microseconds:
timeStamp
.
round
())));
timeStamp
+=
timeStampDelta
;
if
(
timeStamp
-
lastTimeStamp
>
frameInterval
.
inMicroseconds
)
{
await
pump
(
Duration
(
microseconds:
(
timeStamp
-
lastTimeStamp
).
truncate
()));
lastTimeStamp
=
timeStamp
;
}
}
await
sendEventToBinding
(
testPointer
.
panZoomEnd
(
timeStamp:
Duration
(
microseconds:
timeStamp
.
round
())));
});
}
/// A simulator of how the framework handles a series of [PointerEvent]s
/// received from the Flutter engine.
///
...
...
packages/flutter_test/lib/src/test_pointer.dart
View file @
09e400ea
...
...
@@ -530,6 +530,43 @@ class TestGesture {
assert
(!
_pointer
.
_isDown
);
});
}
/// Dispatch a pointer pan zoom start event at the given `location`, caching the
/// hit test result.
Future
<
void
>
panZoomStart
(
Offset
location
,
{
Duration
timeStamp
=
Duration
.
zero
})
async
{
return
TestAsyncUtils
.
guard
<
void
>(()
async
{
return
_dispatcher
(
_pointer
.
panZoomStart
(
location
,
timeStamp:
timeStamp
));
});
}
/// Dispatch a pointer pan zoom update event at the given `location`, caching the
/// hit test result.
Future
<
void
>
panZoomUpdate
(
Offset
location
,
{
Offset
pan
=
Offset
.
zero
,
double
scale
=
1
,
double
rotation
=
0
,
Duration
timeStamp
=
Duration
.
zero
})
async
{
return
TestAsyncUtils
.
guard
<
void
>(()
async
{
return
_dispatcher
(
_pointer
.
panZoomUpdate
(
location
,
pan:
pan
,
scale:
scale
,
rotation:
rotation
,
timeStamp:
timeStamp
));
});
}
/// Dispatch a pointer pan zoom end event, caching the hit test result.
Future
<
void
>
panZoomEnd
({
Duration
timeStamp
=
Duration
.
zero
})
async
{
return
TestAsyncUtils
.
guard
<
void
>(()
async
{
return
_dispatcher
(
_pointer
.
panZoomEnd
(
timeStamp:
timeStamp
));
});
}
}
/// A record of input [PointerEvent] list with the timeStamp of when it is
...
...
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