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
fc83640c
Commit
fc83640c
authored
Sep 21, 2016
by
Hans Muller
Committed by
GitHub
Sep 21, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ClampOverscrolls clamps Scrollable, not its Viewport (#5909)
parent
035afc2c
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
180 additions
and
78 deletions
+180
-78
smoke_test.dart
examples/flutter_gallery/test/smoke_test.dart
+1
-1
overscroll_indicator.dart
packages/flutter/lib/src/material/overscroll_indicator.dart
+2
-2
clamp_overscrolls.dart
packages/flutter/lib/src/widgets/clamp_overscrolls.dart
+4
-26
lazy_block.dart
packages/flutter/lib/src/widgets/lazy_block.dart
+3
-8
scrollable.dart
packages/flutter/lib/src/widgets/scrollable.dart
+53
-17
scrollable_grid.dart
packages/flutter/lib/src/widgets/scrollable_grid.dart
+3
-8
scrollable_list.dart
packages/flutter/lib/src/widgets/scrollable_list.dart
+6
-15
date_picker_test.dart
packages/flutter/test/material/date_picker_test.dart
+1
-1
clamp_overscrolls_test.dart
packages/flutter/test/widget/clamp_overscrolls_test.dart
+76
-0
scrollable_list_hit_testing_test.dart
...flutter/test/widget/scrollable_list_hit_testing_test.dart
+31
-0
No files found.
examples/flutter_gallery/test/smoke_test.dart
View file @
fc83640c
...
...
@@ -82,7 +82,7 @@ void main() {
await
tester
.
scroll
(
findGalleryItemByRouteName
(
tester
,
routeName
),
new
Offset
(
0.0
,
scrollDeltas
[
i
]));
await
tester
.
pump
();
// start the scroll
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
// wait for overscroll to timeout, if necessary
await
tester
.
pump
(
const
Duration
(
milliseconds:
2000
));
// wait for overscroll to fade away, if necessary
await
tester
.
pump
(
const
Duration
(
seconds:
3
));
// wait for overscroll to fade away, if necessary
tester
.
binding
.
debugAssertNoTransientCallbacks
(
'A transient callback was still active after leaving route
$routeName
'
);
}
...
...
packages/flutter/lib/src/material/overscroll_indicator.dart
View file @
fc83640c
...
...
@@ -151,7 +151,7 @@ class _OverscrollIndicatorState extends State<OverscrollIndicator> {
return
;
final
ExtentScrollBehavior
scrollBehavior
=
scrollable
.
scrollBehavior
;
_scrollDirection
=
scrollable
.
config
.
scrollDirection
;
_scrollOffset
=
scrollable
.
s
crollOffset
;
_scrollOffset
=
scrollable
.
virtualS
crollOffset
;
_minScrollOffset
=
scrollBehavior
.
minScrollOffset
;
_maxScrollOffset
=
scrollBehavior
.
maxScrollOffset
;
}
...
...
@@ -166,7 +166,7 @@ class _OverscrollIndicatorState extends State<OverscrollIndicator> {
if
(!
_scrollUnderway
)
// The hide timer has run.
return
;
final
double
value
=
scrollable
.
s
crollOffset
;
final
double
value
=
scrollable
.
virtualS
crollOffset
;
if
(
_isOverscroll
(
value
))
{
_refreshHideTimer
();
// Hide the indicator as soon as user starts scrolling in the reverse direction of overscroll.
...
...
packages/flutter/lib/src/widgets/clamp_overscrolls.dart
View file @
fc83640c
...
...
@@ -83,9 +83,10 @@ class ClampOverscrolls extends InheritedWidget {
/// constraints are applied.
final
ScrollableEdge
edge
;
/// Return the [scrollable]'s scrollOffset clamped according to [edge].
double
clampScrollOffset
(
ScrollableState
scrollable
)
{
final
double
scrollOffset
=
scrollable
.
scrollOffset
;
/// Return the [newScrollOffset] clamped according to [edge] and [scrollable]'s
/// scroll behavior. The value of [newScrollOffset] defaults to `scrollable.scrollOffset`.
double
clampScrollOffset
(
ScrollableState
scrollable
,
[
double
newScrollOffset
])
{
final
double
scrollOffset
=
newScrollOffset
??
scrollable
.
scrollOffset
;
final
double
minScrollOffset
=
scrollable
.
scrollBehavior
.
minScrollOffset
;
final
double
maxScrollOffset
=
scrollable
.
scrollBehavior
.
maxScrollOffset
;
switch
(
edge
)
{
...
...
@@ -106,29 +107,6 @@ class ClampOverscrolls extends InheritedWidget {
return
context
.
inheritFromWidgetOfExactType
(
ClampOverscrolls
);
}
/// Clamps the new viewport's scroll offset according to the value of
/// `ClampOverscrolls.of(context).edge`.
///
/// The clamped overscroll edge is reset to [ScrollableEdge.none] for the viewport's
/// descendants.
///
/// This utility function is typically used by [Scrollable.builder] callbacks.
static
Widget
buildViewport
(
BuildContext
context
,
ScrollableState
state
,
ViewportBuilder
builder
)
{
// TODO(ianh): minScrollOffset and maxScrollOffset are typically determined
// by the container and content size. But we don't know those until we
// layout the viewport, which happens after build phase. We need to rethink
// this.
final
ClampOverscrolls
clampOverscrolls
=
ClampOverscrolls
.
of
(
context
);
if
(
clampOverscrolls
==
null
)
return
builder
(
context
,
state
,
state
.
scrollOffset
);
final
double
clampedScrollOffset
=
clampOverscrolls
.
clampScrollOffset
(
state
);
Widget
viewport
=
builder
(
context
,
state
,
clampedScrollOffset
);
if
(
clampOverscrolls
.
edge
!=
ScrollableEdge
.
none
)
viewport
=
new
ClampOverscrolls
(
edge:
ScrollableEdge
.
none
,
child:
viewport
);
return
viewport
;
}
@override
bool
updateShouldNotify
(
ClampOverscrolls
old
)
=>
edge
!=
old
.
edge
;
...
...
packages/flutter/lib/src/widgets/lazy_block.dart
View file @
fc83640c
...
...
@@ -8,7 +8,6 @@ import 'package:flutter/rendering.dart';
import
'package:meta/meta.dart'
;
import
'basic.dart'
;
import
'clamp_overscrolls.dart'
;
import
'framework.dart'
;
import
'scroll_configuration.dart'
;
import
'scrollable.dart'
;
...
...
@@ -259,9 +258,9 @@ class LazyBlock extends StatelessWidget {
/// See [LazyBlockDelegate] for details.
final
LazyBlockDelegate
delegate
;
Widget
_buildViewport
(
BuildContext
context
,
ScrollableState
state
,
double
scrollOffset
)
{
Widget
_buildViewport
(
BuildContext
context
,
ScrollableState
state
)
{
return
new
LazyBlockViewport
(
startOffset:
scrollOffset
,
startOffset:
s
tate
.
s
crollOffset
,
mainAxis:
scrollDirection
,
padding:
padding
,
onExtentsChanged:
(
int
firstIndex
,
int
lastIndex
,
double
firstStartOffset
,
double
lastEndOffset
,
double
minScrollOffset
,
double
containerExtent
)
{
...
...
@@ -278,10 +277,6 @@ class LazyBlock extends StatelessWidget {
);
}
Widget
_buildContent
(
BuildContext
context
,
ScrollableState
state
)
{
return
ClampOverscrolls
.
buildViewport
(
context
,
state
,
_buildViewport
);
}
@override
Widget
build
(
BuildContext
context
)
{
final
Widget
result
=
new
Scrollable
(
...
...
@@ -292,7 +287,7 @@ class LazyBlock extends StatelessWidget {
onScroll:
onScroll
,
onScrollEnd:
onScrollEnd
,
snapOffsetCallback:
snapOffsetCallback
,
builder:
_build
Conten
t
builder:
_build
Viewpor
t
);
return
ScrollConfiguration
.
wrap
(
context
,
result
);
}
...
...
packages/flutter/lib/src/widgets/scrollable.dart
View file @
fc83640c
...
...
@@ -270,12 +270,14 @@ class ScrollableState<T extends Scrollable> extends State<T> {
..
addListener
(
_handleAnimationChanged
)
..
addStatusListener
(
_handleAnimationStatusChanged
);
_scrollOffset
=
PageStorage
.
of
(
context
)?.
readState
(
context
)
??
config
.
initialScrollOffset
??
0.0
;
_virtualScrollOffset
=
_scrollOffset
;
}
Simulation
_simulation
;
// if we're flinging, then this is the animation with which we're doing it
AnimationController
_controller
;
double
_contentExtent
;
double
_containerExtent
;
bool
_scrollUnderway
=
false
;
@override
void
dispose
()
{
...
...
@@ -300,9 +302,31 @@ class ScrollableState<T extends Scrollable> extends State<T> {
/// The scroll offset is applied to the child widget along the scroll
/// direction before painting. A positive scroll offset indicates that
/// more content in the preferred reading direction is visible.
///
/// The scroll offset's value may be above or below the limits defined
/// by the [scrollBehavior]. This is called "overscrolling" and it can be
/// prevented with the [ClampOverscrolls] widget.
///
/// See also:
///
/// * [virtualScrollOffset]
/// * [initialScrollOffset]
/// * [onScrollStart]
/// * [onScroll]
/// * [onScrollEnd]
/// * [ScrollNotification]
double
get
scrollOffset
=>
_scrollOffset
;
double
_scrollOffset
;
/// The current scroll offset, irrespective of the constraints defined
/// by any [ClampOverscrolls] widget ancestors.
///
/// See also:
///
/// * [scrollOffset]
double
get
virtualScrollOffset
=>
_virtualScrollOffset
;
double
_virtualScrollOffset
;
/// Convert a position or velocity measured in terms of pixels to a scrollOffset.
/// Scrollable gesture handlers convert their incoming values with this method.
/// Subclasses that define scrollOffset in units other than pixels must
...
...
@@ -387,7 +411,7 @@ class ScrollableState<T extends Scrollable> extends State<T> {
bool
_scrollOffsetIsInBounds
(
double
scrollOffset
)
{
if
(
scrollBehavior
is
!
ExtentScrollBehavior
)
return
false
;
ExtentScrollBehavior
behavior
=
scrollBehavior
;
final
ExtentScrollBehavior
behavior
=
scrollBehavior
;
return
scrollOffset
>=
behavior
.
minScrollOffset
&&
scrollOffset
<
behavior
.
maxScrollOffset
;
}
...
...
@@ -398,16 +422,23 @@ class ScrollableState<T extends Scrollable> extends State<T> {
void
_handleAnimationStatusChanged
(
AnimationStatus
status
)
{
// this is not called when stop() is called on the controller
setState
(()
{
if
(!
_controller
.
isAnimating
)
if
(!
_controller
.
isAnimating
)
{
_simulation
=
null
;
_scrollUnderway
=
false
;
}
});
}
void
_setScrollOffset
(
double
newScrollOffset
,
{
DragUpdateDetails
details
})
{
if
(
_scrollOffset
==
newScrollOffset
)
return
;
final
ClampOverscrolls
clampOverscrolls
=
ClampOverscrolls
.
of
(
context
);
final
double
clampedScrollOffset
=
clampOverscrolls
?.
clampScrollOffset
(
this
,
newScrollOffset
)
??
newScrollOffset
;
setState
(()
{
_scrollOffset
=
newScrollOffset
;
_virtualScrollOffset
=
newScrollOffset
;
_scrollUnderway
=
_scrollOffset
!=
clampedScrollOffset
;
_scrollOffset
=
clampedScrollOffset
;
});
PageStorage
.
of
(
context
)?.
writeState
(
context
,
_scrollOffset
);
_startScroll
();
...
...
@@ -429,7 +460,7 @@ class ScrollableState<T extends Scrollable> extends State<T> {
Curve
curve:
Curves
.
ease
,
DragUpdateDetails
details
})
{
double
newScrollOffset
=
scrollBehavior
.
applyCurve
(
_s
crollOffset
,
scrollDelta
);
double
newScrollOffset
=
scrollBehavior
.
applyCurve
(
virtualS
crollOffset
,
scrollDelta
);
return
scrollTo
(
newScrollOffset
,
duration:
duration
,
curve:
curve
,
details:
details
);
}
...
...
@@ -461,7 +492,7 @@ class ScrollableState<T extends Scrollable> extends State<T> {
Future
<
Null
>
_animateTo
(
double
newScrollOffset
,
Duration
duration
,
Curve
curve
)
{
_stop
();
_controller
.
value
=
s
crollOffset
;
_controller
.
value
=
virtualS
crollOffset
;
_startScroll
();
return
_controller
.
animateTo
(
newScrollOffset
,
duration:
duration
,
curve:
curve
).
then
((
Null
_
)
{
_endScroll
();
...
...
@@ -526,7 +557,8 @@ class ScrollableState<T extends Scrollable> extends State<T> {
// If a scroll animation isn't underway already and we're overscrolled or we're
// going to have to snap the scroll offset, then animate the scroll offset to its
// final value.
if
(!
_controller
.
isAnimating
&&
(
shouldSnapScrollOffset
||
!
_scrollOffsetIsInBounds
(
scrollOffset
)))
if
(!
_controller
.
isAnimating
&&
(
shouldSnapScrollOffset
||
!
_scrollOffsetIsInBounds
(
scrollOffset
)))
return
settleScrollOffset
();
return
new
Future
<
Null
>.
value
();
...
...
@@ -573,14 +605,14 @@ class ScrollableState<T extends Scrollable> extends State<T> {
if
(
endScrollOffset
.
isNaN
)
return
null
;
final
double
snappedScrollOffset
=
snapScrollOffset
(
endScrollOffset
);
// Calls the config.snapOffsetCallback callback
final
double
snappedScrollOffset
=
snapScrollOffset
(
endScrollOffset
);
if
(!
_scrollOffsetIsInBounds
(
snappedScrollOffset
))
return
null
;
final
double
snapVelocity
=
scrollVelocity
.
abs
()
*
(
snappedScrollOffset
-
scrollOffset
).
sign
;
final
double
endVelocity
=
pixelOffsetToScrollOffset
(
kPixelScrollTolerance
.
velocity
).
abs
()
*
(
scrollVelocity
<
0.0
?
-
1.0
:
1.0
);
Simulation
toSnapSimulation
=
scrollBehavior
.
createSnapScrollSimulation
(
s
crollOffset
,
snappedScrollOffset
,
snapVelocity
,
endVelocity
virtualS
crollOffset
,
snappedScrollOffset
,
snapVelocity
,
endVelocity
);
if
(
toSnapSimulation
==
null
)
return
null
;
...
...
@@ -591,7 +623,7 @@ class ScrollableState<T extends Scrollable> extends State<T> {
}
Simulation
_createFlingSimulation
(
double
scrollVelocity
)
{
final
Simulation
simulation
=
scrollBehavior
.
createScrollSimulation
(
s
crollOffset
,
scrollVelocity
);
final
Simulation
simulation
=
scrollBehavior
.
createScrollSimulation
(
virtualS
crollOffset
,
scrollVelocity
);
if
(
simulation
!=
null
)
{
final
double
endVelocity
=
pixelOffsetToScrollOffset
(
kPixelScrollTolerance
.
velocity
).
abs
();
final
double
endDistance
=
pixelOffsetToScrollOffset
(
kPixelScrollTolerance
.
distance
).
abs
();
...
...
@@ -672,6 +704,14 @@ class ScrollableState<T extends Scrollable> extends State<T> {
_numberOfInProgressScrolls
-=
1
;
if
(
_numberOfInProgressScrolls
==
0
)
{
_simulation
=
null
;
if
(
_scrollUnderway
&&
mounted
)
{
// If the scroll hasn't already stopped because we've hit a clamped
// edge or the controller stopped animating, then rebuild the Scrollable
// with the IgnorePointer widget turned off.
setState
(()
{
_scrollUnderway
=
false
;
});
}
dispatchOnScrollEnd
();
if
(
mounted
)
{
new
ScrollNotification
(
...
...
@@ -701,7 +741,7 @@ class ScrollableState<T extends Scrollable> extends State<T> {
gestures:
buildGestureDetectors
(),
behavior:
HitTestBehavior
.
opaque
,
child:
new
IgnorePointer
(
ignoring:
_
controller
.
isAnimating
,
ignoring:
_
scrollUnderway
,
child:
buildContent
(
context
)
)
);
...
...
@@ -940,9 +980,9 @@ class ScrollableViewport extends StatelessWidget {
/// The widget that will be scrolled. It will become the child of a Scrollable.
final
Widget
child
;
Widget
_buildViewport
(
BuildContext
context
,
ScrollableState
state
,
double
scrollOffset
)
{
Widget
_buildViewport
(
BuildContext
context
,
ScrollableState
state
)
{
return
new
Viewport
(
paintOffset:
state
.
scrollOffsetToPixelDelta
(
scrollOffset
),
paintOffset:
state
.
scrollOffsetToPixelDelta
(
s
tate
.
s
crollOffset
),
mainAxis:
scrollDirection
,
anchor:
scrollAnchor
,
onPaintOffsetUpdateNeeded:
(
ViewportDimensions
dimensions
)
{
...
...
@@ -955,10 +995,6 @@ class ScrollableViewport extends StatelessWidget {
);
}
Widget
_buildContent
(
BuildContext
context
,
ScrollableState
state
)
{
return
ClampOverscrolls
.
buildViewport
(
context
,
state
,
_buildViewport
);
}
@override
Widget
build
(
BuildContext
context
)
{
final
Widget
result
=
new
Scrollable
(
...
...
@@ -970,7 +1006,7 @@ class ScrollableViewport extends StatelessWidget {
onScroll:
onScroll
,
onScrollEnd:
onScrollEnd
,
snapOffsetCallback:
snapOffsetCallback
,
builder:
_build
Conten
t
builder:
_build
Viewpor
t
);
return
ScrollConfiguration
.
wrap
(
context
,
result
);
}
...
...
packages/flutter/lib/src/widgets/scrollable_grid.dart
View file @
fc83640c
...
...
@@ -8,7 +8,6 @@ import 'package:collection/collection.dart' show lowerBound;
import
'package:flutter/rendering.dart'
;
import
'package:meta/meta.dart'
;
import
'clamp_overscrolls.dart'
;
import
'framework.dart'
;
import
'scroll_configuration.dart'
;
import
'scrollable.dart'
;
...
...
@@ -81,19 +80,15 @@ class ScrollableGrid extends StatelessWidget {
/// The children that will be placed in the grid.
final
Iterable
<
Widget
>
children
;
Widget
_buildViewport
(
BuildContext
context
,
ScrollableState
state
,
double
scrollOffset
)
{
Widget
_buildViewport
(
BuildContext
context
,
ScrollableState
state
)
{
return
new
GridViewport
(
scrollOffset:
scrollOffset
,
scrollOffset:
s
tate
.
s
crollOffset
,
delegate:
delegate
,
onExtentsChanged:
state
.
handleExtentsChanged
,
children:
children
);
}
Widget
_buildContent
(
BuildContext
context
,
ScrollableState
state
)
{
return
ClampOverscrolls
.
buildViewport
(
context
,
state
,
_buildViewport
);
}
@override
Widget
build
(
BuildContext
context
)
{
final
Widget
result
=
new
Scrollable
(
...
...
@@ -107,7 +102,7 @@ class ScrollableGrid extends StatelessWidget {
onScroll:
onScroll
,
onScrollEnd:
onScrollEnd
,
snapOffsetCallback:
snapOffsetCallback
,
builder:
_build
Content
builder:
_build
Viewport
,
);
return
ScrollConfiguration
.
wrap
(
context
,
result
);
}
...
...
packages/flutter/lib/src/widgets/scrollable_list.dart
View file @
fc83640c
...
...
@@ -7,7 +7,6 @@ import 'dart:math' as math;
import
'package:flutter/rendering.dart'
;
import
'package:meta/meta.dart'
;
import
'clamp_overscrolls.dart'
;
import
'framework.dart'
;
import
'scroll_configuration.dart'
;
import
'scrollable.dart'
;
...
...
@@ -126,12 +125,12 @@ class ScrollableList extends StatelessWidget {
/// The children, some of which might be materialized.
final
Iterable
<
Widget
>
children
;
Widget
_buildViewport
(
BuildContext
context
,
ScrollableState
state
,
double
scrollOffset
)
{
Widget
_buildViewport
(
BuildContext
context
,
ScrollableState
state
)
{
return
new
ListViewport
(
onExtentsChanged:
(
double
contentExtent
,
double
containerExtent
)
{
state
.
handleExtentsChanged
(
itemsWrap
?
double
.
INFINITY
:
contentExtent
,
containerExtent
);
},
scrollOffset:
scrollOffset
,
scrollOffset:
s
tate
.
s
crollOffset
,
mainAxis:
scrollDirection
,
anchor:
scrollAnchor
,
itemExtent:
itemExtent
,
...
...
@@ -141,10 +140,6 @@ class ScrollableList extends StatelessWidget {
);
}
Widget
_buildContent
(
BuildContext
context
,
ScrollableState
state
)
{
return
ClampOverscrolls
.
buildViewport
(
context
,
state
,
_buildViewport
);
}
@override
Widget
build
(
BuildContext
context
)
{
final
Widget
result
=
new
Scrollable
(
...
...
@@ -156,7 +151,7 @@ class ScrollableList extends StatelessWidget {
onScroll:
onScroll
,
onScrollEnd:
onScrollEnd
,
snapOffsetCallback:
snapOffsetCallback
,
builder:
_build
Conten
t
builder:
_build
Viewpor
t
);
return
ScrollConfiguration
.
wrap
(
context
,
result
);
}
...
...
@@ -529,10 +524,10 @@ class ScrollableLazyList extends StatelessWidget {
/// The amount of space by which to inset the children inside the viewport.
final
EdgeInsets
padding
;
Widget
_buildViewport
(
BuildContext
context
,
ScrollableState
state
,
double
scrollOffset
)
{
Widget
_buildViewport
(
BuildContext
context
,
ScrollableState
state
)
{
return
new
LazyListViewport
(
onExtentsChanged:
state
.
handleExtentsChanged
,
scrollOffset:
scrollOffset
,
scrollOffset:
s
tate
.
s
crollOffset
,
mainAxis:
scrollDirection
,
anchor:
scrollAnchor
,
itemExtent:
itemExtent
,
...
...
@@ -542,10 +537,6 @@ class ScrollableLazyList extends StatelessWidget {
);
}
Widget
_buildContent
(
BuildContext
context
,
ScrollableState
state
)
{
return
ClampOverscrolls
.
buildViewport
(
context
,
state
,
_buildViewport
);
}
@override
Widget
build
(
BuildContext
context
)
{
final
Widget
result
=
new
Scrollable
(
...
...
@@ -557,7 +548,7 @@ class ScrollableLazyList extends StatelessWidget {
onScroll:
onScroll
,
onScrollEnd:
onScrollEnd
,
snapOffsetCallback:
snapOffsetCallback
,
builder:
_build
Conten
t
builder:
_build
Viewpor
t
);
return
ScrollConfiguration
.
wrap
(
context
,
result
);
}
...
...
packages/flutter/test/material/date_picker_test.dart
View file @
fc83640c
...
...
@@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'tap-select a
n
day'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'tap-select a day'
,
(
WidgetTester
tester
)
async
{
Key
_datePickerKey
=
new
UniqueKey
();
DateTime
_selectedDate
=
new
DateTime
(
2016
,
DateTime
.
JULY
,
26
);
...
...
packages/flutter/test/widget/clamp_overscrolls_test.dart
View file @
fc83640c
...
...
@@ -45,6 +45,9 @@ void main() {
return
new
Future
<
Point
>.
value
(
widgetOrigin
);
}
// Each of the blocks below test overscrolling the top and bottom
// with a value for ClampOverscrolls.edge.
await
tester
.
pumpWidget
(
buildFrame
(
ScrollableEdge
.
none
));
Point
origin
=
await
locationAfterScroll
(
'top'
,
const
Offset
(
0.0
,
400.0
));
expect
(
origin
.
y
,
greaterThan
(
0.0
));
...
...
@@ -70,4 +73,77 @@ void main() {
origin
=
await
locationAfterScroll
(
'bottom'
,
const
Offset
(
0.0
,
-
400.0
));
expect
(
origin
.
y
,
equals
(
500.0
));
});
testWidgets
(
'ClampOverscrolls affects scrollOffset not virtualScrollOffset'
,
(
WidgetTester
tester
)
async
{
// ClampOverscrolls.edge == ScrollableEdge.none
await
tester
.
pumpWidget
(
buildFrame
(
ScrollableEdge
.
none
));
StatefulElement
statefulElement
=
tester
.
element
(
find
.
byType
(
Scrollable
));
ScrollableState
scrollable
=
statefulElement
.
state
;
await
tester
.
scrollAt
(
tester
.
getTopLeft
(
find
.
text
(
'top'
)),
const
Offset
(
0.0
,
400.0
));
await
tester
.
pump
();
expect
(
scrollable
.
scrollOffset
,
lessThan
(
0.0
));
expect
(
scrollable
.
virtualScrollOffset
,
equals
(
scrollable
.
scrollOffset
));
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
// Allow overscroll to settle
await
tester
.
scrollAt
(
tester
.
getTopLeft
(
find
.
text
(
'bottom'
)),
const
Offset
(
0.0
,
-
400.0
));
await
tester
.
pump
();
expect
(
scrollable
.
scrollOffset
,
greaterThan
(
0.0
));
expect
(
scrollable
.
virtualScrollOffset
,
equals
(
scrollable
.
scrollOffset
));
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
// Allow overscroll to settle
// ClampOverscrolls.edge == ScrollableEdge.both
await
tester
.
pumpWidget
(
buildFrame
(
ScrollableEdge
.
both
));
statefulElement
=
tester
.
element
(
find
.
byType
(
Scrollable
));
scrollable
=
statefulElement
.
state
;
await
tester
.
scrollAt
(
tester
.
getTopLeft
(
find
.
text
(
'top'
)),
const
Offset
(
0.0
,
400.0
));
await
tester
.
pump
();
expect
(
scrollable
.
scrollOffset
,
equals
(
0.0
));
expect
(
scrollable
.
virtualScrollOffset
,
lessThan
(
0.0
));
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
// Allow overscroll to settle
await
tester
.
scrollAt
(
tester
.
getTopLeft
(
find
.
text
(
'bottom'
)),
const
Offset
(
0.0
,
-
400.0
));
await
tester
.
pump
();
expect
(
scrollable
.
scrollOffset
,
equals
(
50.0
));
expect
(
scrollable
.
virtualScrollOffset
,
greaterThan
(
50.0
));
// ClampOverscrolls.edge == ScrollableEdge.leading
await
tester
.
pumpWidget
(
buildFrame
(
ScrollableEdge
.
leading
));
statefulElement
=
tester
.
element
(
find
.
byType
(
Scrollable
));
scrollable
=
statefulElement
.
state
;
await
tester
.
scrollAt
(
tester
.
getTopLeft
(
find
.
text
(
'top'
)),
const
Offset
(
0.0
,
400.0
));
await
tester
.
pump
();
expect
(
scrollable
.
scrollOffset
,
equals
(
0.0
));
expect
(
scrollable
.
virtualScrollOffset
,
lessThan
(
0.0
));
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
// Allow overscroll to settle
await
tester
.
scrollAt
(
tester
.
getTopLeft
(
find
.
text
(
'bottom'
)),
const
Offset
(
0.0
,
-
400.0
));
await
tester
.
pump
();
expect
(
scrollable
.
scrollOffset
,
greaterThan
(
0.0
));
expect
(
scrollable
.
virtualScrollOffset
,
equals
(
scrollable
.
scrollOffset
));
// ClampOverscrolls.edge == ScrollableEdge.trailing
await
tester
.
pumpWidget
(
buildFrame
(
ScrollableEdge
.
trailing
));
statefulElement
=
tester
.
element
(
find
.
byType
(
Scrollable
));
scrollable
=
statefulElement
.
state
;
await
tester
.
scrollAt
(
tester
.
getTopLeft
(
find
.
text
(
'top'
)),
const
Offset
(
0.0
,
400.0
));
await
tester
.
pump
();
expect
(
scrollable
.
scrollOffset
,
lessThan
(
0.0
));
expect
(
scrollable
.
virtualScrollOffset
,
equals
(
scrollable
.
scrollOffset
));
expect
(
scrollable
.
virtualScrollOffset
,
equals
(
scrollable
.
scrollOffset
));
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
// Allow overscroll to settle
await
tester
.
scrollAt
(
tester
.
getTopLeft
(
find
.
text
(
'bottom'
)),
const
Offset
(
0.0
,
-
400.0
));
await
tester
.
pump
();
expect
(
scrollable
.
scrollOffset
,
equals
(
50.0
));
expect
(
scrollable
.
virtualScrollOffset
,
greaterThan
(
50.0
));
});
}
packages/flutter/test/widget/scrollable_list_hit_testing_test.dart
View file @
fc83640c
...
...
@@ -151,4 +151,35 @@ void main() {
await
tester
.
tapAt
(
new
Point
(
800.0
-
16.0
,
200.0
));
expect
(
tapped
,
equals
(<
int
>[
5
,
4
,
4
]));
});
testWidgets
(
'Tap immediately following clamped overscroll'
,
(
WidgetTester
tester
)
async
{
// Regression test for https://github.com/flutter/flutter/issues/5709
GlobalKey
<
ScrollableState
>
scrollableKey
=
new
GlobalKey
<
ScrollableState
>();
List
<
int
>
tapped
=
<
int
>[];
await
tester
.
pumpWidget
(
new
ClampOverscrolls
(
edge:
ScrollableEdge
.
both
,
child:
new
ScrollableList
(
scrollableKey:
scrollableKey
,
itemExtent:
200.0
,
children:
items
.
map
((
int
item
)
{
return
new
Container
(
child:
new
GestureDetector
(
onTap:
()
{
tapped
.
add
(
item
);
},
child:
new
Text
(
'
$item
'
)
)
);
})
)
)
);
await
tester
.
fling
(
find
.
text
(
'0'
),
const
Offset
(
0.0
,
400.0
),
1000.0
);
expect
(
scrollableKey
.
currentState
.
scrollOffset
,
equals
(
0.0
));
expect
(
scrollableKey
.
currentState
.
virtualScrollOffset
,
lessThan
(
0.0
));
await
tester
.
tapAt
(
new
Point
(
200.0
,
100.0
));
expect
(
tapped
,
equals
(<
int
>[
0
]));
});
}
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