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
4982a7f1
Unverified
Commit
4982a7f1
authored
Mar 31, 2021
by
Justin McCandless
Committed by
GitHub
Mar 31, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
InteractiveViewer should call onInteractionUpdate even when gesture is disabled (#78990)
parent
97a75b9c
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
167 additions
and
21 deletions
+167
-21
interactive_viewer.dart
packages/flutter/lib/src/widgets/interactive_viewer.dart
+29
-18
interactive_viewer_test.dart
packages/flutter/test/widgets/interactive_viewer_test.dart
+138
-3
No files found.
packages/flutter/lib/src/widgets/interactive_viewer.dart
View file @
4982a7f1
...
...
@@ -253,11 +253,12 @@ class InteractiveViewer extends StatefulWidget {
/// Called when the user ends a pan or scale gesture on the widget.
///
/// At the time this is called, the [TransformationController] will have
/// already been updated to reflect the change caused by the interaction.
/// already been updated to reflect the change caused by the interaction,
/// though a pan may cause an inertia animation after this is called as well.
///
/// {@template flutter.widgets.InteractiveViewer.onInteractionEnd}
/// Will be called even if the interaction is disabled with
/// [
panEnabled] or [scaleEnabled]
.
/// Will be called even if the interaction is disabled with
[panEnabled] or
/// [
scaleEnabled] for both touch gestures and mouse interactions
.
///
/// A [GestureDetector] wrapping the InteractiveViewer will not respond to
/// [GestureDetector.onScaleStart], [GestureDetector.onScaleUpdate], and
...
...
@@ -294,7 +295,8 @@ class InteractiveViewer extends StatefulWidget {
/// Called when the user updates a pan or scale gesture on the widget.
///
/// At the time this is called, the [TransformationController] will have
/// already been updated to reflect the change caused by the interaction.
/// already been updated to reflect the change caused by the interaction, if
/// the interation caused the matrix to change.
///
/// {@macro flutter.widgets.InteractiveViewer.onInteractionEnd}
///
...
...
@@ -796,6 +798,7 @@ class _InteractiveViewerState extends State<InteractiveViewer> with TickerProvid
_gestureType
??=
_getGestureType
(
details
);
}
if
(!
_gestureIsSupported
(
_gestureType
))
{
widget
.
onInteractionUpdate
?.
call
(
details
);
return
;
}
...
...
@@ -839,6 +842,7 @@ class _InteractiveViewerState extends State<InteractiveViewer> with TickerProvid
case
_GestureType
.
rotate
:
if
(
details
.
rotation
==
0.0
)
{
widget
.
onInteractionUpdate
?.
call
(
details
);
return
;
}
final
double
desiredRotation
=
_rotationStart
!
+
details
.
rotation
;
...
...
@@ -856,6 +860,7 @@ class _InteractiveViewerState extends State<InteractiveViewer> with TickerProvid
// In an effort to keep the behavior similar whether or not scaleEnabled
// is true, these gestures are thrown away.
if
(
details
.
scale
!=
1.0
)
{
widget
.
onInteractionUpdate
?.
call
(
details
);
return
;
}
_panAxis
??=
_getPanAxis
(
_referenceFocalPoint
!,
focalPointScene
);
...
...
@@ -871,12 +876,7 @@ class _InteractiveViewerState extends State<InteractiveViewer> with TickerProvid
);
break
;
}
widget
.
onInteractionUpdate
?.
call
(
ScaleUpdateDetails
(
focalPoint:
details
.
focalPoint
,
localFocalPoint:
details
.
localFocalPoint
,
scale:
details
.
scale
,
rotation:
details
.
rotation
,
));
widget
.
onInteractionUpdate
?.
call
(
details
);
}
// Handle the end of a gesture of _GestureType. All of pan, scale, and rotate
...
...
@@ -932,25 +932,36 @@ class _InteractiveViewerState extends State<InteractiveViewer> with TickerProvid
// Handle mousewheel scroll events.
void
_receivedPointerSignal
(
PointerSignalEvent
event
)
{
if
(
event
is
PointerScrollEvent
)
{
// Ignore left and right scroll.
if
(
event
.
scrollDelta
.
dy
==
0.0
)
{
return
;
}
widget
.
onInteractionStart
?.
call
(
ScaleStartDetails
(
focalPoint:
event
.
position
,
localFocalPoint:
event
.
localPosition
,
),
);
// In the Flutter engine, the mousewheel scrollDelta is hardcoded to 20
// per scroll, while a trackpad scroll can be any amount. The calculation
// for scaleChange here was arbitrarily chosen to feel natural for both
// trackpads and mousewheels on all platforms.
final
double
scaleChange
=
math
.
exp
(-
event
.
scrollDelta
.
dy
/
200
);
if
(!
_gestureIsSupported
(
_GestureType
.
scale
))
{
widget
.
onInteractionUpdate
?.
call
(
ScaleUpdateDetails
(
focalPoint:
event
.
position
,
localFocalPoint:
event
.
localPosition
,
rotation:
0.0
,
scale:
scaleChange
,
horizontalScale:
1.0
,
verticalScale:
1.0
,
));
widget
.
onInteractionEnd
?.
call
(
ScaleEndDetails
());
return
;
}
// Ignore left and right scroll.
if
(
event
.
scrollDelta
.
dy
==
0.0
)
{
return
;
}
// In the Flutter engine, the mousewheel scrollDelta is hardcoded to 20 per scroll, while a trackpad scroll can be any amount.
// The calculation for scaleChange here was arbitrarily chosen to feel natural for both trackpads and mousewheels on all platforms.
final
double
scaleChange
=
math
.
exp
(-
event
.
scrollDelta
.
dy
/
200
);
final
Offset
focalPointScene
=
_transformationController
!.
toScene
(
event
.
localPosition
,
);
...
...
packages/flutter/test/widgets/interactive_viewer_test.dart
View file @
4982a7f1
...
...
@@ -4,6 +4,7 @@
import
'dart:math'
as
math
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:vector_math/vector_math_64.dart'
show
Quad
,
Vector3
,
Matrix4
;
...
...
@@ -717,15 +718,15 @@ void main() {
body:
Center
(
child:
InteractiveViewer
(
transformationController:
transformationController
,
onInteractionStart:
(
ScaleStartDetails
details
){
onInteractionStart:
(
ScaleStartDetails
details
)
{
calledStart
=
true
;
},
onInteractionUpdate:
(
ScaleUpdateDetails
details
){
onInteractionUpdate:
(
ScaleUpdateDetails
details
)
{
scaleChange
=
details
.
scale
;
focalPoint
=
details
.
focalPoint
;
localFocalPoint
=
details
.
localFocalPoint
;
},
onInteractionEnd:
(
ScaleEndDetails
details
){
onInteractionEnd:
(
ScaleEndDetails
details
)
{
currentVelocity
=
details
.
velocity
;
},
child:
const
SizedBox
(
width:
200.0
,
height:
200.0
),
...
...
@@ -758,6 +759,140 @@ void main() {
expect
(
scenePoint
.
dy
,
greaterThan
(
0.0
));
});
testWidgets
(
'onInteraction is called even when disabled (touch)'
,
(
WidgetTester
tester
)
async
{
final
TransformationController
transformationController
=
TransformationController
();
bool
calledStart
=
false
;
bool
calledUpdate
=
false
;
bool
calledEnd
=
false
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
Center
(
child:
InteractiveViewer
(
transformationController:
transformationController
,
scaleEnabled:
false
,
onInteractionStart:
(
ScaleStartDetails
details
)
{
calledStart
=
true
;
},
onInteractionUpdate:
(
ScaleUpdateDetails
details
)
{
calledUpdate
=
true
;
},
onInteractionEnd:
(
ScaleEndDetails
details
)
{
calledEnd
=
true
;
},
child:
const
SizedBox
(
width:
200.0
,
height:
200.0
),
),
),
),
),
);
final
Offset
childOffset
=
tester
.
getTopLeft
(
find
.
byType
(
SizedBox
));
final
Offset
childInterior
=
Offset
(
childOffset
.
dx
+
20.0
,
childOffset
.
dy
+
20.0
,
);
TestGesture
gesture
=
await
tester
.
startGesture
(
childOffset
);
// Attempting to pan doesn't work because it's disabled, but the
// interaction methods are still called.
addTearDown
(
gesture
.
removePointer
);
await
tester
.
pump
();
await
gesture
.
moveTo
(
childInterior
);
await
tester
.
pump
();
await
gesture
.
up
();
await
tester
.
pumpAndSettle
();
expect
(
transformationController
.
value
,
equals
(
Matrix4
.
identity
()));
expect
(
calledStart
,
isTrue
);
expect
(
calledUpdate
,
isTrue
);
expect
(
calledEnd
,
isTrue
);
// Attempting to pinch to zoom doesn't work because it's disabled, but the
// interaction methods are still called.
calledStart
=
false
;
calledUpdate
=
false
;
calledEnd
=
false
;
final
Offset
scaleStart1
=
childInterior
;
final
Offset
scaleStart2
=
Offset
(
childInterior
.
dx
+
10.0
,
childInterior
.
dy
);
final
Offset
scaleEnd1
=
Offset
(
childInterior
.
dx
-
10.0
,
childInterior
.
dy
);
final
Offset
scaleEnd2
=
Offset
(
childInterior
.
dx
+
20.0
,
childInterior
.
dy
);
gesture
=
await
tester
.
startGesture
(
scaleStart1
);
final
TestGesture
gesture2
=
await
tester
.
startGesture
(
scaleStart2
);
addTearDown
(
gesture2
.
removePointer
);
await
tester
.
pump
();
await
gesture
.
moveTo
(
scaleEnd1
);
await
gesture2
.
moveTo
(
scaleEnd2
);
await
tester
.
pump
();
await
gesture
.
up
();
await
gesture2
.
up
();
await
tester
.
pumpAndSettle
();
expect
(
transformationController
.
value
,
equals
(
Matrix4
.
identity
()));
expect
(
calledStart
,
isTrue
);
expect
(
calledUpdate
,
isTrue
);
expect
(
calledEnd
,
isTrue
);
},
variant:
const
TargetPlatformVariant
(<
TargetPlatform
>{
TargetPlatform
.
android
,
TargetPlatform
.
iOS
}));
testWidgets
(
'onInteraction is called even when disabled (mouse)'
,
(
WidgetTester
tester
)
async
{
final
TransformationController
transformationController
=
TransformationController
();
bool
calledStart
=
false
;
bool
calledUpdate
=
false
;
bool
calledEnd
=
false
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
Center
(
child:
InteractiveViewer
(
transformationController:
transformationController
,
scaleEnabled:
false
,
onInteractionStart:
(
ScaleStartDetails
details
)
{
calledStart
=
true
;
},
onInteractionUpdate:
(
ScaleUpdateDetails
details
)
{
calledUpdate
=
true
;
},
onInteractionEnd:
(
ScaleEndDetails
details
)
{
calledEnd
=
true
;
},
child:
const
SizedBox
(
width:
200.0
,
height:
200.0
),
),
),
),
),
);
final
Offset
childOffset
=
tester
.
getTopLeft
(
find
.
byType
(
SizedBox
));
final
Offset
childInterior
=
Offset
(
childOffset
.
dx
+
20.0
,
childOffset
.
dy
+
20.0
,
);
final
TestGesture
gesture
=
await
tester
.
startGesture
(
childOffset
,
kind:
PointerDeviceKind
.
mouse
);
// Attempting to pan doesn't work because it's disabled, but the
// interaction methods are still called.
addTearDown
(
gesture
.
removePointer
);
await
tester
.
pump
();
await
gesture
.
moveTo
(
childInterior
);
await
tester
.
pump
();
await
gesture
.
up
();
await
tester
.
pumpAndSettle
();
expect
(
transformationController
.
value
,
equals
(
Matrix4
.
identity
()));
expect
(
calledStart
,
isTrue
);
expect
(
calledUpdate
,
isTrue
);
expect
(
calledEnd
,
isTrue
);
// Attempting to scroll with a mouse to zoom doesn't work because it's
// disabled, but the interaction methods are still called.
calledStart
=
false
;
calledUpdate
=
false
;
calledEnd
=
false
;
await
scrollAt
(
childInterior
,
tester
,
const
Offset
(
0.0
,
-
20.0
));
await
tester
.
pumpAndSettle
();
expect
(
transformationController
.
value
,
equals
(
Matrix4
.
identity
()));
expect
(
calledStart
,
isTrue
);
expect
(
calledUpdate
,
isTrue
);
expect
(
calledEnd
,
isTrue
);
},
variant:
const
TargetPlatformVariant
(<
TargetPlatform
>{
TargetPlatform
.
macOS
,
TargetPlatform
.
linux
,
TargetPlatform
.
windows
}));
testWidgets
(
'viewport changes size'
,
(
WidgetTester
tester
)
async
{
final
TransformationController
transformationController
=
TransformationController
();
await
tester
.
pumpWidget
(
...
...
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