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
135a38d6
Commit
135a38d6
authored
Feb 25, 2016
by
Ian Hickson
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2165 from Hixie/size-obs-3
SizeObserver crusade: ScrollableViewport and tabs
parents
1ce3146d
f8080557
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
229 additions
and
106 deletions
+229
-106
tabs.dart
packages/flutter/lib/src/material/tabs.dart
+14
-11
viewport.dart
packages/flutter/lib/src/rendering/viewport.dart
+26
-1
binding.dart
packages/flutter/lib/src/services/binding.dart
+2
-0
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+8
-1
gesture_detector.dart
packages/flutter/lib/src/widgets/gesture_detector.dart
+12
-12
mixed_viewport.dart
packages/flutter/lib/src/widgets/mixed_viewport.dart
+9
-10
scroll_behavior.dart
packages/flutter/lib/src/widgets/scroll_behavior.dart
+16
-0
scrollable.dart
packages/flutter/lib/src/widgets/scrollable.dart
+53
-47
block_test.dart
packages/flutter/test/widget/block_test.dart
+5
-3
instrumentation.dart
packages/flutter_test/lib/src/instrumentation.dart
+78
-21
widget_tester.dart
packages/flutter_test/lib/src/widget_tester.dart
+6
-0
No files found.
packages/flutter/lib/src/material/tabs.dart
View file @
135a38d6
...
@@ -699,10 +699,10 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
...
@@ -699,10 +699,10 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
}
}
void
_updateScrollBehavior
()
{
void
_updateScrollBehavior
()
{
scrollBehavior
.
updateExtents
(
scroll
To
(
scroll
Behavior
.
updateExtents
(
containerExtent:
config
.
scrollDirection
==
Axis
.
vertical
?
_viewportSize
.
height
:
_viewportSize
.
width
,
containerExtent:
config
.
scrollDirection
==
Axis
.
vertical
?
_viewportSize
.
height
:
_viewportSize
.
width
,
contentExtent:
_tabWidths
.
reduce
((
double
sum
,
double
width
)
=>
sum
+
width
)
contentExtent:
_tabWidths
.
reduce
((
double
sum
,
double
width
)
=>
sum
+
width
)
);
)
)
;
}
}
void
_layoutChanged
(
Size
tabBarSize
,
List
<
double
>
tabWidths
)
{
void
_layoutChanged
(
Size
tabBarSize
,
List
<
double
>
tabWidths
)
{
...
@@ -713,11 +713,16 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
...
@@ -713,11 +713,16 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
});
});
}
}
void
_handleViewportSizeChanged
(
Size
newSize
)
{
Offset
_handlePaintOffsetUpdateNeeded
(
ViewportDimensions
dimensions
)
{
_viewportSize
=
newSize
;
// We make various state changes here but don't have to do so in a
// setState() callback because we are called during layout and all
// we're updating is the new offset, which we are providing to the
// render object via our return value.
_viewportSize
=
dimensions
.
containerSize
;
_updateScrollBehavior
();
_updateScrollBehavior
();
if
(
config
.
isScrollable
)
if
(
config
.
isScrollable
)
scrollTo
(
_centeredTabScrollOffset
(
_selection
.
index
),
duration:
_kTabBarScroll
);
scrollTo
(
_centeredTabScrollOffset
(
_selection
.
index
),
duration:
_kTabBarScroll
);
return
scrollOffsetToPixelDelta
(
scrollOffset
);
}
}
Widget
buildContent
(
BuildContext
context
)
{
Widget
buildContent
(
BuildContext
context
)
{
...
@@ -772,13 +777,11 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
...
@@ -772,13 +777,11 @@ class _TabBarState<T> extends ScrollableState<TabBar<T>> implements TabBarSelect
);
);
if
(
config
.
isScrollable
)
{
if
(
config
.
isScrollable
)
{
contents
=
new
SizeObserver
(
child:
new
Viewport
(
onSizeChanged:
_handleViewportSizeChanged
,
scrollDirection:
Axis
.
horizontal
,
child:
new
Viewport
(
paintOffset:
scrollOffsetToPixelDelta
(
scrollOffset
),
scrollDirection:
Axis
.
horizontal
,
onPaintOffsetUpdateNeeded:
_handlePaintOffsetUpdateNeeded
,
paintOffset:
scrollOffsetToPixelDelta
(
scrollOffset
),
child:
contents
child:
contents
)
);
);
}
}
...
...
packages/flutter/lib/src/rendering/viewport.dart
View file @
135a38d6
...
@@ -39,6 +39,20 @@ class ViewportDimensions {
...
@@ -39,6 +39,20 @@ class ViewportDimensions {
return
paintOffset
+
(
containerSize
-
contentSize
);
return
paintOffset
+
(
containerSize
-
contentSize
);
}
}
}
}
bool
operator
==(
dynamic
other
)
{
if
(
identical
(
this
,
other
))
return
true
;
if
(
other
is
!
ViewportDimensions
)
return
false
;
final
ViewportDimensions
typedOther
=
other
;
return
contentSize
==
typedOther
.
contentSize
&&
containerSize
==
typedOther
.
containerSize
;
}
int
get
hashCode
=>
hashValues
(
contentSize
,
containerSize
);
String
toString
()
=>
'ViewportDimensions(container:
$containerSize
, content:
$contentSize
)'
;
}
}
abstract
class
HasScrollDirection
{
abstract
class
HasScrollDirection
{
...
@@ -163,6 +177,8 @@ class RenderViewportBase extends RenderBox implements HasScrollDirection {
...
@@ -163,6 +177,8 @@ class RenderViewportBase extends RenderBox implements HasScrollDirection {
}
}
typedef
Offset
ViewportDimensionsChangeCallback
(
ViewportDimensions
dimensions
);
/// A render object that's bigger on the inside.
/// A render object that's bigger on the inside.
///
///
/// The child of a viewport can layout to a larger size than the viewport
/// The child of a viewport can layout to a larger size than the viewport
...
@@ -176,11 +192,16 @@ class RenderViewport extends RenderViewportBase with RenderObjectWithChildMixin<
...
@@ -176,11 +192,16 @@ class RenderViewport extends RenderViewportBase with RenderObjectWithChildMixin<
Offset
paintOffset:
Offset
.
zero
,
Offset
paintOffset:
Offset
.
zero
,
Axis
scrollDirection:
Axis
.
vertical
,
Axis
scrollDirection:
Axis
.
vertical
,
ViewportAnchor
scrollAnchor:
ViewportAnchor
.
start
,
ViewportAnchor
scrollAnchor:
ViewportAnchor
.
start
,
Painter
overlayPainter
Painter
overlayPainter
,
this
.
onPaintOffsetUpdateNeeded
})
:
super
(
paintOffset
,
scrollDirection
,
scrollAnchor
,
overlayPainter
)
{
})
:
super
(
paintOffset
,
scrollDirection
,
scrollAnchor
,
overlayPainter
)
{
this
.
child
=
child
;
this
.
child
=
child
;
}
}
/// Called during [layout] to report the dimensions of the viewport
/// and its child.
ViewportDimensionsChangeCallback
onPaintOffsetUpdateNeeded
;
BoxConstraints
_getInnerConstraints
(
BoxConstraints
constraints
)
{
BoxConstraints
_getInnerConstraints
(
BoxConstraints
constraints
)
{
BoxConstraints
innerConstraints
;
BoxConstraints
innerConstraints
;
switch
(
scrollDirection
)
{
switch
(
scrollDirection
)
{
...
@@ -228,6 +249,7 @@ class RenderViewport extends RenderViewportBase with RenderObjectWithChildMixin<
...
@@ -228,6 +249,7 @@ class RenderViewport extends RenderViewportBase with RenderObjectWithChildMixin<
// parent was baseline-aligned, which makes no sense.
// parent was baseline-aligned, which makes no sense.
void
performLayout
()
{
void
performLayout
()
{
ViewportDimensions
oldDimensions
=
dimensions
;
if
(
child
!=
null
)
{
if
(
child
!=
null
)
{
child
.
layout
(
_getInnerConstraints
(
constraints
),
parentUsesSize:
true
);
child
.
layout
(
_getInnerConstraints
(
constraints
),
parentUsesSize:
true
);
size
=
constraints
.
constrain
(
child
.
size
);
size
=
constraints
.
constrain
(
child
.
size
);
...
@@ -238,6 +260,9 @@ class RenderViewport extends RenderViewportBase with RenderObjectWithChildMixin<
...
@@ -238,6 +260,9 @@ class RenderViewport extends RenderViewportBase with RenderObjectWithChildMixin<
performResize
();
performResize
();
dimensions
=
new
ViewportDimensions
(
containerSize:
size
);
dimensions
=
new
ViewportDimensions
(
containerSize:
size
);
}
}
if
(
onPaintOffsetUpdateNeeded
!=
null
&&
dimensions
!=
oldDimensions
)
paintOffset
=
onPaintOffsetUpdateNeeded
(
dimensions
);
assert
(
paintOffset
!=
null
);
}
}
bool
_shouldClipAtPaintOffset
(
Offset
paintOffset
)
{
bool
_shouldClipAtPaintOffset
(
Offset
paintOffset
)
{
...
...
packages/flutter/lib/src/services/binding.dart
View file @
135a38d6
...
@@ -37,6 +37,8 @@ abstract class BindingBase {
...
@@ -37,6 +37,8 @@ abstract class BindingBase {
void
initInstances
()
{
void
initInstances
()
{
assert
(()
{
_debugInitialized
=
true
;
return
true
;
});
assert
(()
{
_debugInitialized
=
true
;
return
true
;
});
}
}
String
toString
()
=>
'<
$runtimeType
>'
;
}
}
// A replacement for shell.connectToService. Implementations should return true
// A replacement for shell.connectToService. Implementations should return true
...
...
packages/flutter/lib/src/widgets/basic.dart
View file @
135a38d6
...
@@ -43,7 +43,9 @@ export 'package:flutter/rendering.dart' show
...
@@ -43,7 +43,9 @@ export 'package:flutter/rendering.dart' show
RelativeRect
,
RelativeRect
,
ShaderCallback
,
ShaderCallback
,
ValueChanged
,
ValueChanged
,
ViewportAnchor
;
ViewportAnchor
,
ViewportDimensions
,
ViewportDimensionsChangeCallback
;
// PAINTING NODES
// PAINTING NODES
...
@@ -777,6 +779,7 @@ class Viewport extends OneChildRenderObjectWidget {
...
@@ -777,6 +779,7 @@ class Viewport extends OneChildRenderObjectWidget {
this
.
scrollDirection
:
Axis
.
vertical
,
this
.
scrollDirection
:
Axis
.
vertical
,
this
.
scrollAnchor
:
ViewportAnchor
.
start
,
this
.
scrollAnchor
:
ViewportAnchor
.
start
,
this
.
overlayPainter
,
this
.
overlayPainter
,
this
.
onPaintOffsetUpdateNeeded
,
Widget
child
Widget
child
})
:
super
(
key:
key
,
child:
child
)
{
})
:
super
(
key:
key
,
child:
child
)
{
assert
(
scrollDirection
!=
null
);
assert
(
scrollDirection
!=
null
);
...
@@ -802,11 +805,14 @@ class Viewport extends OneChildRenderObjectWidget {
...
@@ -802,11 +805,14 @@ class Viewport extends OneChildRenderObjectWidget {
/// Often used to paint scroll bars.
/// Often used to paint scroll bars.
final
Painter
overlayPainter
;
final
Painter
overlayPainter
;
final
ViewportDimensionsChangeCallback
onPaintOffsetUpdateNeeded
;
RenderViewport
createRenderObject
()
{
RenderViewport
createRenderObject
()
{
return
new
RenderViewport
(
return
new
RenderViewport
(
paintOffset:
paintOffset
,
paintOffset:
paintOffset
,
scrollDirection:
scrollDirection
,
scrollDirection:
scrollDirection
,
scrollAnchor:
scrollAnchor
,
scrollAnchor:
scrollAnchor
,
onPaintOffsetUpdateNeeded:
onPaintOffsetUpdateNeeded
,
overlayPainter:
overlayPainter
overlayPainter:
overlayPainter
);
);
}
}
...
@@ -817,6 +823,7 @@ class Viewport extends OneChildRenderObjectWidget {
...
@@ -817,6 +823,7 @@ class Viewport extends OneChildRenderObjectWidget {
..
scrollDirection
=
scrollDirection
..
scrollDirection
=
scrollDirection
..
scrollAnchor
=
scrollAnchor
..
scrollAnchor
=
scrollAnchor
..
paintOffset
=
paintOffset
..
paintOffset
=
paintOffset
..
onPaintOffsetUpdateNeeded
=
onPaintOffsetUpdateNeeded
..
overlayPainter
=
overlayPainter
;
..
overlayPainter
=
overlayPainter
;
}
}
}
}
...
...
packages/flutter/lib/src/widgets/gesture_detector.dart
View file @
135a38d6
...
@@ -280,29 +280,29 @@ class RawGestureDetectorState extends State<RawGestureDetector> {
...
@@ -280,29 +280,29 @@ class RawGestureDetectorState extends State<RawGestureDetector> {
void
replaceGestureRecognizers
(
Map
<
Type
,
GestureRecognizerFactory
>
gestures
)
{
void
replaceGestureRecognizers
(
Map
<
Type
,
GestureRecognizerFactory
>
gestures
)
{
assert
(()
{
assert
(()
{
RenderObject
renderObject
=
context
.
findRenderObject
();
RenderObject
renderObject
=
context
.
findRenderObject
();
assert
(
renderObject
is
RenderPointerListener
);
RenderPointerListener
listener
=
renderObject
;
RenderBox
descendant
=
listener
.
child
;
if
(!
config
.
excludeFromSemantics
)
{
if
(!
config
.
excludeFromSemantics
)
{
assert
(
descendan
t
is
RenderSemanticsGestureHandler
);
assert
(
renderObjec
t
is
RenderSemanticsGestureHandler
);
RenderSemanticsGestureHandler
semanticsGestureHandler
=
descendan
t
;
RenderSemanticsGestureHandler
semanticsGestureHandler
=
renderObjec
t
;
descendan
t
=
semanticsGestureHandler
.
child
;
renderObjec
t
=
semanticsGestureHandler
.
child
;
}
}
assert
(
descendant
!=
null
);
assert
(
renderObject
is
RenderPointerListener
);
if
(!
descendant
.
debugDoingThisLayout
)
{
RenderPointerListener
pointerListener
=
renderObject
;
renderObject
=
pointerListener
.
child
;
if
(!
renderObject
.
debugDoingThisLayout
)
{
throw
new
WidgetError
(
throw
new
WidgetError
(
'replaceGestureRecognizers() can only be called during the layout phase of the GestureDetector
\'
s nearest descendant RenderObjectWidget.
\n
'
'replaceGestureRecognizers() can only be called during the layout phase of the GestureDetector
\'
s nearest descendant RenderObjectWidget.
\n
'
'In this particular case, that is:
\n
'
'In this particular case, that is:
\n
'
'
$
descendan
t
'
'
$
renderObjec
t
'
);
);
}
}
return
true
;
return
true
;
});
});
_syncAll
(
gestures
);
_syncAll
(
gestures
);
if
(!
config
.
excludeFromSemantics
)
{
if
(!
config
.
excludeFromSemantics
)
{
RenderPointerListener
listener
=
context
.
findRenderObject
();
RenderSemanticsGestureHandler
semanticsGestureHandler
=
context
.
findRenderObject
();
RenderSemanticsGestureHandler
semanticsGestureHandler
=
listener
.
child
;
context
.
visitChildElements
((
RenderObjectElement
element
)
{
context
.
visitChildElements
((
RenderObjectElement
element
)
=>
element
.
widget
.
updateRenderObject
(
semanticsGestureHandler
,
null
));
element
.
widget
.
updateRenderObject
(
semanticsGestureHandler
,
null
);
});
}
}
}
}
...
...
packages/flutter/lib/src/widgets/mixed_viewport.dart
View file @
135a38d6
...
@@ -10,7 +10,6 @@ import 'framework.dart';
...
@@ -10,7 +10,6 @@ import 'framework.dart';
import
'basic.dart'
;
import
'basic.dart'
;
typedef
Widget
IndexedBuilder
(
BuildContext
context
,
int
index
);
// return null if index is greater than index of last entry
typedef
Widget
IndexedBuilder
(
BuildContext
context
,
int
index
);
// return null if index is greater than index of last entry
typedef
void
ExtentsUpdateCallback
(
double
newExtents
);
typedef
void
InvalidatorCallback
(
Iterable
<
int
>
indices
);
typedef
void
InvalidatorCallback
(
Iterable
<
int
>
indices
);
typedef
void
InvalidatorAvailableCallback
(
InvalidatorCallback
invalidator
);
typedef
void
InvalidatorAvailableCallback
(
InvalidatorCallback
invalidator
);
...
@@ -23,7 +22,7 @@ class MixedViewport extends RenderObjectWidget {
...
@@ -23,7 +22,7 @@ class MixedViewport extends RenderObjectWidget {
this
.
direction
:
Axis
.
vertical
,
this
.
direction
:
Axis
.
vertical
,
this
.
builder
,
this
.
builder
,
this
.
token
,
this
.
token
,
this
.
onExtent
sUpdate
,
this
.
onExtent
Changed
,
this
.
onInvalidatorAvailable
this
.
onInvalidatorAvailable
})
:
super
(
key:
key
);
})
:
super
(
key:
key
);
...
@@ -31,7 +30,7 @@ class MixedViewport extends RenderObjectWidget {
...
@@ -31,7 +30,7 @@ class MixedViewport extends RenderObjectWidget {
final
Axis
direction
;
final
Axis
direction
;
final
IndexedBuilder
builder
;
final
IndexedBuilder
builder
;
final
Object
token
;
// change this if the list changed (i.e. there are added, removed, or resorted items)
final
Object
token
;
// change this if the list changed (i.e. there are added, removed, or resorted items)
final
ExtentsUpdateCallback
onExtentsUpdate
;
final
ValueChanged
<
double
>
onExtentChanged
;
final
InvalidatorAvailableCallback
onInvalidatorAvailable
;
// call the callback this gives to invalidate sizes
final
InvalidatorAvailableCallback
onInvalidatorAvailable
;
// call the callback this gives to invalidate sizes
_MixedViewportElement
createElement
()
=>
new
_MixedViewportElement
(
this
);
_MixedViewportElement
createElement
()
=>
new
_MixedViewportElement
(
this
);
...
@@ -108,8 +107,8 @@ class _MixedViewportElement extends RenderObjectElement<MixedViewport> {
...
@@ -108,8 +107,8 @@ class _MixedViewportElement extends RenderObjectElement<MixedViewport> {
/// The constraints for which the current offsets are valid.
/// The constraints for which the current offsets are valid.
BoxConstraints
_lastLayoutConstraints
;
BoxConstraints
_lastLayoutConstraints
;
/// The last value that was sent to onExtent
sUpdate
.
/// The last value that was sent to onExtent
Changed
.
double
_lastReportedExtent
s
;
double
_lastReportedExtent
;
RenderBlockViewport
get
renderObject
=>
super
.
renderObject
;
RenderBlockViewport
get
renderObject
=>
super
.
renderObject
;
...
@@ -227,11 +226,11 @@ class _MixedViewportElement extends RenderObjectElement<MixedViewport> {
...
@@ -227,11 +226,11 @@ class _MixedViewportElement extends RenderObjectElement<MixedViewport> {
BuildableElement
.
lockState
(()
{
BuildableElement
.
lockState
(()
{
_doLayout
(
constraints
);
_doLayout
(
constraints
);
},
building:
true
);
},
building:
true
);
if
(
widget
.
onExtent
sUpdate
!=
null
)
{
if
(
widget
.
onExtent
Changed
!=
null
)
{
final
double
newExtent
s
=
_didReachLastChild
?
_childOffsets
.
last
:
null
;
final
double
newExtent
=
_didReachLastChild
?
_childOffsets
.
last
:
null
;
if
(
newExtent
s
!=
_lastReportedExtents
)
{
if
(
newExtent
!=
_lastReportedExtent
)
{
_lastReportedExtent
s
=
newExtents
;
_lastReportedExtent
=
newExtent
;
widget
.
onExtent
sUpdate
(
_lastReportedExtents
);
widget
.
onExtent
Changed
(
_lastReportedExtent
);
}
}
}
}
}
}
...
...
packages/flutter/lib/src/widgets/scroll_behavior.dart
View file @
135a38d6
...
@@ -35,6 +35,15 @@ abstract class ScrollBehavior<T, U> {
...
@@ -35,6 +35,15 @@ abstract class ScrollBehavior<T, U> {
/// Whether this scroll behavior currently permits scrolling
/// Whether this scroll behavior currently permits scrolling
bool
get
isScrollable
=>
true
;
bool
get
isScrollable
=>
true
;
String
toString
()
{
List
<
String
>
description
=
<
String
>[];
debugFillDescription
(
description
);
return
'
$runtimeType
(
${description.join("; ")}
)'
;
}
void
debugFillDescription
(
List
<
String
>
description
)
{
description
.
add
(
isScrollable
?
'scrollable'
:
'not scrollable'
);
}
}
}
/// A scroll behavior for a scrollable widget with linear extent (i.e.
/// A scroll behavior for a scrollable widget with linear extent (i.e.
...
@@ -74,6 +83,13 @@ abstract class ExtentScrollBehavior extends ScrollBehavior<double, double> {
...
@@ -74,6 +83,13 @@ abstract class ExtentScrollBehavior extends ScrollBehavior<double, double> {
/// The maximum value the scroll offset can obtain.
/// The maximum value the scroll offset can obtain.
double
get
maxScrollOffset
;
double
get
maxScrollOffset
;
void
debugFillDescription
(
List
<
String
>
description
)
{
super
.
debugFillDescription
(
description
);
description
.
add
(
'content:
${contentExtent.toStringAsFixed(1)}
'
);
description
.
add
(
'container:
${contentExtent.toStringAsFixed(1)}
'
);
description
.
add
(
'range:
${minScrollOffset?.toStringAsFixed(1)}
..
${maxScrollOffset?.toStringAsFixed(1)}
'
);
}
}
}
/// A scroll behavior that prevents the user from exceeding scroll bounds.
/// A scroll behavior that prevents the user from exceeding scroll bounds.
...
...
packages/flutter/lib/src/widgets/scrollable.dart
View file @
135a38d6
...
@@ -237,32 +237,42 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
...
@@ -237,32 +237,42 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
return
_scrollBehavior
;
return
_scrollBehavior
;
}
}
GestureDragStartCallback
_getDragStartHandler
(
Axis
direction
)
{
Map
<
Type
,
GestureRecognizerFactory
>
buildGestureDetectors
()
{
if
(
config
.
scrollDirection
!=
direction
||
!
scrollBehavior
.
isScrollable
)
if
(
scrollBehavior
.
isScrollable
)
{
return
null
;
switch
(
config
.
scrollDirection
)
{
return
_handleDragStart
;
case
Axis
.
vertical
:
return
<
Type
,
GestureRecognizerFactory
>{
VerticalDragGestureRecognizer:
(
VerticalDragGestureRecognizer
recognizer
)
{
return
(
recognizer
??=
new
VerticalDragGestureRecognizer
())
..
onStart
=
_handleDragStart
..
onUpdate
=
_handleDragUpdate
..
onEnd
=
_handleDragEnd
;
}
};
case
Axis
.
horizontal
:
return
<
Type
,
GestureRecognizerFactory
>{
HorizontalDragGestureRecognizer:
(
HorizontalDragGestureRecognizer
recognizer
)
{
return
(
recognizer
??=
new
HorizontalDragGestureRecognizer
())
..
onStart
=
_handleDragStart
..
onUpdate
=
_handleDragUpdate
..
onEnd
=
_handleDragEnd
;
}
};
}
}
return
const
<
Type
,
GestureRecognizerFactory
>{};
}
}
GestureDragUpdateCallback
_getDragUpdateHandler
(
Axis
direction
)
{
final
GlobalKey
_gestureDetectorKey
=
new
GlobalKey
();
if
(
config
.
scrollDirection
!=
direction
||
!
scrollBehavior
.
isScrollable
)
return
null
;
return
_handleDragUpdate
;
}
GestureDragEndCallback
_getDragEndHandler
(
Axis
direction
)
{
void
updateGestureDetector
()
{
if
(
config
.
scrollDirection
!=
direction
||
!
scrollBehavior
.
isScrollable
)
_gestureDetectorKey
.
currentState
.
replaceGestureRecognizers
(
buildGestureDetectors
());
return
null
;
return
_handleDragEnd
;
}
}
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
new
GestureDetector
(
return
new
RawGestureDetector
(
onVerticalDragStart:
_getDragStartHandler
(
Axis
.
vertical
),
key:
_gestureDetectorKey
,
onVerticalDragUpdate:
_getDragUpdateHandler
(
Axis
.
vertical
),
gestures:
buildGestureDetectors
(),
onVerticalDragEnd:
_getDragEndHandler
(
Axis
.
vertical
),
onHorizontalDragStart:
_getDragStartHandler
(
Axis
.
horizontal
),
onHorizontalDragUpdate:
_getDragUpdateHandler
(
Axis
.
horizontal
),
onHorizontalDragEnd:
_getDragEndHandler
(
Axis
.
horizontal
),
behavior:
HitTestBehavior
.
opaque
,
behavior:
HitTestBehavior
.
opaque
,
child:
new
Listener
(
child:
new
Listener
(
child:
buildContent
(
context
),
child:
buildContent
(
context
),
...
@@ -321,7 +331,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
...
@@ -321,7 +331,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
if
(
endScrollOffset
.
isNaN
)
if
(
endScrollOffset
.
isNaN
)
return
null
;
return
null
;
final
double
snappedScrollOffset
=
snapScrollOffset
(
endScrollOffset
);
final
double
snappedScrollOffset
=
snapScrollOffset
(
endScrollOffset
);
// invokes the config.snapOffsetCallback callback
if
(!
_scrollOffsetIsInBounds
(
snappedScrollOffset
))
if
(!
_scrollOffsetIsInBounds
(
snappedScrollOffset
))
return
null
;
return
null
;
...
@@ -443,7 +453,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
...
@@ -443,7 +453,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
}
}
void
_handleDragStart
(
_
)
{
void
_handleDragStart
(
_
)
{
scheduleMicrotask
(
dispatchOnScrollStart
);
dispatchOnScrollStart
(
);
}
}
void
_handleDragUpdate
(
double
delta
)
{
void
_handleDragUpdate
(
double
delta
)
{
...
@@ -503,18 +513,19 @@ class _ScrollableViewportState extends ScrollableState<ScrollableViewport> {
...
@@ -503,18 +513,19 @@ class _ScrollableViewportState extends ScrollableState<ScrollableViewport> {
double
_viewportSize
=
0.0
;
double
_viewportSize
=
0.0
;
double
_childSize
=
0.0
;
double
_childSize
=
0.0
;
void
_handleViewportSizeChanged
(
Size
newSize
)
{
_viewportSize
=
config
.
scrollDirection
==
Axis
.
vertical
?
newSize
.
height
:
newSize
.
width
;
Offset
_handlePaintOffsetUpdateNeeded
(
ViewportDimensions
dimensions
)
{
setState
(()
{
// We make various state changes here but don't have to do so in a
_updateScrollBehavior
();
// setState() callback because we are called during layout and all
});
// we're updating is the new offset, which we are providing to the
}
// render object via our return value.
void
_handleChildSizeChanged
(
Size
newSize
)
{
_viewportSize
=
config
.
scrollDirection
==
Axis
.
vertical
?
dimensions
.
containerSize
.
height
:
dimensions
.
containerSize
.
width
;
_childSize
=
config
.
scrollDirection
==
Axis
.
vertical
?
newSize
.
height
:
new
Size
.
width
;
_childSize
=
config
.
scrollDirection
==
Axis
.
vertical
?
dimensions
.
contentSize
.
height
:
dimensions
.
content
Size
.
width
;
setState
(()
{
_updateScrollBehavior
();
_updateScrollBehavi
or
();
updateGestureDetect
or
();
}
);
return
scrollOffsetToPixelDelta
(
scrollOffset
);
}
}
void
_updateScrollBehavior
()
{
void
_updateScrollBehavior
()
{
// if you don't call this from build(), you must call it from setState().
// if you don't call this from build(), you must call it from setState().
scrollTo
(
scrollBehavior
.
updateExtents
(
scrollTo
(
scrollBehavior
.
updateExtents
(
...
@@ -525,17 +536,12 @@ class _ScrollableViewportState extends ScrollableState<ScrollableViewport> {
...
@@ -525,17 +536,12 @@ class _ScrollableViewportState extends ScrollableState<ScrollableViewport> {
}
}
Widget
buildContent
(
BuildContext
context
)
{
Widget
buildContent
(
BuildContext
context
)
{
return
new
SizeObserver
(
return
new
Viewport
(
onSizeChanged:
_handleViewportSizeChanged
,
paintOffset:
scrollOffsetToPixelDelta
(
scrollOffset
),
child:
new
Viewport
(
scrollDirection:
config
.
scrollDirection
,
paintOffset:
scrollOffsetToPixelDelta
(
scrollOffset
),
scrollAnchor:
config
.
scrollAnchor
,
scrollDirection:
config
.
scrollDirection
,
onPaintOffsetUpdateNeeded:
_handlePaintOffsetUpdateNeeded
,
scrollAnchor:
config
.
scrollAnchor
,
child:
config
.
child
child:
new
SizeObserver
(
onSizeChanged:
_handleChildSizeChanged
,
child:
config
.
child
)
)
);
);
}
}
}
}
...
@@ -690,11 +696,11 @@ class ScrollableMixedWidgetListState extends ScrollableState<ScrollableMixedWidg
...
@@ -690,11 +696,11 @@ class ScrollableMixedWidgetListState extends ScrollableState<ScrollableMixedWidg
}
}
}
}
void
_handleExtent
sUpdate
(
double
newExtents
)
{
void
_handleExtent
Changed
(
double
newExtent
)
{
double
newScrollOffset
;
double
newScrollOffset
;
setState
(()
{
setState
(()
{
newScrollOffset
=
scrollBehavior
.
updateExtents
(
newScrollOffset
=
scrollBehavior
.
updateExtents
(
contentExtent:
newExtent
s
??
double
.
INFINITY
,
contentExtent:
newExtent
??
double
.
INFINITY
,
scrollOffset:
scrollOffset
scrollOffset:
scrollOffset
);
);
});
});
...
@@ -712,7 +718,7 @@ class ScrollableMixedWidgetListState extends ScrollableState<ScrollableMixedWidg
...
@@ -712,7 +718,7 @@ class ScrollableMixedWidgetListState extends ScrollableState<ScrollableMixedWidg
builder:
config
.
builder
,
builder:
config
.
builder
,
token:
config
.
token
,
token:
config
.
token
,
onInvalidatorAvailable:
config
.
onInvalidatorAvailable
,
onInvalidatorAvailable:
config
.
onInvalidatorAvailable
,
onExtent
sUpdate:
_handleExtentsUpdate
onExtent
Changed:
_handleExtentChanged
)
)
);
);
}
}
...
...
packages/flutter/test/widget/block_test.dart
View file @
135a38d6
...
@@ -50,16 +50,18 @@ void main() {
...
@@ -50,16 +50,18 @@ void main() {
]
]
)
)
);
);
tester
.
pump
();
// for SizeObservers
Point
middleOfContainer
=
tester
.
getCenter
(
tester
.
findText
(
'Hello'
));
Point
middleOfContainer
=
tester
.
getCenter
(
tester
.
findText
(
'Hello'
));
expect
(
middleOfContainer
.
x
,
equals
(
400.0
));
expect
(
middleOfContainer
.
y
,
equals
(
1000.0
));
Point
target
=
tester
.
getCenter
(
tester
.
findElementByKey
(
blockKey
));
Point
target
=
tester
.
getCenter
(
tester
.
findElementByKey
(
blockKey
));
TestGesture
gesture
=
tester
.
startGesture
(
target
);
TestGesture
gesture
=
tester
.
startGesture
(
target
);
gesture
.
moveBy
(
const
Offset
(
0.0
,
-
10.0
));
gesture
.
moveBy
(
const
Offset
(
0.0
,
-
10.0
));
tester
.
pump
(
const
Duration
(
milliseconds:
1
));
tester
.
pump
(
);
// redo layout
expect
(
tester
.
getCenter
(
tester
.
findText
(
'Hello'
))
==
middleOfContainer
,
isFalse
);
expect
(
tester
.
getCenter
(
tester
.
findText
(
'Hello'
))
,
isNot
(
equals
(
middleOfContainer
))
);
gesture
.
up
();
gesture
.
up
();
});
});
...
...
packages/flutter_test/lib/src/instrumentation.dart
View file @
135a38d6
...
@@ -19,6 +19,8 @@ class Instrumentation {
...
@@ -19,6 +19,8 @@ class Instrumentation {
final
WidgetFlutterBinding
binding
;
final
WidgetFlutterBinding
binding
;
/// Returns a list of all the [Layer] objects in the rendering.
List
<
Layer
>
get
layers
=>
_layers
(
binding
.
renderView
.
layer
);
// TODO(ianh): This should not be O(N) hidden behind a getter!
// TODO(ianh): This should not be O(N) hidden behind a getter!
List
<
Layer
>
_layers
(
Layer
layer
)
{
List
<
Layer
>
_layers
(
Layer
layer
)
{
List
<
Layer
>
result
=
<
Layer
>[
layer
];
List
<
Layer
>
result
=
<
Layer
>[
layer
];
...
@@ -32,9 +34,9 @@ class Instrumentation {
...
@@ -32,9 +34,9 @@ class Instrumentation {
}
}
return
result
;
return
result
;
}
}
List
<
Layer
>
get
layers
=>
_layers
(
binding
.
renderView
.
layer
);
/// Walks all the elements in the tree, in depth-first pre-order,
/// calling the given function for each one.
void
walkElements
(
ElementVisitor
visitor
)
{
void
walkElements
(
ElementVisitor
visitor
)
{
void
walk
(
Element
element
)
{
void
walk
(
Element
element
)
{
visitor
(
element
);
visitor
(
element
);
...
@@ -43,6 +45,9 @@ class Instrumentation {
...
@@ -43,6 +45,9 @@ class Instrumentation {
binding
.
renderViewElement
.
visitChildren
(
walk
);
binding
.
renderViewElement
.
visitChildren
(
walk
);
}
}
/// Returns the first element that for which the given predicate
/// function returns true, if any, or null if the predicate function
/// never returns true.
Element
findElement
(
bool
predicate
(
Element
element
))
{
Element
findElement
(
bool
predicate
(
Element
element
))
{
try
{
try
{
walkElements
((
Element
element
)
{
walkElements
((
Element
element
)
{
...
@@ -55,16 +60,24 @@ class Instrumentation {
...
@@ -55,16 +60,24 @@ class Instrumentation {
return
null
;
return
null
;
}
}
/// Returns the first element that corresponds to a widget with the
/// given [Key], or null if there is no such element.
Element
findElementByKey
(
Key
key
)
{
Element
findElementByKey
(
Key
key
)
{
return
findElement
((
Element
element
)
=>
element
.
widget
.
key
==
key
);
return
findElement
((
Element
element
)
=>
element
.
widget
.
key
==
key
);
}
}
/// Returns the first element that corresponds to a [Text] widget
/// whose data is the given string, or null if there is no such
/// element.
Element
findText
(
String
text
)
{
Element
findText
(
String
text
)
{
return
findElement
((
Element
element
)
{
return
findElement
((
Element
element
)
{
return
element
.
widget
is
Text
&&
element
.
widget
.
data
==
text
;
return
element
.
widget
is
Text
&&
element
.
widget
.
data
==
text
;
});
});
}
}
/// Returns the [State] object of the first element whose state has
/// the given [runtimeType], if any. Returns null if there is no
/// matching element.
State
findStateOfType
(
Type
type
)
{
State
findStateOfType
(
Type
type
)
{
StatefulComponentElement
element
=
findElement
((
Element
element
)
{
StatefulComponentElement
element
=
findElement
((
Element
element
)
{
return
element
is
StatefulComponentElement
&&
element
.
state
.
runtimeType
==
type
;
return
element
is
StatefulComponentElement
&&
element
.
state
.
runtimeType
==
type
;
...
@@ -72,6 +85,10 @@ class Instrumentation {
...
@@ -72,6 +85,10 @@ class Instrumentation {
return
element
?.
state
;
return
element
?.
state
;
}
}
/// Returns the [State] object of the first element whose
/// configuration is the given widget, if any. Returns null if the
/// given configuration is not that of a stateful widget or if there
/// is no matching element.
State
findStateByConfig
(
Widget
config
)
{
State
findStateByConfig
(
Widget
config
)
{
StatefulComponentElement
element
=
findElement
((
Element
element
)
{
StatefulComponentElement
element
=
findElement
((
Element
element
)
{
return
element
is
StatefulComponentElement
&&
element
.
state
.
config
==
config
;
return
element
is
StatefulComponentElement
&&
element
.
state
.
config
==
config
;
...
@@ -79,26 +96,36 @@ class Instrumentation {
...
@@ -79,26 +96,36 @@ class Instrumentation {
return
element
?.
state
;
return
element
?.
state
;
}
}
/// Returns the point at the center of the given element.
Point
getCenter
(
Element
element
)
{
Point
getCenter
(
Element
element
)
{
return
_getElementPoint
(
element
,
(
Size
size
)
=>
size
.
center
(
Point
.
origin
));
return
_getElementPoint
(
element
,
(
Size
size
)
=>
size
.
center
(
Point
.
origin
));
}
}
/// Returns the point at the top left of the given element.
Point
getTopLeft
(
Element
element
)
{
Point
getTopLeft
(
Element
element
)
{
return
_getElementPoint
(
element
,
(
_
)
=>
Point
.
origin
);
return
_getElementPoint
(
element
,
(
_
)
=>
Point
.
origin
);
}
}
/// Returns the point at the top right of the given element. This
/// point is not inside the object's hit test area.
Point
getTopRight
(
Element
element
)
{
Point
getTopRight
(
Element
element
)
{
return
_getElementPoint
(
element
,
(
Size
size
)
=>
size
.
topRight
(
Point
.
origin
));
return
_getElementPoint
(
element
,
(
Size
size
)
=>
size
.
topRight
(
Point
.
origin
));
}
}
/// Returns the point at the bottom left of the given element. This
/// point is not inside the object's hit test area.
Point
getBottomLeft
(
Element
element
)
{
Point
getBottomLeft
(
Element
element
)
{
return
_getElementPoint
(
element
,
(
Size
size
)
=>
size
.
bottomLeft
(
Point
.
origin
));
return
_getElementPoint
(
element
,
(
Size
size
)
=>
size
.
bottomLeft
(
Point
.
origin
));
}
}
/// Returns the point at the bottom right of the given element. This
/// point is not inside the object's hit test area.
Point
getBottomRight
(
Element
element
)
{
Point
getBottomRight
(
Element
element
)
{
return
_getElementPoint
(
element
,
(
Size
size
)
=>
size
.
bottomRight
(
Point
.
origin
));
return
_getElementPoint
(
element
,
(
Size
size
)
=>
size
.
bottomRight
(
Point
.
origin
));
}
}
/// Returns the size of the given element. This is only valid once
/// the element's render object has been laid out at least once.
Size
getSize
(
Element
element
)
{
Size
getSize
(
Element
element
)
{
assert
(
element
!=
null
);
assert
(
element
!=
null
);
RenderBox
box
=
element
.
renderObject
as
RenderBox
;
RenderBox
box
=
element
.
renderObject
as
RenderBox
;
...
@@ -113,22 +140,34 @@ class Instrumentation {
...
@@ -113,22 +140,34 @@ class Instrumentation {
return
box
.
localToGlobal
(
sizeToPoint
(
box
.
size
));
return
box
.
localToGlobal
(
sizeToPoint
(
box
.
size
));
}
}
/// Dispatch a pointer down / pointer up sequence at the center of
/// the given element, assuming it is exposed. If the center of the
/// element is not exposed, this might send events to another
/// object.
void
tap
(
Element
element
,
{
int
pointer:
1
})
{
void
tap
(
Element
element
,
{
int
pointer:
1
})
{
tapAt
(
getCenter
(
element
),
pointer:
pointer
);
tapAt
(
getCenter
(
element
),
pointer:
pointer
);
}
}
/// Dispatch a pointer down / pointer up sequence at the given
/// location.
void
tapAt
(
Point
location
,
{
int
pointer:
1
})
{
void
tapAt
(
Point
location
,
{
int
pointer:
1
})
{
HitTestResult
result
=
_hitTest
(
location
);
HitTestResult
result
=
_hitTest
(
location
);
TestPointer
p
=
new
TestPointer
(
pointer
);
TestPointer
p
=
new
TestPointer
(
pointer
);
_
dispatchEvent
(
p
.
down
(
location
),
result
);
dispatchEvent
(
p
.
down
(
location
),
result
);
_
dispatchEvent
(
p
.
up
(),
result
);
dispatchEvent
(
p
.
up
(),
result
);
}
}
/// Attempts a fling gesture starting from the center of the given
/// element, moving the given distance, reaching the given velocity.
///
/// If the middle of the element is not exposed, this might send
/// events to another object.
void
fling
(
Element
element
,
Offset
offset
,
double
velocity
,
{
int
pointer:
1
})
{
void
fling
(
Element
element
,
Offset
offset
,
double
velocity
,
{
int
pointer:
1
})
{
flingFrom
(
getCenter
(
element
),
offset
,
velocity
,
pointer:
pointer
);
flingFrom
(
getCenter
(
element
),
offset
,
velocity
,
pointer:
pointer
);
}
}
/// Attempts a fling gesture starting from the given location,
/// moving the given distance, reaching the given velocity.
void
flingFrom
(
Point
startLocation
,
Offset
offset
,
double
velocity
,
{
int
pointer:
1
})
{
void
flingFrom
(
Point
startLocation
,
Offset
offset
,
double
velocity
,
{
int
pointer:
1
})
{
assert
(
offset
.
distance
>
0.0
);
assert
(
offset
.
distance
>
0.0
);
assert
(
velocity
!=
0.0
);
// velocity is pixels/second
assert
(
velocity
!=
0.0
);
// velocity is pixels/second
...
@@ -137,53 +176,65 @@ class Instrumentation {
...
@@ -137,53 +176,65 @@ class Instrumentation {
const
int
kMoveCount
=
50
;
// Needs to be >= kHistorySize, see _LeastSquaresVelocityTrackerStrategy
const
int
kMoveCount
=
50
;
// Needs to be >= kHistorySize, see _LeastSquaresVelocityTrackerStrategy
final
double
timeStampDelta
=
1000.0
*
offset
.
distance
/
(
kMoveCount
*
velocity
);
final
double
timeStampDelta
=
1000.0
*
offset
.
distance
/
(
kMoveCount
*
velocity
);
double
timeStamp
=
0.0
;
double
timeStamp
=
0.0
;
_
dispatchEvent
(
p
.
down
(
startLocation
,
timeStamp:
new
Duration
(
milliseconds:
timeStamp
.
round
())),
result
);
dispatchEvent
(
p
.
down
(
startLocation
,
timeStamp:
new
Duration
(
milliseconds:
timeStamp
.
round
())),
result
);
for
(
int
i
=
0
;
i
<
kMoveCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
kMoveCount
;
i
++)
{
final
Point
location
=
startLocation
+
Offset
.
lerp
(
Offset
.
zero
,
offset
,
i
/
kMoveCount
);
final
Point
location
=
startLocation
+
Offset
.
lerp
(
Offset
.
zero
,
offset
,
i
/
kMoveCount
);
_
dispatchEvent
(
p
.
move
(
location
,
timeStamp:
new
Duration
(
milliseconds:
timeStamp
.
round
())),
result
);
dispatchEvent
(
p
.
move
(
location
,
timeStamp:
new
Duration
(
milliseconds:
timeStamp
.
round
())),
result
);
timeStamp
+=
timeStampDelta
;
timeStamp
+=
timeStampDelta
;
}
}
_
dispatchEvent
(
p
.
up
(
timeStamp:
new
Duration
(
milliseconds:
timeStamp
.
round
())),
result
);
dispatchEvent
(
p
.
up
(
timeStamp:
new
Duration
(
milliseconds:
timeStamp
.
round
())),
result
);
}
}
/// Attempts to drag the given element by the given offset, by
/// starting a drag in the middle of the element.
///
/// If the middle of the element is not exposed, this might send
/// events to another object.
void
scroll
(
Element
element
,
Offset
offset
,
{
int
pointer:
1
})
{
void
scroll
(
Element
element
,
Offset
offset
,
{
int
pointer:
1
})
{
scrollAt
(
getCenter
(
element
),
offset
,
pointer:
pointer
);
scrollAt
(
getCenter
(
element
),
offset
,
pointer:
pointer
);
}
}
/// Attempts a drag gesture consisting of a pointer down, a move by
/// the given offset, and a pointer up.
void
scrollAt
(
Point
startLocation
,
Offset
offset
,
{
int
pointer:
1
})
{
void
scrollAt
(
Point
startLocation
,
Offset
offset
,
{
int
pointer:
1
})
{
Point
endLocation
=
startLocation
+
offset
;
Point
endLocation
=
startLocation
+
offset
;
TestPointer
p
=
new
TestPointer
(
pointer
);
TestPointer
p
=
new
TestPointer
(
pointer
);
// Events for the entire press-drag-release gesture are dispatched
// Events for the entire press-drag-release gesture are dispatched
// to the widgets "hit" by the pointer down event.
// to the widgets "hit" by the pointer down event.
HitTestResult
result
=
_hitTest
(
startLocation
);
HitTestResult
result
=
_hitTest
(
startLocation
);
_
dispatchEvent
(
p
.
down
(
startLocation
),
result
);
dispatchEvent
(
p
.
down
(
startLocation
),
result
);
_
dispatchEvent
(
p
.
move
(
endLocation
),
result
);
dispatchEvent
(
p
.
move
(
endLocation
),
result
);
_
dispatchEvent
(
p
.
up
(),
result
);
dispatchEvent
(
p
.
up
(),
result
);
}
}
/// Begins a gesture at a particular point, and returns the
/// [TestGesture] object which you can use to continue the gesture.
TestGesture
startGesture
(
Point
downLocation
,
{
int
pointer:
1
})
{
TestGesture
startGesture
(
Point
downLocation
,
{
int
pointer:
1
})
{
TestPointer
p
=
new
TestPointer
(
pointer
);
TestPointer
p
=
new
TestPointer
(
pointer
);
HitTestResult
result
=
_hitTest
(
downLocation
);
HitTestResult
result
=
_hitTest
(
downLocation
);
_
dispatchEvent
(
p
.
down
(
downLocation
),
result
);
dispatchEvent
(
p
.
down
(
downLocation
),
result
);
return
new
TestGesture
.
_
(
this
,
result
,
p
);
return
new
TestGesture
.
_
(
this
,
result
,
p
);
}
}
@Deprecated
(
'soon. Use startGesture instead.'
)
void
dispatchEvent
(
PointerEvent
event
,
Point
location
)
{
_dispatchEvent
(
event
,
_hitTest
(
location
));
}
HitTestResult
_hitTest
(
Point
location
)
{
HitTestResult
_hitTest
(
Point
location
)
{
HitTestResult
result
=
new
HitTestResult
();
HitTestResult
result
=
new
HitTestResult
();
binding
.
hitTest
(
result
,
location
);
binding
.
hitTest
(
result
,
location
);
return
result
;
return
result
;
}
}
void
_dispatchEvent
(
PointerEvent
event
,
HitTestResult
result
)
{
/// Sends a [PointerEvent] at a particular [HitTestResult].
///
/// Generally speaking, it is preferred to use one of the more
/// semantically meaningful ways to dispatch events in tests, in
/// particular: [tap], [tapAt], [fling], [flingFrom], [scroll],
/// [scrollAt], or [startGesture].
void
dispatchEvent
(
PointerEvent
event
,
HitTestResult
result
)
{
binding
.
dispatchEvent
(
event
,
result
);
binding
.
dispatchEvent
(
event
,
result
);
}
}
}
}
/// A class for performing gestures in tests. To create a
/// [TestGesture], call [WidgetTester.startGesture].
class
TestGesture
{
class
TestGesture
{
TestGesture
.
_
(
this
.
_target
,
this
.
_result
,
this
.
pointer
);
TestGesture
.
_
(
this
.
_target
,
this
.
_result
,
this
.
pointer
);
...
@@ -192,25 +243,31 @@ class TestGesture {
...
@@ -192,25 +243,31 @@ class TestGesture {
final
TestPointer
pointer
;
final
TestPointer
pointer
;
bool
_isDown
=
true
;
bool
_isDown
=
true
;
/// Send a move event moving the pointer to the given location.
void
moveTo
(
Point
location
)
{
void
moveTo
(
Point
location
)
{
assert
(
_isDown
);
assert
(
_isDown
);
_target
.
_
dispatchEvent
(
pointer
.
move
(
location
),
_result
);
_target
.
dispatchEvent
(
pointer
.
move
(
location
),
_result
);
}
}
/// Send a move event moving the pointer by the given offset.
void
moveBy
(
Offset
offset
)
{
void
moveBy
(
Offset
offset
)
{
assert
(
_isDown
);
assert
(
_isDown
);
moveTo
(
pointer
.
location
+
offset
);
moveTo
(
pointer
.
location
+
offset
);
}
}
/// End the gesture by releasing the pointer.
void
up
()
{
void
up
()
{
assert
(
_isDown
);
assert
(
_isDown
);
_isDown
=
false
;
_isDown
=
false
;
_target
.
_
dispatchEvent
(
pointer
.
up
(),
_result
);
_target
.
dispatchEvent
(
pointer
.
up
(),
_result
);
}
}
/// End the gesture by canceling the pointer (as would happen if the
/// system showed a modal dialog on top of the Flutter application,
/// for instance).
void
cancel
()
{
void
cancel
()
{
assert
(
_isDown
);
assert
(
_isDown
);
_isDown
=
false
;
_isDown
=
false
;
_target
.
_
dispatchEvent
(
pointer
.
cancel
(),
_result
);
_target
.
dispatchEvent
(
pointer
.
cancel
(),
_result
);
}
}
}
}
packages/flutter_test/lib/src/widget_tester.dart
View file @
135a38d6
...
@@ -6,6 +6,7 @@ import 'dart:ui' as ui show window;
...
@@ -6,6 +6,7 @@ import 'dart:ui' as ui show window;
import
'package:quiver/testing/async.dart'
;
import
'package:quiver/testing/async.dart'
;
import
'package:quiver/time.dart'
;
import
'package:quiver/time.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/widgets.dart'
;
...
@@ -57,6 +58,11 @@ class WidgetTester extends Instrumentation {
...
@@ -57,6 +58,11 @@ class WidgetTester extends Instrumentation {
);
);
async
.
flushMicrotasks
();
async
.
flushMicrotasks
();
}
}
void
dispatchEvent
(
PointerEvent
event
,
HitTestResult
result
)
{
super
.
dispatchEvent
(
event
,
result
);
async
.
flushMicrotasks
();
}
}
}
void
testWidgets
(
callback
(
WidgetTester
tester
))
{
void
testWidgets
(
callback
(
WidgetTester
tester
))
{
...
...
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