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
8603ed99
Unverified
Commit
8603ed99
authored
Jun 10, 2021
by
Kate Lovett
Committed by
GitHub
Jun 10, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix scrollbar drag gestures for reversed scrollables (#82764)
parent
6728cf34
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
228 additions
and
24 deletions
+228
-24
scrollbar.dart
packages/flutter/lib/src/widgets/scrollbar.dart
+25
-24
scrollbar_test.dart
packages/flutter/test/cupertino/scrollbar_test.dart
+148
-0
scrollbar_test.dart
packages/flutter/test/widgets/scrollbar_test.dart
+55
-0
No files found.
packages/flutter/lib/src/widgets/scrollbar.dart
View file @
8603ed99
...
...
@@ -951,7 +951,7 @@ class RawScrollbar extends StatefulWidget {
/// Provides defaults gestures for dragging the scrollbar thumb and tapping on the
/// scrollbar track.
class
RawScrollbarState
<
T
extends
RawScrollbar
>
extends
State
<
T
>
with
TickerProviderStateMixin
<
T
>
{
double
?
_dragScrollbarAxisPosition
;
Offset
?
_dragScrollbarAxisOffset
;
ScrollController
?
_currentController
;
Timer
?
_fadeoutTimer
;
late
AnimationController
_fadeoutAnimationController
;
...
...
@@ -1133,9 +1133,25 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
}
}
void
_updateScrollPosition
(
double
primaryDelta
)
{
void
_updateScrollPosition
(
Offset
updatedOffset
)
{
assert
(
_currentController
!=
null
);
assert
(
_dragScrollbarAxisOffset
!=
null
);
final
ScrollPosition
position
=
_currentController
!.
position
;
late
double
primaryDelta
;
switch
(
position
.
axisDirection
)
{
case
AxisDirection
.
up
:
primaryDelta
=
_dragScrollbarAxisOffset
!.
dy
-
updatedOffset
.
dy
;
break
;
case
AxisDirection
.
right
:
primaryDelta
=
updatedOffset
.
dx
-
_dragScrollbarAxisOffset
!.
dx
;
break
;
case
AxisDirection
.
down
:
primaryDelta
=
updatedOffset
.
dy
-
_dragScrollbarAxisOffset
!.
dy
;
break
;
case
AxisDirection
.
left
:
primaryDelta
=
_dragScrollbarAxisOffset
!.
dx
-
updatedOffset
.
dx
;
break
;
}
// Convert primaryDelta, the amount that the scrollbar moved since the last
// time _updateScrollPosition was called, into the coordinate space of the scroll
...
...
@@ -1159,8 +1175,8 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
}
}
/// Returns the [Axis] of the child scroll view, or null if the
current scroll
/// controller does not have any attached positions.
/// Returns the [Axis] of the child scroll view, or null if the
/// c
urrent scroll c
ontroller does not have any attached positions.
@protected
Axis
?
getScrollbarDirection
()
{
assert
(
_currentController
!=
null
);
...
...
@@ -1194,14 +1210,7 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
}
_fadeoutTimer
?.
cancel
();
_fadeoutAnimationController
.
forward
();
switch
(
direction
)
{
case
Axis
.
vertical
:
_dragScrollbarAxisPosition
=
localPosition
.
dy
;
break
;
case
Axis
.
horizontal
:
_dragScrollbarAxisPosition
=
localPosition
.
dx
;
break
;
}
_dragScrollbarAxisOffset
=
localPosition
;
}
/// Handler called when a currently active long press gesture moves.
...
...
@@ -1214,16 +1223,8 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
if
(
direction
==
null
)
{
return
;
}
switch
(
direction
)
{
case
Axis
.
vertical
:
_updateScrollPosition
(
localPosition
.
dy
-
_dragScrollbarAxisPosition
!);
_dragScrollbarAxisPosition
=
localPosition
.
dy
;
break
;
case
Axis
.
horizontal
:
_updateScrollPosition
(
localPosition
.
dx
-
_dragScrollbarAxisPosition
!);
_dragScrollbarAxisPosition
=
localPosition
.
dx
;
break
;
}
_updateScrollPosition
(
localPosition
);
_dragScrollbarAxisOffset
=
localPosition
;
}
/// Handler called when a long press has ended.
...
...
@@ -1234,7 +1235,7 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
if
(
direction
==
null
)
return
;
_maybeStartFadeoutTimer
();
_dragScrollbarAxis
Position
=
null
;
_dragScrollbarAxis
Offset
=
null
;
_currentController
=
null
;
}
...
...
@@ -1303,7 +1304,7 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
_fadeoutTimer
?.
cancel
();
scrollbarPainter
.
update
(
notification
.
metrics
,
notification
.
metrics
.
axisDirection
);
}
else
if
(
notification
is
ScrollEndNotification
)
{
if
(
_dragScrollbarAxis
Position
==
null
)
if
(
_dragScrollbarAxis
Offset
==
null
)
_maybeStartFadeoutTimer
();
}
return
false
;
...
...
packages/flutter/test/cupertino/scrollbar_test.dart
View file @
8603ed99
...
...
@@ -167,6 +167,80 @@ void main() {
await
tester
.
pump
(
_kScrollbarFadeDuration
);
});
testWidgets
(
'Scrollbar thumb can be dragged with long press - reverse'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
scrollController
=
ScrollController
();
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(),
child:
PrimaryScrollController
(
controller:
scrollController
,
child:
const
CupertinoScrollbar
(
child:
SingleChildScrollView
(
reverse:
true
,
child:
SizedBox
(
width:
4000.0
,
height:
4000.0
),
),
),
),
),
),
);
expect
(
scrollController
.
offset
,
0.0
);
// Scroll a bit.
const
double
scrollAmount
=
10.0
;
final
TestGesture
scrollGesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byType
(
SingleChildScrollView
)));
// Scroll up by swiping down.
await
scrollGesture
.
moveBy
(
const
Offset
(
0.0
,
scrollAmount
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
// Scrollbar thumb is fully showing and scroll offset has moved by
// scrollAmount.
expect
(
find
.
byType
(
CupertinoScrollbar
),
paints
..
rrect
(
color:
_kScrollbarColor
.
color
,
));
expect
(
scrollController
.
offset
,
scrollAmount
);
await
scrollGesture
.
up
();
await
tester
.
pump
();
int
hapticFeedbackCalls
=
0
;
SystemChannels
.
platform
.
setMockMethodCallHandler
((
MethodCall
methodCall
)
async
{
if
(
methodCall
.
method
==
'HapticFeedback.vibrate'
)
{
hapticFeedbackCalls
++;
}
});
// Long press on the scrollbar thumb and expect a vibration after it resizes.
expect
(
hapticFeedbackCalls
,
0
);
final
TestGesture
dragScrollbarGesture
=
await
tester
.
startGesture
(
const
Offset
(
796.0
,
550.0
));
await
tester
.
pump
(
_kLongPressDuration
);
expect
(
hapticFeedbackCalls
,
0
);
await
tester
.
pump
(
_kScrollbarResizeDuration
);
// Allow the haptic feedback some slack.
await
tester
.
pump
(
const
Duration
(
milliseconds:
1
));
expect
(
hapticFeedbackCalls
,
1
);
// Drag the thumb up to scroll up.
await
dragScrollbarGesture
.
moveBy
(
const
Offset
(
0.0
,
-
scrollAmount
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
await
dragScrollbarGesture
.
up
();
await
tester
.
pumpAndSettle
();
// The view has scrolled more than it would have by a swipe gesture of the
// same distance.
expect
(
scrollController
.
offset
,
greaterThan
(
scrollAmount
*
2
));
// The scrollbar thumb is still fully visible.
expect
(
find
.
byType
(
CupertinoScrollbar
),
paints
..
rrect
(
color:
_kScrollbarColor
.
color
,
));
// Let the thumb fade out so all timers have resolved.
await
tester
.
pump
(
_kScrollbarTimeToFade
);
await
tester
.
pump
(
_kScrollbarFadeDuration
);
});
testWidgets
(
'Scrollbar changes thickness and radius when dragged'
,
(
WidgetTester
tester
)
async
{
const
double
thickness
=
20
;
const
double
thicknessWhileDragging
=
40
;
...
...
@@ -727,6 +801,80 @@ void main() {
await
tester
.
pump
(
_kScrollbarFadeDuration
);
});
testWidgets
(
'Scrollbar thumb can be dragged with long press - horizontal axis, reverse'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
scrollController
=
ScrollController
();
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(),
child:
CupertinoScrollbar
(
controller:
scrollController
,
child:
SingleChildScrollView
(
reverse:
true
,
controller:
scrollController
,
scrollDirection:
Axis
.
horizontal
,
child:
const
SizedBox
(
width:
4000.0
,
height:
4000.0
),
),
),
),
),
);
expect
(
scrollController
.
offset
,
0.0
);
// Scroll a bit.
const
double
scrollAmount
=
10.0
;
final
TestGesture
scrollGesture
=
await
tester
.
startGesture
(
tester
.
getCenter
(
find
.
byType
(
SingleChildScrollView
)));
// Scroll right by swiping right.
await
scrollGesture
.
moveBy
(
const
Offset
(
scrollAmount
,
0.0
));
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
// Scrollbar thumb is fully showing and scroll offset has moved by
// scrollAmount.
expect
(
find
.
byType
(
CupertinoScrollbar
),
paints
..
rrect
(
color:
_kScrollbarColor
.
color
,
));
expect
(
scrollController
.
offset
,
scrollAmount
);
await
scrollGesture
.
up
();
await
tester
.
pump
();
int
hapticFeedbackCalls
=
0
;
SystemChannels
.
platform
.
setMockMethodCallHandler
((
MethodCall
methodCall
)
async
{
if
(
methodCall
.
method
==
'HapticFeedback.vibrate'
)
{
hapticFeedbackCalls
++;
}
});
// Long press on the scrollbar thumb and expect a vibration after it resizes.
expect
(
hapticFeedbackCalls
,
0
);
final
TestGesture
dragScrollbarGesture
=
await
tester
.
startGesture
(
const
Offset
(
750.0
,
596.0
));
await
tester
.
pump
(
_kLongPressDuration
);
expect
(
hapticFeedbackCalls
,
0
);
await
tester
.
pump
(
_kScrollbarResizeDuration
);
// Allow the haptic feedback some slack.
await
tester
.
pump
(
const
Duration
(
milliseconds:
1
));
expect
(
hapticFeedbackCalls
,
1
);
// Drag the thumb to scroll back to the right.
await
dragScrollbarGesture
.
moveBy
(
const
Offset
(-
scrollAmount
,
0.0
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
await
dragScrollbarGesture
.
up
();
await
tester
.
pumpAndSettle
();
// The view has scrolled more than it would have by a swipe gesture of the
// same distance.
expect
(
scrollController
.
offset
,
greaterThan
(
scrollAmount
*
2
));
// The scrollbar thumb is still fully visible.
expect
(
find
.
byType
(
CupertinoScrollbar
),
paints
..
rrect
(
color:
_kScrollbarColor
.
color
,
));
// Let the thumb fade out so all timers have resolved.
await
tester
.
pump
(
_kScrollbarTimeToFade
);
await
tester
.
pump
(
_kScrollbarFadeDuration
);
});
testWidgets
(
'Tapping the track area pages the Scroll View'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
scrollController
=
ScrollController
();
await
tester
.
pumpWidget
(
...
...
packages/flutter/test/widgets/scrollbar_test.dart
View file @
8603ed99
...
...
@@ -1339,6 +1339,61 @@ void main() {
);
});
testWidgets
(
'Scrollbar thumb can be dragged in reverse'
,
(
WidgetTester
tester
)
async
{
final
ScrollController
scrollController
=
ScrollController
();
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(),
child:
PrimaryScrollController
(
controller:
scrollController
,
child:
RawScrollbar
(
isAlwaysShown:
true
,
controller:
scrollController
,
child:
const
SingleChildScrollView
(
reverse:
true
,
child:
SizedBox
(
width:
4000.0
,
height:
4000.0
),
),
),
),
),
),
);
await
tester
.
pumpAndSettle
();
expect
(
scrollController
.
offset
,
0.0
);
expect
(
find
.
byType
(
RawScrollbar
),
paints
..
rect
(
rect:
const
Rect
.
fromLTRB
(
794.0
,
0.0
,
800.0
,
600.0
))
..
rect
(
rect:
const
Rect
.
fromLTRB
(
794.0
,
510.0
,
800.0
,
600.0
),
color:
const
Color
(
0x66BCBCBC
),
),
);
// Drag the thumb up to scroll up.
const
double
scrollAmount
=
10.0
;
final
TestGesture
dragScrollbarGesture
=
await
tester
.
startGesture
(
const
Offset
(
797.0
,
550.0
));
await
tester
.
pumpAndSettle
();
await
dragScrollbarGesture
.
moveBy
(
const
Offset
(
0.0
,
-
scrollAmount
));
await
tester
.
pumpAndSettle
();
await
dragScrollbarGesture
.
up
();
await
tester
.
pumpAndSettle
();
// The view has scrolled more than it would have by a swipe gesture of the
// same distance.
expect
(
scrollController
.
offset
,
greaterThan
(
scrollAmount
*
2
));
expect
(
find
.
byType
(
RawScrollbar
),
paints
..
rect
(
rect:
const
Rect
.
fromLTRB
(
794.0
,
0.0
,
800.0
,
600.0
))
..
rect
(
rect:
const
Rect
.
fromLTRB
(
794.0
,
500.0
,
800.0
,
590.0
),
color:
const
Color
(
0x66BCBCBC
),
),
);
});
testWidgets
(
'ScrollbarPainter asserts if scrollbarOrientation is used with wrong axisDirection'
,
(
WidgetTester
tester
)
async
{
final
ScrollbarPainter
painter
=
ScrollbarPainter
(
color:
_kScrollbarColor
,
...
...
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