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
3d8aec2b
Unverified
Commit
3d8aec2b
authored
Dec 20, 2018
by
jslavitz
Committed by
GitHub
Dec 20, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adds force press gesture detector and recognizer (#24554)
* adds Force Press gesture detector and recognizer
parent
bbddade1
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
1027 additions
and
2 deletions
+1027
-2
gestures.dart
packages/flutter/lib/gestures.dart
+1
-0
events.dart
packages/flutter/lib/src/gestures/events.dart
+10
-2
force_press.dart
packages/flutter/lib/src/gestures/force_press.dart
+308
-0
gesture_detector.dart
packages/flutter/lib/src/widgets/gesture_detector.dart
+55
-0
force_press_test.dart
packages/flutter/test/gestures/force_press_test.dart
+452
-0
gesture_detector_test.dart
packages/flutter/test/widgets/gesture_detector_test.dart
+142
-0
test_pointer.dart
packages/flutter_test/lib/src/test_pointer.dart
+59
-0
No files found.
packages/flutter/lib/gestures.dart
View file @
3d8aec2b
...
...
@@ -16,6 +16,7 @@ export 'src/gestures/drag.dart';
export
'src/gestures/drag_details.dart'
;
export
'src/gestures/eager.dart'
;
export
'src/gestures/events.dart'
;
export
'src/gestures/force_press.dart'
;
export
'src/gestures/hit_test.dart'
;
export
'src/gestures/long_press.dart'
;
export
'src/gestures/lsq_solver.dart'
;
...
...
packages/flutter/lib/src/gestures/events.dart
View file @
3d8aec2b
...
...
@@ -334,6 +334,7 @@ class PointerAddedEvent extends PointerEvent {
int
device
=
0
,
Offset
position
=
Offset
.
zero
,
bool
obscured
=
false
,
double
pressure
=
0.0
,
double
pressureMin
=
1.0
,
double
pressureMax
=
1.0
,
double
distance
=
0.0
,
...
...
@@ -348,6 +349,7 @@ class PointerAddedEvent extends PointerEvent {
device:
device
,
position:
position
,
obscured:
obscured
,
pressure:
pressure
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
,
distance:
distance
,
...
...
@@ -372,6 +374,7 @@ class PointerRemovedEvent extends PointerEvent {
PointerDeviceKind
kind
=
PointerDeviceKind
.
touch
,
int
device
=
0
,
bool
obscured
=
false
,
double
pressure
=
0.0
,
double
pressureMin
=
1.0
,
double
pressureMax
=
1.0
,
double
distanceMax
=
0.0
,
...
...
@@ -383,11 +386,12 @@ class PointerRemovedEvent extends PointerEvent {
device:
device
,
position:
null
,
obscured:
obscured
,
pressure:
pressure
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
,
distanceMax:
distanceMax
,
radiusMin:
radiusMin
,
radiusMax:
radiusMax
radiusMax:
radiusMax
,
);
}
...
...
@@ -410,6 +414,7 @@ class PointerHoverEvent extends PointerEvent {
Offset
delta
=
Offset
.
zero
,
int
buttons
=
0
,
bool
obscured
=
false
,
double
pressure
=
0.0
,
double
pressureMin
=
1.0
,
double
pressureMax
=
1.0
,
double
distance
=
0.0
,
...
...
@@ -431,6 +436,7 @@ class PointerHoverEvent extends PointerEvent {
buttons:
buttons
,
down:
false
,
obscured:
obscured
,
pressure:
pressure
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
,
distance:
distance
,
...
...
@@ -567,7 +573,7 @@ class PointerUpEvent extends PointerEvent {
Offset
position
=
Offset
.
zero
,
int
buttons
=
0
,
bool
obscured
=
false
,
double
pressure
=
1
.0
,
double
pressure
=
0
.0
,
double
pressureMin
=
1.0
,
double
pressureMax
=
1.0
,
double
distance
=
0.0
,
...
...
@@ -616,6 +622,7 @@ class PointerCancelEvent extends PointerEvent {
Offset
position
=
Offset
.
zero
,
int
buttons
=
0
,
bool
obscured
=
false
,
double
pressure
=
0.0
,
double
pressureMin
=
1.0
,
double
pressureMax
=
1.0
,
double
distance
=
0.0
,
...
...
@@ -636,6 +643,7 @@ class PointerCancelEvent extends PointerEvent {
buttons:
buttons
,
down:
false
,
obscured:
obscured
,
pressure:
pressure
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
,
distance:
distance
,
...
...
packages/flutter/lib/src/gestures/force_press.dart
0 → 100644
View file @
3d8aec2b
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:ui'
show
Offset
;
import
'package:flutter/foundation.dart'
;
import
'arena.dart'
;
import
'constants.dart'
;
import
'events.dart'
;
import
'recognizer.dart'
;
enum
_ForceState
{
// No pointer has touched down and the detector is ready for a pointer down to occur.
ready
,
// A pointer has touched down, but a force press gesture has not yet been detected.
possible
,
// A pointer is down and a force press gesture has been detected. However, if
// the ForcePressGestureRecognizer is the only recognizer in the arena, thus
// accepted as soon as the gesture state is possible, the gesture will not
// yet have started.
accepted
,
// A pointer is down and the gesture has started, ie. the pressure of the pointer
// has just become greater than the ForcePressGestureRecognizer.startPressure.
started
,
// A pointer is down and the pressure of the pointer has just become greater
// than the ForcePressGestureRecognizer.peakPressure. Even after a pointer
// crosses this threshold, onUpdate callbacks will still be sent.
peaked
,
}
/// Details object for callbacks that use [GestureForcePressStartCallback],
/// [GestureForcePressPeakCallback], [GestureForcePressEndCallback] or
/// [GestureForcePressUpdateCallback].
///
/// See also:
///
/// * [ForcePressGestureRecognizer.onStart], [ForcePressGestureRecognizer.onPeak],
/// [ForcePressGestureRecognizer.onEnd], and [ForcePressGestureRecognizer.onUpdate]
/// which use [ForcePressDetails].
/// * [ForcePressUpdateDetails], the details for [ForcePressUpdateCallback].
class
ForcePressDetails
{
/// Creates details for a [GestureForcePressStartCallback],
/// [GestureForcePressPeakCallback] or [GestureForcePressEndCallback].
///
/// The [globalPosition] argument must not be null.
ForcePressDetails
({
@required
this
.
globalPosition
,
@required
this
.
pressure
,
})
:
assert
(
globalPosition
!=
null
),
assert
(
pressure
!=
null
);
/// The global position at which the function was called.
final
Offset
globalPosition
;
/// The pressure of the pointer on the screen.
final
double
pressure
;
}
/// Signature used by a [ForcePressGestureRecognizer] for when a pointer has
/// pressed with at least [ForcePressGestureRecognizer.startPressure].
typedef
GestureForcePressStartCallback
=
void
Function
(
ForcePressDetails
details
);
/// Signature used by [ForcePressGestureRecognizer] for when a pointer that has
/// pressed with at least [ForcePressGestureRecognizer.peakPressure].
typedef
GestureForcePressPeakCallback
=
void
Function
(
ForcePressDetails
details
);
/// Signature used by [ForcePressGestureRecognizer] during the frames
/// after the triggering of a [ForcePressGestureRecognizer.onStart] callback.
typedef
GestureForcePressUpdateCallback
=
void
Function
(
ForcePressDetails
details
);
/// Signature for when the pointer that previously triggered a
/// [ForcePressGestureRecognizer.onStart] callback is no longer in contact
/// with the screen.
typedef
GestureForcePressEndCallback
=
void
Function
(
ForcePressDetails
details
);
/// Signature used by [ForcePressGestureRecognizer] for interpolating the raw
/// device pressure to a value in the range [0, 1] given the device's pressure
/// min and pressure max.
typedef
GestureForceInterpolation
=
double
Function
(
double
pressureMin
,
double
pressureMax
,
double
pressure
);
/// Recognizes a force press on devices that have force sensors.
///
/// Only the force from a single pointer is used to invoke events. A tap
/// recognizer will win against this recognizer on pointer up as long as the
/// pointer has not pressed with a force greater than
/// [ForcePressGestureRecognizer.startPressure]. A long press recognizer will
/// win when the press down time exceeds the threshold time as long as the
/// pointer's pressure was never greater than
/// [ForcePressGestureRecognizer.startPressure] in that duration.
///
/// As of November, 2018 iPhone devices of generation 6S and higher have
/// force touch functionality, with the exception of the iPhone XR. In addition,
/// a small handful of Android devices have this functionality as well.
///
/// Reported pressure will always be in the range [0.0, 1.0], where 1.0 is
/// maximum pressure and 0.0 is minimum pressure. If using a non-linear
/// interpolation equation, the pressure reported will correspond with the
/// custom curve. (ie. if the interpolation maps t(0.5) -> 0.1, a value of 0.1
/// will be reported at a device pressure value of 0.5).
///
class
ForcePressGestureRecognizer
extends
OneSequenceGestureRecognizer
{
/// Creates a force press gesture recognizer.
///
/// The [startPressure] defaults to 0.4, and [peakPressure] defaults to 0.85
/// where a value of 0.0 is no pressure and a value of 1.0 is maximum pressure.
///
/// [startPressure], [peakPressure] and [interpolation] must not be null.
/// [peakPressure] must be greater than [startPressure]. [interpolation] must
/// always return a value in the range [0.0, 1.0] where
/// pressureMin <= pressure <= pressureMax.
ForcePressGestureRecognizer
({
this
.
startPressure
=
0.4
,
this
.
peakPressure
=
0.85
,
this
.
interpolation
=
_inverseLerp
,
Object
debugOwner
,
})
:
assert
(
startPressure
!=
null
),
assert
(
peakPressure
!=
null
),
assert
(
interpolation
!=
null
),
assert
(
peakPressure
>
startPressure
),
super
(
debugOwner:
debugOwner
);
/// A pointer is in contact with the screen and has just pressed with a force
/// exceeding the [startPressure]. Consequently, if there were other gesture
/// detectors, only the force press gesture will be detected and all others
/// will be rejected.
///
/// The position of the pointer is provided in the callback's `details`
/// argument, which is a [ForcePressDetails] object.
GestureForcePressStartCallback
onStart
;
/// A pointer is in contact with the screen and is either moving on the plane
/// of the screen, pressing the screen with varying forces or both
/// simultaneously.
///
/// This callback will be invoked for every pointer event after the invocation
/// of [onStart] and/or [onPeak] and before the invocation of [onEnd], no
/// matter what the pressure is during this time period. The position and
/// pressure of the pointer is provided in the callback's `details` argument,
/// which is a [ForcePressUpdateDetails] object.
GestureForcePressUpdateCallback
onUpdate
;
/// A pointer is in contact with the screen and has just pressed with a force
/// exceeding the [peakPressure]. This is an arbitrary second level action
/// threshold and isn't necessarily the maximum possible device pressure
/// (which is 1.0).
///
/// The position of the pointer is provided in the callback's `details`
/// argument, which is a [ForcePressDetails] object.
GestureForcePressPeakCallback
onPeak
;
/// A pointer is no longer in contact with the screen.
///
/// The position of the pointer is provided in the callback's `details`
/// argument, which is a [ForcePressDetails] object.
GestureForcePressEndCallback
onEnd
;
/// The pressure of the press required to initiate a force press.
///
/// A value of 0.0 is no pressure, and 1.0 is maximum pressure.
final
double
startPressure
;
/// The pressure of the press required to peak a force press.
///
/// A value of 0.0 is no pressure, and 1.0 is maximum pressure. This value
/// must be greater than [startPressure].
final
double
peakPressure
;
/// The function used to convert the raw device pressure values into a value
/// in the range [0, 1].
///
/// The function takes in the device's min, max and raw touch
/// pressure and returns a value in the range [0.0, 1.0] denoting the
/// interpolated touch pressure.
///
/// This function must always return values in the range [0, 1] when
/// pressureMin <= pressure <= pressureMax.
///
/// By default, the the function is a simple linear interpolation, however,
/// changing the function could be useful to accommodate variations in the way
/// different devices respond to pressure, change how animations from pressure
/// feedback are rendered or for other custom functionality.
///
/// For example, an ease in curve can be used to determine the interpolated
/// value:
///
/// ```dart
/// static double interpolateWithEasing(double min, double max, double t) {
/// final double lerp = (t - min) / (max - min);
/// return Curves.easeIn.transform(lerp);
/// }
/// ```
final
GestureForceInterpolation
interpolation
;
Offset
_lastPosition
;
double
_lastPressure
;
_ForceState
_state
=
_ForceState
.
ready
;
@override
void
addPointer
(
PointerEvent
event
)
{
startTrackingPointer
(
event
.
pointer
);
if
(
_state
==
_ForceState
.
ready
)
{
_state
=
_ForceState
.
possible
;
_lastPosition
=
event
.
position
;
}
}
@override
void
handleEvent
(
PointerEvent
event
)
{
assert
(
_state
!=
_ForceState
.
ready
);
// A static pointer with changes in pressure creates PointerMoveEvent events.
if
(
event
is
PointerMoveEvent
||
event
is
PointerDownEvent
)
{
final
double
pressure
=
interpolation
(
event
.
pressureMin
,
event
.
pressureMax
,
event
.
pressure
);
assert
(
pressure
.
isNaN
?
true
:
(
pressure
<=
1.0
&&
pressure
>=
0.0
));
_lastPosition
=
event
.
position
;
_lastPressure
=
pressure
;
if
(
_state
==
_ForceState
.
possible
)
{
if
(
pressure
>
startPressure
)
{
_state
=
_ForceState
.
started
;
resolve
(
GestureDisposition
.
accepted
);
}
else
if
(
event
.
delta
.
distanceSquared
>
kTouchSlop
)
{
resolve
(
GestureDisposition
.
rejected
);
}
}
// In case this is the only gesture detector we still don't want to start
// the gesture until the pressure is greater than the startPressure.
if
(
pressure
>
startPressure
&&
_state
==
_ForceState
.
accepted
)
{
_state
=
_ForceState
.
started
;
if
(
onStart
!=
null
)
{
invokeCallback
<
void
>(
'onStart'
,
()
=>
onStart
(
ForcePressDetails
(
pressure:
pressure
,
globalPosition:
_lastPosition
,
)));
}
}
if
(
onPeak
!=
null
&&
pressure
>
peakPressure
&&
(
_state
==
_ForceState
.
started
))
{
_state
=
_ForceState
.
peaked
;
if
(
onPeak
!=
null
)
{
invokeCallback
<
void
>(
'onPeak'
,
()
=>
onPeak
(
ForcePressDetails
(
pressure:
pressure
,
globalPosition:
event
.
position
,
)));
}
}
if
(
onUpdate
!=
null
&&
(
_state
==
_ForceState
.
started
||
_state
==
_ForceState
.
peaked
))
{
if
(
onUpdate
!=
null
)
{
invokeCallback
<
void
>(
'onUpdate'
,
()
=>
onUpdate
(
ForcePressDetails
(
pressure:
pressure
,
globalPosition:
event
.
position
,
)));
}
}
}
stopTrackingIfPointerNoLongerDown
(
event
);
}
@override
void
acceptGesture
(
int
pointer
)
{
if
(
_state
==
_ForceState
.
possible
)
_state
=
_ForceState
.
accepted
;
if
(
onStart
!=
null
&&
_state
==
_ForceState
.
started
)
{
invokeCallback
<
void
>(
'onStart'
,
()
=>
onStart
(
ForcePressDetails
(
pressure:
_lastPressure
,
globalPosition:
_lastPosition
,
)));
}
}
@override
void
didStopTrackingLastPointer
(
int
pointer
)
{
final
bool
wasAccepted
=
_state
==
_ForceState
.
started
||
_state
==
_ForceState
.
peaked
;
if
(
_state
==
_ForceState
.
possible
)
{
resolve
(
GestureDisposition
.
rejected
);
return
;
}
if
(
wasAccepted
&&
onEnd
!=
null
)
{
if
(
onEnd
!=
null
)
{
invokeCallback
<
void
>(
'onEnd'
,
()
=>
onEnd
(
ForcePressDetails
(
pressure:
0.0
,
globalPosition:
_lastPosition
,
)));
}
}
_state
=
_ForceState
.
ready
;
}
@override
void
rejectGesture
(
int
pointer
)
{
stopTrackingPointer
(
pointer
);
didStopTrackingLastPointer
(
pointer
);
}
static
double
_inverseLerp
(
double
min
,
double
max
,
double
t
)
{
return
(
t
-
min
)
/
(
max
-
min
);
}
@override
String
get
debugDescription
=>
'force press'
;
}
packages/flutter/lib/src/widgets/gesture_detector.dart
View file @
3d8aec2b
...
...
@@ -27,6 +27,10 @@ export 'package:flutter/gestures.dart' show
GestureScaleStartCallback
,
GestureScaleUpdateCallback
,
GestureScaleEndCallback
,
GestureForcePressStartCallback
,
GestureForcePressPeakCallback
,
GestureForcePressEndCallback
,
GestureForcePressUpdateCallback
,
ScaleStartDetails
,
ScaleUpdateDetails
,
ScaleEndDetails
,
...
...
@@ -167,6 +171,10 @@ class GestureDetector extends StatelessWidget {
this
.
onHorizontalDragUpdate
,
this
.
onHorizontalDragEnd
,
this
.
onHorizontalDragCancel
,
this
.
onForcePressStart
,
this
.
onForcePressPeak
,
this
.
onForcePressUpdate
,
this
.
onForcePressEnd
,
this
.
onPanDown
,
this
.
onPanStart
,
this
.
onPanUpdate
,
...
...
@@ -318,6 +326,37 @@ class GestureDetector extends StatelessWidget {
/// The pointers are no longer in contact with the screen.
final
GestureScaleEndCallback
onScaleEnd
;
/// The pointer is in contact with the screen and has pressed with sufficient
/// force to initiate a force press. The amount of force is at least
/// [ForcePressGestureRecognizer.startPressure].
///
/// Note that this callback will only be fired on devices with pressure
/// detecting screens.
final
GestureForcePressStartCallback
onForcePressStart
;
/// The pointer is in contact with the screen and has pressed with the maximum
/// force. The amount of force is at least
/// [ForcePressGestureRecognizer.peakPressure].
///
/// Note that this callback will only be fired on devices with pressure
/// detecting screens.
final
GestureForcePressPeakCallback
onForcePressPeak
;
/// A pointer is in contact with the screen, has previously passed the
/// [ForcePressGestureRecognizer.startPressure] and is either moving on the
/// plane of the screen, pressing the screen with varying forces or both
/// simultaneously.
///
/// Note that this callback will only be fired on devices with pressure
/// detecting screens.
final
GestureForcePressUpdateCallback
onForcePressUpdate
;
/// The pointer is no longer in contact with the screen.
///
/// Note that this callback will only be fired on devices with pressure
/// detecting screens.
final
GestureForcePressEndCallback
onForcePressEnd
;
/// How this gesture detector should behave during hit testing.
///
/// This defaults to [HitTestBehavior.deferToChild] if [child] is not null and
...
...
@@ -435,6 +474,22 @@ class GestureDetector extends StatelessWidget {
);
}
if
(
onForcePressStart
!=
null
||
onForcePressPeak
!=
null
||
onForcePressUpdate
!=
null
||
onForcePressEnd
!=
null
)
{
gestures
[
ForcePressGestureRecognizer
]
=
GestureRecognizerFactoryWithHandlers
<
ForcePressGestureRecognizer
>(
()
=>
ForcePressGestureRecognizer
(
debugOwner:
this
),
(
ForcePressGestureRecognizer
instance
)
{
instance
..
onStart
=
onForcePressStart
..
onPeak
=
onForcePressPeak
..
onUpdate
=
onForcePressUpdate
..
onEnd
=
onForcePressEnd
;
},
);
}
return
RawGestureDetector
(
gestures:
gestures
,
behavior:
behavior
,
...
...
packages/flutter/test/gestures/force_press_test.dart
0 → 100644
View file @
3d8aec2b
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/material.dart'
;
import
'gesture_tester.dart'
;
void
main
(
)
{
setUp
(
ensureGestureBinding
);
testGesture
(
'A force press can be recognized'
,
(
GestureTester
tester
)
{
// Device specific constants that represent those from the iPhone X
const
double
pressureMin
=
0
;
const
double
pressureMax
=
6.66
;
// Interpolated Flutter pressure values.
const
double
startPressure
=
0.4
;
// = Device pressure of 2.66.
const
double
peakPressure
=
0.85
;
// = Device pressure of 5.66.
int
started
=
0
;
int
peaked
=
0
;
int
updated
=
0
;
int
ended
=
0
;
Offset
startGlobalPosition
;
void
onStart
(
ForcePressDetails
details
)
{
startGlobalPosition
=
details
.
globalPosition
;
started
+=
1
;
}
final
ForcePressGestureRecognizer
force
=
ForcePressGestureRecognizer
(
startPressure:
startPressure
,
peakPressure:
peakPressure
);
force
.
onStart
=
onStart
;
force
.
onPeak
=
(
ForcePressDetails
details
)
=>
peaked
+=
1
;
force
.
onUpdate
=
(
ForcePressDetails
details
)
=>
updated
+=
1
;
force
.
onEnd
=
(
ForcePressDetails
details
)
=>
ended
+=
1
;
const
int
pointerValue
=
1
;
final
TestPointer
pointer
=
TestPointer
(
pointerValue
);
const
PointerDownEvent
down
=
PointerDownEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
);
pointer
.
setDownInfo
(
down
,
const
Offset
(
10.0
,
10.0
));
force
.
addPointer
(
down
);
tester
.
closeArena
(
pointerValue
);
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
// Pressure fed into the test environment simulates the values received directly from the device.
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
2.5
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
// We have not hit the start pressure, so no events should be true.
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
2.8
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
// We have just hit the start pressure so just the start event should be triggered and one update call should have occurred.
expect
(
started
,
1
);
expect
(
peaked
,
0
);
expect
(
updated
,
1
);
expect
(
ended
,
0
);
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
3.3
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
4.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
5.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
1.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
// We have exceeded the start pressure so update should be greater than 0.
expect
(
started
,
1
);
expect
(
updated
,
5
);
expect
(
peaked
,
0
);
expect
(
ended
,
0
);
expect
(
startGlobalPosition
,
const
Offset
(
10.0
,
10.0
));
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
6.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
// We have exceeded the peak pressure so peak pressure should be true.
expect
(
started
,
1
);
expect
(
updated
,
6
);
expect
(
peaked
,
1
);
expect
(
ended
,
0
);
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
3.3
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
4.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
5.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
1.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
// Update is still called.
expect
(
started
,
1
);
expect
(
updated
,
10
);
expect
(
peaked
,
1
);
expect
(
ended
,
0
);
tester
.
route
(
pointer
.
up
());
// We have ended the gesture so ended should be true.
expect
(
started
,
1
);
expect
(
updated
,
10
);
expect
(
peaked
,
1
);
expect
(
ended
,
1
);
});
testGesture
(
'If minimum pressure is not reached, start and end callbacks are not called'
,
(
GestureTester
tester
)
{
// Device specific constants that represent those from the iPhone X
const
double
pressureMin
=
0
;
const
double
pressureMax
=
6.66
;
// Interpolated Flutter pressure values.
const
double
startPressure
=
0.4
;
// = Device pressure of 2.66.
const
double
peakPressure
=
0.85
;
// = Device pressure of 5.66.
int
started
=
0
;
int
peaked
=
0
;
int
updated
=
0
;
int
ended
=
0
;
final
ForcePressGestureRecognizer
force
=
ForcePressGestureRecognizer
(
startPressure:
startPressure
,
peakPressure:
peakPressure
);
force
.
onStart
=
(
_
)
=>
started
+=
1
;
force
.
onPeak
=
(
_
)
=>
peaked
+=
1
;
force
.
onUpdate
=
(
_
)
=>
updated
+=
1
;
force
.
onEnd
=
(
_
)
=>
ended
+=
1
;
const
int
pointerValue
=
1
;
final
TestPointer
pointer
=
TestPointer
(
pointerValue
);
const
PointerDownEvent
down
=
PointerDownEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
);
pointer
.
setDownInfo
(
down
,
const
Offset
(
10.0
,
10.0
));
force
.
addPointer
(
down
);
tester
.
closeArena
(
1
);
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
// Pressure fed into the test environment simulates the values received directly from the device.
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
2.5
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
// We have not hit the start pressure, so no events should be true.
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
tester
.
route
(
pointer
.
up
());
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
});
testGesture
(
'Should recognize drag and not force touch if there is a drag recognizer'
,
(
GestureTester
tester
)
{
final
PanGestureRecognizer
drag
=
PanGestureRecognizer
();
// Device specific constants that represent those from the iPhone X
const
double
pressureMin
=
0
;
const
double
pressureMax
=
6.66
;
// Interpolated Flutter pressure values.
const
double
startPressure
=
0.4
;
// = Device pressure of 2.66.
const
double
peakPressure
=
0.85
;
// = Device pressure of 5.66.
int
started
=
0
;
int
peaked
=
0
;
int
updated
=
0
;
int
ended
=
0
;
final
ForcePressGestureRecognizer
force
=
ForcePressGestureRecognizer
(
startPressure:
startPressure
,
peakPressure:
peakPressure
);
force
.
onStart
=
(
_
)
=>
started
+=
1
;
force
.
onPeak
=
(
_
)
=>
peaked
+=
1
;
force
.
onUpdate
=
(
_
)
=>
updated
+=
1
;
force
.
onEnd
=
(
_
)
=>
ended
+=
1
;
int
didStartPan
=
0
;
drag
.
onStart
=
(
_
)
=>
didStartPan
+=
1
;
const
int
pointerValue
=
1
;
final
TestPointer
pointer
=
TestPointer
(
pointerValue
);
const
PointerDownEvent
down
=
PointerDownEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
1.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
);
pointer
.
setDownInfo
(
down
,
const
Offset
(
10.0
,
10.0
));
force
.
addPointer
(
down
);
drag
.
addPointer
(
down
);
tester
.
closeArena
(
1
);
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
expect
(
didStartPan
,
0
);
tester
.
route
(
pointer
.
move
(
const
Offset
(
30.0
,
30.0
)));
// moved 20 horizontally and 20 vertically which is 28 total
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
expect
(
didStartPan
,
1
);
// Pressure fed into the test environment simulates the values received directly from the device.
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
2.5
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
// We have not hit the start pressure, so no events should be true.
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
expect
(
didStartPan
,
1
);
// We don't expect any events from the force press recognizer.
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
4.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
expect
(
didStartPan
,
1
);
tester
.
route
(
pointer
.
up
());
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
expect
(
didStartPan
,
1
);
});
testGesture
(
'Should not call ended on pointer up if the gesture was never accepted'
,
(
GestureTester
tester
)
{
final
PanGestureRecognizer
drag
=
PanGestureRecognizer
();
// Interpolated Flutter pressure values.
const
double
startPressure
=
0.4
;
// = Device pressure of 2.66.
const
double
peakPressure
=
0.85
;
// = Device pressure of 5.66.
// Device specific constants that represent those from the iPhone X
const
double
pressureMin
=
0
;
const
double
pressureMax
=
6.66
;
int
started
=
0
;
int
peaked
=
0
;
int
updated
=
0
;
int
ended
=
0
;
final
ForcePressGestureRecognizer
force
=
ForcePressGestureRecognizer
(
startPressure:
startPressure
,
peakPressure:
peakPressure
);
force
.
onStart
=
(
_
)
=>
started
+=
1
;
force
.
onPeak
=
(
_
)
=>
peaked
+=
1
;
force
.
onUpdate
=
(
_
)
=>
updated
+=
1
;
force
.
onEnd
=
(
_
)
=>
ended
+=
1
;
int
didStartPan
=
0
;
drag
.
onStart
=
(
_
)
=>
didStartPan
+=
1
;
const
int
pointerValue
=
1
;
final
TestPointer
pointer
=
TestPointer
(
pointerValue
);
const
PointerDownEvent
down
=
PointerDownEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
1.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
);
pointer
.
setDownInfo
(
down
,
const
Offset
(
10.0
,
10.0
));
force
.
addPointer
(
down
);
drag
.
addPointer
(
down
);
tester
.
closeArena
(
1
);
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
expect
(
didStartPan
,
0
);
tester
.
route
(
pointer
.
up
());
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
expect
(
didStartPan
,
0
);
});
testGesture
(
'Should call start only once if there is a competing gesture recognizer'
,
(
GestureTester
tester
)
{
final
PanGestureRecognizer
drag
=
PanGestureRecognizer
();
// Interpolated Flutter pressure values.
const
double
startPressure
=
0.4
;
// = Device pressure of 2.66.
const
double
peakPressure
=
0.85
;
// = Device pressure of 5.66.
// Device specific constants that represent those from the iPhone X
const
double
pressureMin
=
0
;
const
double
pressureMax
=
6.66
;
int
started
=
0
;
int
peaked
=
0
;
int
updated
=
0
;
int
ended
=
0
;
final
ForcePressGestureRecognizer
force
=
ForcePressGestureRecognizer
(
startPressure:
startPressure
,
peakPressure:
peakPressure
);
force
.
onStart
=
(
_
)
=>
started
+=
1
;
force
.
onPeak
=
(
_
)
=>
peaked
+=
1
;
force
.
onUpdate
=
(
_
)
=>
updated
+=
1
;
force
.
onEnd
=
(
_
)
=>
ended
+=
1
;
int
didStartPan
=
0
;
drag
.
onStart
=
(
_
)
=>
didStartPan
+=
1
;
const
int
pointerValue
=
1
;
final
TestPointer
pointer
=
TestPointer
(
pointerValue
);
const
PointerDownEvent
down
=
PointerDownEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
1.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
);
pointer
.
setDownInfo
(
down
,
const
Offset
(
10.0
,
10.0
));
force
.
addPointer
(
down
);
drag
.
addPointer
(
down
);
tester
.
closeArena
(
1
);
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
expect
(
didStartPan
,
0
);
// Pressure fed into the test environment simulates the values received directly from the device.
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
3.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
// We have not hit the start pressure, so no events should be true.
expect
(
started
,
1
);
expect
(
peaked
,
0
);
expect
(
updated
,
1
);
expect
(
ended
,
0
);
expect
(
didStartPan
,
0
);
tester
.
route
(
pointer
.
up
());
expect
(
started
,
1
);
expect
(
peaked
,
0
);
expect
(
updated
,
1
);
expect
(
ended
,
1
);
expect
(
didStartPan
,
0
);
});
testGesture
(
'A force press can be recognized with a custom interpolation function'
,
(
GestureTester
tester
)
{
// Device specific constants that represent those from the iPhone X
const
double
pressureMin
=
0
;
const
double
pressureMax
=
6.66
;
// Interpolated Flutter pressure values.
const
double
startPressure
=
0.4
;
// = Device pressure of 2.66.
const
double
peakPressure
=
0.85
;
// = Device pressure of 5.66.
int
started
=
0
;
int
peaked
=
0
;
int
updated
=
0
;
int
ended
=
0
;
Offset
startGlobalPosition
;
void
onStart
(
ForcePressDetails
details
)
{
startGlobalPosition
=
details
.
globalPosition
;
started
+=
1
;
}
double
interpolateWithEasing
(
double
min
,
double
max
,
double
t
)
{
final
double
lerp
=
(
t
-
min
)
/
(
max
-
min
);
return
Curves
.
easeIn
.
transform
(
lerp
);
}
final
ForcePressGestureRecognizer
force
=
ForcePressGestureRecognizer
(
startPressure:
startPressure
,
peakPressure:
peakPressure
,
interpolation:
interpolateWithEasing
);
force
.
onStart
=
onStart
;
force
.
onPeak
=
(
ForcePressDetails
details
)
=>
peaked
+=
1
;
force
.
onUpdate
=
(
ForcePressDetails
details
)
=>
updated
+=
1
;
force
.
onEnd
=
(
ForcePressDetails
details
)
=>
ended
+=
1
;
const
int
pointerValue
=
1
;
final
TestPointer
pointer
=
TestPointer
(
pointerValue
);
const
PointerDownEvent
down
=
PointerDownEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
);
pointer
.
setDownInfo
(
down
,
const
Offset
(
10.0
,
10.0
));
force
.
addPointer
(
down
);
tester
.
closeArena
(
pointerValue
);
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
// Pressure fed into the test environment simulates the values received directly from the device.
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
2.5
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
// We have not hit the start pressure, so no events should be true.
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
2.8
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
// We have just hit the start pressure so just the start event should be triggered and one update call should have occurred.
expect
(
started
,
0
);
expect
(
peaked
,
0
);
expect
(
updated
,
0
);
expect
(
ended
,
0
);
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
3.3
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
expect
(
started
,
0
);
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
4.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
expect
(
started
,
1
);
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
5.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
1.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
// We have exceeded the start pressure so update should be greater than 0.
expect
(
started
,
1
);
expect
(
updated
,
3
);
expect
(
peaked
,
0
);
expect
(
ended
,
0
);
expect
(
startGlobalPosition
,
const
Offset
(
10.0
,
10.0
));
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
6.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
// We have exceeded the peak pressure so peak pressure should be true.
expect
(
started
,
1
);
expect
(
updated
,
4
);
expect
(
peaked
,
0
);
expect
(
ended
,
0
);
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
3.3
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
4.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
6.5
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
tester
.
route
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
10.0
,
10.0
),
pressure:
1.0
,
pressureMin:
pressureMin
,
pressureMax:
pressureMax
));
// Update is still called.
expect
(
started
,
1
);
expect
(
updated
,
8
);
expect
(
peaked
,
1
);
expect
(
ended
,
0
);
tester
.
route
(
pointer
.
up
());
// We have ended the gesture so ended should be true.
expect
(
started
,
1
);
expect
(
updated
,
8
);
expect
(
peaked
,
1
);
expect
(
ended
,
1
);
});
}
packages/flutter/test/widgets/gesture_detector_test.dart
View file @
3d8aec2b
...
...
@@ -343,4 +343,146 @@ void main() {
await
longPress
(
kLongPressTimeout
+
const
Duration
(
seconds:
1
));
// To make sure the time for long press has occurred
expect
(
longPressUp
,
1
);
});
testWidgets
(
'Force Press Callback called after force press'
,
(
WidgetTester
tester
)
async
{
int
forcePressStart
=
0
;
int
forcePressPeaked
=
0
;
int
forcePressUpdate
=
0
;
int
forcePressEnded
=
0
;
await
tester
.
pumpWidget
(
Container
(
alignment:
Alignment
.
topLeft
,
child:
Container
(
alignment:
Alignment
.
center
,
height:
100.0
,
color:
const
Color
(
0xFF00FF00
),
child:
GestureDetector
(
onForcePressStart:
(
_
)
=>
forcePressStart
+=
1
,
onForcePressEnd:
(
_
)
=>
forcePressEnded
+=
1
,
onForcePressPeak:
(
_
)
=>
forcePressPeaked
+=
1
,
onForcePressUpdate:
(
_
)
=>
forcePressUpdate
+=
1
,
),
),
),
);
final
TestGesture
gesture
=
await
tester
.
startGesture
(
const
Offset
(
400.0
,
50.0
));
const
int
pointerValue
=
1
;
await
gesture
.
updateWithCustomEvent
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
0.0
,
0.0
),
pressure:
0.3
,
pressureMin:
0
,
pressureMax:
1
));
expect
(
forcePressStart
,
0
);
expect
(
forcePressPeaked
,
0
);
expect
(
forcePressUpdate
,
0
);
expect
(
forcePressEnded
,
0
);
await
gesture
.
updateWithCustomEvent
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
0.0
,
0.0
),
pressure:
0.5
,
pressureMin:
0
,
pressureMax:
1
));
expect
(
forcePressStart
,
1
);
expect
(
forcePressPeaked
,
0
);
expect
(
forcePressUpdate
,
1
);
expect
(
forcePressEnded
,
0
);
await
gesture
.
updateWithCustomEvent
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
0.0
,
0.0
),
pressure:
0.6
,
pressureMin:
0
,
pressureMax:
1
));
await
gesture
.
updateWithCustomEvent
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
0.0
,
0.0
),
pressure:
0.7
,
pressureMin:
0
,
pressureMax:
1
));
await
gesture
.
updateWithCustomEvent
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
0.0
,
0.0
),
pressure:
0.2
,
pressureMin:
0
,
pressureMax:
1
));
await
gesture
.
updateWithCustomEvent
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
0.0
,
0.0
),
pressure:
0.3
,
pressureMin:
0
,
pressureMax:
1
));
expect
(
forcePressStart
,
1
);
expect
(
forcePressPeaked
,
0
);
expect
(
forcePressUpdate
,
5
);
expect
(
forcePressEnded
,
0
);
await
gesture
.
updateWithCustomEvent
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
0.0
,
0.0
),
pressure:
0.9
,
pressureMin:
0
,
pressureMax:
1
));
expect
(
forcePressStart
,
1
);
expect
(
forcePressPeaked
,
1
);
expect
(
forcePressUpdate
,
6
);
expect
(
forcePressEnded
,
0
);
await
gesture
.
up
();
expect
(
forcePressStart
,
1
);
expect
(
forcePressPeaked
,
1
);
expect
(
forcePressUpdate
,
6
);
expect
(
forcePressEnded
,
1
);
});
testWidgets
(
'Force Press Callback not called if long press triggered before force press'
,
(
WidgetTester
tester
)
async
{
int
forcePressStart
=
0
;
int
longPressTimes
=
0
;
await
tester
.
pumpWidget
(
Container
(
alignment:
Alignment
.
topLeft
,
child:
Container
(
alignment:
Alignment
.
center
,
height:
100.0
,
color:
const
Color
(
0xFF00FF00
),
child:
GestureDetector
(
onForcePressStart:
(
_
)
=>
forcePressStart
+=
1
,
onLongPress:
()
=>
longPressTimes
+=
1
,
),
),
),
);
final
TestGesture
gesture
=
await
tester
.
startGesture
(
const
Offset
(
400.0
,
50.0
));
const
int
pointerValue
=
1
;
await
gesture
.
updateWithCustomEvent
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
400.0
,
50.0
),
pressure:
0.3
,
pressureMin:
0
,
pressureMax:
1
));
expect
(
forcePressStart
,
0
);
expect
(
longPressTimes
,
0
);
// Trigger the long press.
await
tester
.
pump
(
kLongPressTimeout
+
const
Duration
(
seconds:
1
));
expect
(
longPressTimes
,
1
);
expect
(
forcePressStart
,
0
);
// Failed attempt to trigger the force press.
await
gesture
.
updateWithCustomEvent
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
400.0
,
50.0
),
pressure:
0.5
,
pressureMin:
0
,
pressureMax:
1
));
expect
(
longPressTimes
,
1
);
expect
(
forcePressStart
,
0
);
});
testWidgets
(
'Force Press Callback not called if drag triggered before force press'
,
(
WidgetTester
tester
)
async
{
int
forcePressStart
=
0
;
int
horizontalDragStart
=
0
;
await
tester
.
pumpWidget
(
Container
(
alignment:
Alignment
.
topLeft
,
child:
Container
(
alignment:
Alignment
.
center
,
height:
100.0
,
color:
const
Color
(
0xFF00FF00
),
child:
GestureDetector
(
onForcePressStart:
(
_
)
=>
forcePressStart
+=
1
,
onHorizontalDragStart:
(
_
)
=>
horizontalDragStart
+=
1
,
),
),
),
);
final
TestGesture
gesture
=
await
tester
.
startGesture
(
const
Offset
(
50.0
,
50.0
));
const
int
pointerValue
=
1
;
await
gesture
.
updateWithCustomEvent
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
0.0
,
0.0
),
pressure:
0.3
,
pressureMin:
0
,
pressureMax:
1
));
expect
(
forcePressStart
,
0
);
expect
(
horizontalDragStart
,
0
);
// Trigger horizontal drag.
await
gesture
.
moveBy
(
const
Offset
(
100
,
0
));
expect
(
horizontalDragStart
,
1
);
expect
(
forcePressStart
,
0
);
// Failed attempt to trigger the force press.
await
gesture
.
updateWithCustomEvent
(
const
PointerMoveEvent
(
pointer:
pointerValue
,
position:
Offset
(
0.0
,
0.0
),
pressure:
0.5
,
pressureMin:
0
,
pressureMax:
1
));
expect
(
horizontalDragStart
,
1
);
expect
(
forcePressStart
,
0
);
});
}
packages/flutter_test/lib/src/test_pointer.dart
View file @
3d8aec2b
...
...
@@ -43,6 +43,25 @@ class TestPointer {
Offset
get
location
=>
_location
;
Offset
_location
;
/// If a custom event is created outside of this class, this function is used
/// to set the [isDown].
bool
setDownInfo
(
PointerEvent
event
,
Offset
newLocation
)
{
_location
=
newLocation
;
switch
(
event
.
runtimeType
)
{
case
PointerDownEvent:
assert
(!
isDown
);
_isDown
=
true
;
break
;
case
PointerUpEvent:
case
PointerCancelEvent:
assert
(
isDown
);
_isDown
=
false
;
break
;
default
:
break
;
}
return
isDown
;
}
/// Create a [PointerDownEvent] at the given location.
///
/// By default, the time stamp on the event is [Duration.zero]. You
...
...
@@ -157,10 +176,50 @@ class TestGesture {
});
}
/// Create a [TestGesture] by starting with a custom [PointerDownEvent] at the
/// given point.
///
/// By default, the pointer identifier used is 1. This can be overridden by
/// providing the `pointer` argument.
///
/// A function to use for hit testing should be provided via the `hitTester`
/// argument, and a function to use for dispatching events should be provided
/// via the `dispatcher` argument.
static
Future
<
TestGesture
>
downWithCustomEvent
(
Offset
downLocation
,
PointerDownEvent
downEvent
,
{
int
pointer
=
1
,
@required
HitTester
hitTester
,
@required
EventDispatcher
dispatcher
,
})
async
{
assert
(
hitTester
!=
null
);
assert
(
dispatcher
!=
null
);
TestGesture
result
;
return
TestAsyncUtils
.
guard
<
void
>(()
async
{
// dispatch down event
final
HitTestResult
hitTestResult
=
hitTester
(
downLocation
);
final
TestPointer
testPointer
=
TestPointer
(
pointer
);
testPointer
.
setDownInfo
(
downEvent
,
downLocation
);
await
dispatcher
(
downEvent
,
hitTestResult
);
// create a TestGesture
result
=
TestGesture
.
_
(
dispatcher
,
hitTestResult
,
testPointer
);
}).
then
<
TestGesture
>((
void
value
)
{
return
result
;
},
onError:
(
dynamic
error
,
StackTrace
stack
)
{
return
Future
<
TestGesture
>.
error
(
error
,
stack
);
});
}
final
EventDispatcher
_dispatcher
;
final
HitTestResult
_result
;
final
TestPointer
_pointer
;
/// Send a move event moving the pointer by the given offset.
Future
<
void
>
updateWithCustomEvent
(
PointerEvent
event
,
{
Duration
timeStamp
=
Duration
.
zero
})
{
_pointer
.
setDownInfo
(
event
,
event
.
position
);
return
TestAsyncUtils
.
guard
<
void
>(()
{
return
_dispatcher
(
event
,
_result
);
});
}
/// Send a move event moving the pointer by the given offset.
Future
<
void
>
moveBy
(
Offset
offset
,
{
Duration
timeStamp
=
Duration
.
zero
})
{
assert
(
_pointer
.
_isDown
);
...
...
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