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
70d96ecb
Commit
70d96ecb
authored
Feb 09, 2016
by
Adam Barth
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve unit conversions in Scrollable
parent
c91ace82
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
111 additions
and
72 deletions
+111
-72
tabs.dart
packages/flutter/lib/src/material/tabs.dart
+3
-3
viewport.dart
packages/flutter/lib/src/rendering/viewport.dart
+5
-0
pageable_list.dart
packages/flutter/lib/src/widgets/pageable_list.dart
+21
-10
scrollable.dart
packages/flutter/lib/src/widgets/scrollable.dart
+75
-49
snap_scrolling_test.dart
packages/flutter/test/widget/snap_scrolling_test.dart
+7
-10
No files found.
packages/flutter/lib/src/material/tabs.dart
View file @
70d96ecb
...
...
@@ -922,12 +922,12 @@ class _TabBarViewState extends PageableListState<TabBarView> implements TabBarSe
controller
.
value
=
scrollOffset
/
2.0
;
}
Future
fling
(
Offset
scrollVelocity
)
{
Future
fling
(
double
scrollVelocity
)
{
if
(
_selection
==
null
||
_selection
.
valueIsChanging
)
return
new
Future
.
value
();
if
(
scrollVelocity
.
dx
.
abs
()
>
_kMinFlingVelocity
)
{
final
int
selectionDelta
=
scrollVelocity
.
dx
>
0
?
-
1
:
1
;
if
(
scrollVelocity
.
abs
()
>
_kMinFlingVelocity
)
{
final
int
selectionDelta
=
scrollVelocity
.
sign
.
truncate
()
;
final
int
targetIndex
=
(
_selection
.
index
+
selectionDelta
).
clamp
(
0
,
_tabCount
-
1
);
_selection
.
value
=
_selection
.
values
[
targetIndex
];
return
new
Future
.
value
();
...
...
packages/flutter/lib/src/rendering/viewport.dart
View file @
70d96ecb
...
...
@@ -9,6 +9,11 @@ import 'package:vector_math/vector_math_64.dart';
import
'box.dart'
;
import
'object.dart'
;
enum
ViewportAnchor
{
start
,
end
,
}
abstract
class
HasScrollDirection
{
Axis
get
scrollDirection
;
}
...
...
packages/flutter/lib/src/widgets/pageable_list.dart
View file @
70d96ecb
...
...
@@ -5,7 +5,6 @@
import
'dart:async'
;
import
'package:flutter/animation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/rendering.dart'
;
import
'basic.dart'
;
...
...
@@ -65,12 +64,25 @@ class PageableListState<T extends PageableList> extends ScrollableState<T> {
int
get
itemCount
=>
config
.
children
?.
length
??
0
;
int
_previousItemCount
;
double
pixelToScrollOffset
(
double
value
)
{
double
get
_pixelsPerScrollUnit
{
final
RenderBox
box
=
context
.
findRenderObject
();
if
(
box
==
null
||
!
box
.
hasSize
)
return
0.0
;
final
double
pixelScrollExtent
=
config
.
scrollDirection
==
Axis
.
vertical
?
box
.
size
.
height
:
box
.
size
.
width
;
return
pixelScrollExtent
==
0.0
?
0.0
:
value
/
pixelScrollExtent
;
switch
(
config
.
scrollDirection
)
{
case
Axis
.
horizontal
:
return
box
.
size
.
width
;
case
Axis
.
vertical
:
return
box
.
size
.
height
;
}
}
double
pixelOffsetToScrollOffset
(
double
pixelOffset
)
{
final
double
pixelsPerScrollUnit
=
_pixelsPerScrollUnit
;
return
super
.
pixelOffsetToScrollOffset
(
pixelsPerScrollUnit
==
0.0
?
0.0
:
pixelOffset
/
pixelsPerScrollUnit
);
}
double
scrollOffsetToPixelOffset
(
double
scrollOffset
)
{
return
super
.
scrollOffsetToPixelOffset
(
scrollOffset
*
_pixelsPerScrollUnit
);
}
void
initState
()
{
...
...
@@ -143,7 +155,7 @@ class PageableListState<T extends PageableList> extends ScrollableState<T> {
ScrollBehavior
createScrollBehavior
()
=>
scrollBehavior
;
bool
get
s
napScrollOffsetChanges
=>
config
.
itemsSnapAlignment
==
ItemsSnapAlignment
.
item
;
bool
get
s
houldSnapScrollOffset
=>
config
.
itemsSnapAlignment
==
ItemsSnapAlignment
.
item
;
double
snapScrollOffset
(
double
newScrollOffset
)
{
final
double
previousItemOffset
=
newScrollOffset
.
floorToDouble
();
...
...
@@ -152,20 +164,19 @@ class PageableListState<T extends PageableList> extends ScrollableState<T> {
.
clamp
(
scrollBehavior
.
minScrollOffset
,
scrollBehavior
.
maxScrollOffset
);
}
Future
_flingToAdjacentItem
(
Offset
velocity
)
{
final
double
scrollVelocity
=
scrollDirectionVelocity
(
velocity
);
Future
_flingToAdjacentItem
(
double
scrollVelocity
)
{
final
double
newScrollOffset
=
snapScrollOffset
(
scrollOffset
+
scrollVelocity
.
sign
)
.
clamp
(
snapScrollOffset
(
scrollOffset
-
0.5
),
snapScrollOffset
(
scrollOffset
+
0.5
));
return
scrollTo
(
newScrollOffset
,
duration:
config
.
duration
,
curve:
config
.
curve
)
.
then
(
_notifyPageChanged
);
}
Future
fling
(
Offset
v
elocity
)
{
Future
fling
(
double
scrollV
elocity
)
{
switch
(
config
.
itemsSnapAlignment
)
{
case
ItemsSnapAlignment
.
adjacentItem
:
return
_flingToAdjacentItem
(
v
elocity
);
return
_flingToAdjacentItem
(
scrollV
elocity
);
default
:
return
super
.
fling
(
v
elocity
).
then
(
_notifyPageChanged
);
return
super
.
fling
(
scrollV
elocity
).
then
(
_notifyPageChanged
);
}
}
...
...
packages/flutter/lib/src/widgets/scrollable.dart
View file @
70d96ecb
...
...
@@ -19,11 +19,6 @@ import 'notification_listener.dart';
import
'page_storage.dart'
;
import
'scroll_behavior.dart'
;
// The gesture velocity properties are pixels/second, config min,max limits are pixels/ms
const
double
_kMillisecondsPerSecond
=
1000.0
;
const
double
_kMinFlingVelocity
=
-
kMaxFlingVelocity
*
_kMillisecondsPerSecond
;
const
double
_kMaxFlingVelocity
=
kMaxFlingVelocity
*
_kMillisecondsPerSecond
;
/// The accuracy to which scrolling is computed.
final
Tolerance
kPixelScrollTolerance
=
new
Tolerance
(
velocity:
1.0
/
(
0.050
*
ui
.
window
.
devicePixelRatio
),
// logical pixels per second
...
...
@@ -45,14 +40,16 @@ abstract class Scrollable extends StatefulComponent {
Key
key
,
this
.
initialScrollOffset
,
this
.
scrollDirection
:
Axis
.
vertical
,
this
.
scrollAnchor
:
ViewportAnchor
.
start
,
this
.
onScrollStart
,
this
.
onScroll
,
this
.
onScrollEnd
,
this
.
snapOffsetCallback
,
this
.
snapAlignmentOffset
:
0.0
})
:
super
(
key:
key
)
{
assert
(
scrollDirection
==
Axis
.
vertical
||
scrollDirection
==
Axis
.
horizontal
);
assert
(
scrollDirection
==
Axis
.
vertical
||
scrollDirection
==
Axis
.
horizontal
);
assert
(
scrollAnchor
==
ViewportAnchor
.
start
||
scrollAnchor
==
ViewportAnchor
.
end
);
assert
(
snapAlignmentOffset
!=
null
);
}
/// The scroll offset this widget should use when first created.
...
...
@@ -61,6 +58,8 @@ abstract class Scrollable extends StatefulComponent {
/// The axis along which this widget should scroll.
final
Axis
scrollDirection
;
final
ViewportAnchor
scrollAnchor
;
/// Called whenever this widget starts to scroll.
final
ScrollListener
onScrollStart
;
...
...
@@ -172,13 +171,46 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
/// Scrollable gesture handlers convert their incoming values with this method.
/// Subclasses that define scrollOffset in units other than pixels must
/// override this method.
double
pixelToScrollOffset
(
double
pixelValue
)
=>
pixelValue
;
double
pixelOffsetToScrollOffset
(
double
pixelOffset
)
{
switch
(
config
.
scrollAnchor
)
{
case
ViewportAnchor
.
start
:
// We negate the delta here because a positive scroll offset moves the
// the content up (or to the left) rather than down (or the right).
return
-
pixelOffset
;
case
ViewportAnchor
.
end
:
return
pixelOffset
;
}
}
/// Returns the component of the given velocity in the scroll direction.
double
scrollDirectionVelocity
(
Offset
scrollVelocity
)
{
return
config
.
scrollDirection
==
Axis
.
horizontal
?
-
scrollVelocity
.
dx
:
-
scrollVelocity
.
dy
;
double
scrollOffsetToPixelOffset
(
double
scrollOffset
)
{
switch
(
config
.
scrollAnchor
)
{
case
ViewportAnchor
.
start
:
return
-
scrollOffset
;
case
ViewportAnchor
.
end
:
return
scrollOffset
;
}
}
/// Returns the scroll offset component of the given pixel delta, accounting
/// for the scroll direction and scroll anchor.
double
pixelDeltaToScrollOffset
(
Offset
pixelDelta
)
{
switch
(
config
.
scrollDirection
)
{
case
Axis
.
horizontal
:
return
pixelOffsetToScrollOffset
(
pixelDelta
.
dx
);
case
Axis
.
vertical
:
return
pixelOffsetToScrollOffset
(
pixelDelta
.
dy
);
}
}
/// Returns a two-dimensional representation of the scroll offset, accounting
/// for the scroll direction and scroll anchor.
Offset
scrollOffsetToPixelDelta
(
double
scrollOffset
)
{
switch
(
config
.
scrollDirection
)
{
case
Axis
.
horizontal
:
return
new
Offset
(
scrollOffsetToPixelOffset
(
scrollOffset
),
0.0
);
case
Axis
.
vertical
:
return
new
Offset
(
0.0
,
scrollOffsetToPixelOffset
(
scrollOffset
));
}
}
ScrollBehavior
_scrollBehavior
;
...
...
@@ -244,19 +276,19 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
return
_controller
.
animateTo
(
newScrollOffset
,
duration:
duration
,
curve:
curve
);
}
bool
_scrollOffsetIsInBounds
(
double
o
ffset
)
{
bool
_scrollOffsetIsInBounds
(
double
scrollO
ffset
)
{
if
(
scrollBehavior
is
!
ExtentScrollBehavior
)
return
false
;
ExtentScrollBehavior
behavior
=
scrollBehavior
;
return
offset
>=
behavior
.
minScrollOffset
&&
o
ffset
<
behavior
.
maxScrollOffset
;
return
scrollOffset
>=
behavior
.
minScrollOffset
&&
scrollO
ffset
<
behavior
.
maxScrollOffset
;
}
Simulation
_createFlingSimulation
(
double
v
elocity
)
{
final
Simulation
simulation
=
scrollBehavior
.
createFlingScrollSimulation
(
scrollOffset
,
v
elocity
);
Simulation
_createFlingSimulation
(
double
scrollV
elocity
)
{
final
Simulation
simulation
=
scrollBehavior
.
createFlingScrollSimulation
(
scrollOffset
,
scrollV
elocity
);
if
(
simulation
!=
null
)
{
final
double
endVelocity
=
pixel
ToScrollOffset
(
kPixelScrollTolerance
.
velocity
)
;
final
double
endDistance
=
pixel
ToScrollOffset
(
kPixelScrollTolerance
.
distance
);
simulation
.
tolerance
=
new
Tolerance
(
velocity:
endVelocity
.
abs
()
,
distance:
endDistance
);
final
double
endVelocity
=
pixel
OffsetToScrollOffset
(
kPixelScrollTolerance
.
velocity
)
*
scrollVelocity
.
sign
;
final
double
endDistance
=
pixel
OffsetToScrollOffset
(
kPixelScrollTolerance
.
distance
).
abs
(
);
simulation
.
tolerance
=
new
Tolerance
(
velocity:
endVelocity
,
distance:
endDistance
);
}
return
simulation
;
}
...
...
@@ -267,41 +299,40 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
}
/// Whether this scrollable should attempt to snap scroll offsets.
bool
get
s
napScrollOffsetChanges
=>
config
.
snapOffsetCallback
!=
null
;
bool
get
s
houldSnapScrollOffset
=>
config
.
snapOffsetCallback
!=
null
;
Simulation
_createSnapSimulation
(
double
v
elocity
)
{
if
(!
s
napScrollOffsetChanges
||
v
elocity
==
0.0
||
!
_scrollOffsetIsInBounds
(
scrollOffset
))
Simulation
_createSnapSimulation
(
double
scrollV
elocity
)
{
if
(!
s
houldSnapScrollOffset
||
scrollV
elocity
==
0.0
||
!
_scrollOffsetIsInBounds
(
scrollOffset
))
return
null
;
Simulation
simulation
=
_createFlingSimulation
(
v
elocity
);
Simulation
simulation
=
_createFlingSimulation
(
scrollV
elocity
);
if
(
simulation
==
null
)
return
null
;
double
endScrollOffset
=
simulation
.
x
(
double
.
INFINITY
);
final
double
endScrollOffset
=
simulation
.
x
(
double
.
INFINITY
);
if
(
endScrollOffset
.
isNaN
)
return
null
;
double
snappedScrollOffset
=
snapScrollOffset
(
endScrollOffset
+
config
.
snapAlignmentOffset
);
double
alignedScrollOffset
=
snappedScrollOffset
-
config
.
snapAlignmentOffset
;
final
double
snappedScrollOffset
=
snapScrollOffset
(
endScrollOffset
+
config
.
snapAlignmentOffset
);
final
double
alignedScrollOffset
=
snappedScrollOffset
-
config
.
snapAlignmentOffset
;
if
(!
_scrollOffsetIsInBounds
(
alignedScrollOffset
))
return
null
;
double
snapVelocity
=
v
elocity
.
abs
()
*
(
alignedScrollOffset
-
scrollOffset
).
sign
;
double
endVelocity
=
pixelToScrollOffset
(
kPixelScrollTolerance
.
velocity
*
velocity
.
sign
)
;
final
double
snapVelocity
=
scrollV
elocity
.
abs
()
*
(
alignedScrollOffset
-
scrollOffset
).
sign
;
final
double
endVelocity
=
pixelOffsetToScrollOffset
(
kPixelScrollTolerance
.
velocity
).
abs
()
*
scrollVelocity
.
sign
;
Simulation
toSnapSimulation
=
scrollBehavior
.
createSnapScrollSimulation
(
scrollOffset
,
alignedScrollOffset
,
snapVelocity
,
endVelocity
);
if
(
toSnapSimulation
==
null
)
return
null
;
double
o
ffsetMin
=
math
.
min
(
scrollOffset
,
alignedScrollOffset
);
double
o
ffsetMax
=
math
.
max
(
scrollOffset
,
alignedScrollOffset
);
return
new
ClampedSimulation
(
toSnapSimulation
,
xMin:
offsetMin
,
xMax:
o
ffsetMax
);
final
double
scrollO
ffsetMin
=
math
.
min
(
scrollOffset
,
alignedScrollOffset
);
final
double
scrollO
ffsetMax
=
math
.
max
(
scrollOffset
,
alignedScrollOffset
);
return
new
ClampedSimulation
(
toSnapSimulation
,
xMin:
scrollOffsetMin
,
xMax:
scrollO
ffsetMax
);
}
Future
_startToEndAnimation
(
Offset
scrollVelocity
)
{
double
velocity
=
scrollDirectionVelocity
(
scrollVelocity
);
Future
_startToEndAnimation
(
double
scrollVelocity
)
{
_controller
.
stop
();
Simulation
simulation
=
_createSnapSimulation
(
velocity
)
??
_createFlingSimulation
(
v
elocity
);
Simulation
simulation
=
_createSnapSimulation
(
scrollVelocity
)
??
_createFlingSimulation
(
scrollV
elocity
);
if
(
simulation
==
null
)
return
new
Future
.
value
();
return
_controller
.
animateWith
(
simulation
);
...
...
@@ -358,8 +389,8 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
/// Calling this function starts a physics-based animation of the scroll
/// offset with the given value as the initial velocity. The physics
/// simulation used is determined by the scroll behavior.
Future
fling
(
Offset
scrollVelocity
)
{
if
(
scrollVelocity
!=
Offset
.
zero
)
Future
fling
(
double
scrollVelocity
)
{
if
(
scrollVelocity
!=
0.0
)
return
_startToEndAnimation
(
scrollVelocity
);
if
(!
_controller
.
isAnimating
)
return
settleScrollOffset
();
...
...
@@ -372,7 +403,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
/// offset either to a snap point or to within the scrolling bounds. The
/// physics simulation used is determined by the scroll behavior.
Future
settleScrollOffset
()
{
return
_startToEndAnimation
(
Offset
.
zero
);
return
_startToEndAnimation
(
0.0
);
}
/// Calls the onScrollStart callback.
...
...
@@ -408,19 +439,14 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
}
void
_handleDragUpdate
(
double
delta
)
{
// We negate the delta here because a positive scroll offset moves the
// the content up (or to the left) rather than down (or the right).
scrollBy
(
pixelToScrollOffset
(-
delta
));
}
double
_toScrollVelocity
(
double
velocity
)
{
return
pixelToScrollOffset
(
velocity
.
clamp
(
_kMinFlingVelocity
,
_kMaxFlingVelocity
)
/
_kMillisecondsPerSecond
);
scrollBy
(
pixelOffsetToScrollOffset
(
delta
));
}
Future
_handleDragEnd
(
Offset
pixelScrollVelocity
)
{
final
Offset
scrollVelocity
=
new
Offset
(
_toScrollVelocity
(
pixelScrollVelocity
.
dx
),
_toScrollVelocity
(
pixelScrollVelocity
.
dy
));
return
fling
(
scrollVelocity
).
then
((
_
)
{
dispatchOnScrollEnd
();
Future
_handleDragEnd
(
Offset
velocity
)
{
double
scrollVelocity
=
pixelDeltaToScrollOffset
(
velocity
)
/
Duration
.
MILLISECONDS_PER_SECOND
;
// The gesture velocity properties are pixels/second, config min,max limits are pixels/ms
return
fling
(
scrollVelocity
.
clamp
(-
kMaxFlingVelocity
,
kMaxFlingVelocity
)).
then
((
_
)
{
dispatchOnScrollEnd
();
});
}
}
...
...
packages/flutter/test/widget/snap_scrolling_test.dart
View file @
70d96ecb
...
...
@@ -48,14 +48,11 @@ void set scrollOffset(double value) {
}
Future
fling
(
double
velocity
)
{
Offset
velocityOffset
=
scrollDirection
==
Axis
.
vertical
?
new
Offset
(
0.0
,
velocity
)
:
new
Offset
(
velocity
,
0.0
);
return
scrollableState
.
fling
(
velocityOffset
);
return
scrollableState
.
fling
(
velocity
);
}
void
main
(
)
{
test
(
'ScrollableList snap scrolling, fling(
-
0.8)'
,
()
{
test
(
'ScrollableList snap scrolling, fling(0.8)'
,
()
{
testWidgets
((
WidgetTester
tester
)
{
tester
.
pumpWidget
(
buildFrame
());
...
...
@@ -65,7 +62,7 @@ void main() {
Duration
dt
=
const
Duration
(
seconds:
2
);
fling
(
-
0.8
);
fling
(
0.8
);
tester
.
pump
();
// Start the scheduler at 0.0
tester
.
pump
(
dt
);
expect
(
scrollOffset
,
closeTo
(
200.0
,
1.0
));
...
...
@@ -74,7 +71,7 @@ void main() {
tester
.
pump
();
expect
(
scrollOffset
,
0.0
);
fling
(
-
2.0
);
fling
(
2.0
);
tester
.
pump
();
tester
.
pump
(
dt
);
expect
(
scrollOffset
,
closeTo
(
400.0
,
1.0
));
...
...
@@ -83,7 +80,7 @@ void main() {
tester
.
pump
();
expect
(
scrollOffset
,
400.0
);
fling
(
0.8
);
fling
(
-
0.8
);
tester
.
pump
();
tester
.
pump
(
dt
);
expect
(
scrollOffset
,
closeTo
(
0.0
,
1.0
));
...
...
@@ -92,7 +89,7 @@ void main() {
tester
.
pump
();
expect
(
scrollOffset
,
800.0
);
fling
(
2.0
);
fling
(
-
2.0
);
tester
.
pump
();
tester
.
pump
(
dt
);
expect
(
scrollOffset
,
closeTo
(
200.0
,
1.0
));
...
...
@@ -102,7 +99,7 @@ void main() {
expect
(
scrollOffset
,
800.0
);
bool
completed
=
false
;
fling
(
2.0
).
then
((
_
)
{
fling
(
-
2.0
).
then
((
_
)
{
completed
=
true
;
expect
(
scrollOffset
,
closeTo
(
200.0
,
1.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