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
7b549def
Commit
7b549def
authored
Aug 30, 2017
by
Adam Barth
Committed by
GitHub
Aug 30, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add RTL support to Slider and CupertinoSlider (#11822)
Fixes #11358
parent
3437b770
Changes
5
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
480 additions
and
154 deletions
+480
-154
slider.dart
packages/flutter/lib/src/cupertino/slider.dart
+57
-10
slider.dart
packages/flutter/lib/src/material/slider.dart
+68
-16
slider_test.dart
packages/flutter/test/cupertino/slider_test.dart
+91
-14
slider_test.dart
packages/flutter/test/material/slider_test.dart
+254
-107
semantics_debugger_test.dart
packages/flutter/test/widgets/semantics_debugger_test.dart
+10
-7
No files found.
packages/flutter/lib/src/cupertino/slider.dart
View file @
7b549def
...
...
@@ -161,6 +161,7 @@ class _CupertinoSliderRenderObjectWidget extends LeafRenderObjectWidget {
activeColor:
activeColor
,
onChanged:
onChanged
,
vsync:
vsync
,
textDirection:
Directionality
.
of
(
context
),
);
}
...
...
@@ -170,7 +171,8 @@ class _CupertinoSliderRenderObjectWidget extends LeafRenderObjectWidget {
..
value
=
value
..
divisions
=
divisions
..
activeColor
=
activeColor
..
onChanged
=
onChanged
;
..
onChanged
=
onChanged
..
textDirection
=
Directionality
.
of
(
context
);
// Ticker provider cannot change since there's a 1:1 relationship between
// the _SliderRenderObjectWidget object and the _SliderState object.
}
...
...
@@ -192,11 +194,14 @@ class _RenderCupertinoSlider extends RenderConstrainedBox implements SemanticsAc
Color
activeColor
,
ValueChanged
<
double
>
onChanged
,
TickerProvider
vsync
,
@required
TextDirection
textDirection
,
})
:
assert
(
value
!=
null
&&
value
>=
0.0
&&
value
<=
1.0
),
assert
(
textDirection
!=
null
),
_value
=
value
,
_divisions
=
divisions
,
_activeColor
=
activeColor
,
_onChanged
=
onChanged
,
_textDirection
=
textDirection
,
super
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
width:
_kSliderWidth
,
height:
_kSliderHeight
))
{
_drag
=
new
HorizontalDragGestureRecognizer
()
..
onStart
=
_handleDragStart
...
...
@@ -251,6 +256,16 @@ class _RenderCupertinoSlider extends RenderConstrainedBox implements SemanticsAc
markNeedsSemanticsUpdate
(
noGeometry:
true
);
}
TextDirection
get
textDirection
=>
_textDirection
;
TextDirection
_textDirection
;
set
textDirection
(
TextDirection
value
)
{
assert
(
value
!=
null
);
if
(
_textDirection
==
value
)
return
;
_textDirection
=
value
;
markNeedsPaint
();
}
AnimationController
_position
;
HorizontalDragGestureRecognizer
_drag
;
...
...
@@ -265,7 +280,18 @@ class _RenderCupertinoSlider extends RenderConstrainedBox implements SemanticsAc
double
get
_trackLeft
=>
_kPadding
;
double
get
_trackRight
=>
size
.
width
-
_kPadding
;
double
get
_thumbCenter
=>
lerpDouble
(
_trackLeft
+
CupertinoThumbPainter
.
radius
,
_trackRight
-
CupertinoThumbPainter
.
radius
,
_value
);
double
get
_thumbCenter
{
double
visualPosition
;
switch
(
textDirection
)
{
case
TextDirection
.
rtl
:
visualPosition
=
1.0
-
_value
;
break
;
case
TextDirection
.
ltr
:
visualPosition
=
_value
;
break
;
}
return
lerpDouble
(
_trackLeft
+
CupertinoThumbPainter
.
radius
,
_trackRight
-
CupertinoThumbPainter
.
radius
,
visualPosition
);
}
bool
get
isInteractive
=>
onChanged
!=
null
;
...
...
@@ -279,7 +305,15 @@ class _RenderCupertinoSlider extends RenderConstrainedBox implements SemanticsAc
void
_handleDragUpdate
(
DragUpdateDetails
details
)
{
if
(
isInteractive
)
{
final
double
extent
=
math
.
max
(
_kPadding
,
size
.
width
-
2.0
*
(
_kPadding
+
CupertinoThumbPainter
.
radius
));
_currentDragValue
+=
details
.
primaryDelta
/
extent
;
final
double
valueDelta
=
details
.
primaryDelta
/
extent
;
switch
(
textDirection
)
{
case
TextDirection
.
rtl
:
_currentDragValue
-=
valueDelta
;
break
;
case
TextDirection
.
ltr
:
_currentDragValue
+=
valueDelta
;
break
;
}
onChanged
(
_discretizedCurrentDragValue
);
}
}
...
...
@@ -304,9 +338,21 @@ class _RenderCupertinoSlider extends RenderConstrainedBox implements SemanticsAc
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
final
Canvas
canvas
=
context
.
canvas
;
final
double
value
=
_position
.
value
;
double
visualPosition
;
Color
leftColor
;
Color
rightColor
;
switch
(
textDirection
)
{
case
TextDirection
.
rtl
:
visualPosition
=
1.0
-
_position
.
value
;
leftColor
=
_kTrackColor
;
rightColor
=
_activeColor
;
break
;
case
TextDirection
.
ltr
:
visualPosition
=
_position
.
value
;
leftColor
=
_activeColor
;
rightColor
=
_kTrackColor
;
break
;
}
final
double
trackCenter
=
offset
.
dy
+
size
.
height
/
2.0
;
final
double
trackLeft
=
offset
.
dx
+
_trackLeft
;
...
...
@@ -315,15 +361,16 @@ class _RenderCupertinoSlider extends RenderConstrainedBox implements SemanticsAc
final
double
trackRight
=
offset
.
dx
+
_trackRight
;
final
double
trackActive
=
offset
.
dx
+
_thumbCenter
;
final
Canvas
canvas
=
context
.
canvas
;
final
Paint
paint
=
new
Paint
();
if
(
v
alue
>
0.0
)
{
paint
.
color
=
_active
Color
;
if
(
v
isualPosition
>
0.0
)
{
paint
.
color
=
right
Color
;
canvas
.
drawRRect
(
new
RRect
.
fromLTRBXY
(
trackLeft
,
trackTop
,
trackActive
,
trackBottom
,
1.0
,
1.0
),
paint
);
}
if
(
v
alue
<
1.0
)
{
paint
.
color
=
_kTrack
Color
;
if
(
v
isualPosition
<
1.0
)
{
paint
.
color
=
left
Color
;
canvas
.
drawRRect
(
new
RRect
.
fromLTRBXY
(
trackActive
,
trackTop
,
trackRight
,
trackBottom
,
1.0
,
1.0
),
paint
);
}
...
...
packages/flutter/lib/src/material/slider.dart
View file @
7b549def
...
...
@@ -229,6 +229,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
textTheme:
textTheme
,
onChanged:
onChanged
,
vsync:
vsync
,
textDirection:
Directionality
.
of
(
context
),
);
}
...
...
@@ -242,7 +243,8 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget {
..
inactiveColor
=
inactiveColor
..
thumbOpenAtMin
=
thumbOpenAtMin
..
textTheme
=
textTheme
..
onChanged
=
onChanged
;
..
onChanged
=
onChanged
..
textDirection
=
Directionality
.
of
(
context
);
// Ticker provider cannot change since there's a 1:1 relationship between
// the _SliderRenderObjectWidget object and the _SliderState object.
}
...
...
@@ -290,14 +292,17 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler {
TextTheme
textTheme
,
ValueChanged
<
double
>
onChanged
,
TickerProvider
vsync
,
@required
TextDirection
textDirection
,
})
:
assert
(
value
!=
null
&&
value
>=
0.0
&&
value
<=
1.0
),
assert
(
textDirection
!=
null
),
_value
=
value
,
_divisions
=
divisions
,
_activeColor
=
activeColor
,
_inactiveColor
=
inactiveColor
,
_thumbOpenAtMin
=
thumbOpenAtMin
,
_textTheme
=
textTheme
,
_onChanged
=
onChanged
{
_onChanged
=
onChanged
,
_textDirection
=
textDirection
{
this
.
label
=
label
;
final
GestureArenaTeam
team
=
new
GestureArenaTeam
();
_drag
=
new
HorizontalDragGestureRecognizer
()
...
...
@@ -415,6 +420,17 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler {
}
}
TextDirection
get
textDirection
=>
_textDirection
;
TextDirection
_textDirection
;
set
textDirection
(
TextDirection
value
)
{
assert
(
value
!=
null
);
if
(
_textDirection
==
value
)
return
;
_textDirection
=
value
;
// TODO(abarth): Update _labelPainter's text direction.
markNeedsPaint
();
}
double
get
_trackLength
=>
size
.
width
-
2.0
*
_kReactionRadius
;
Animation
<
double
>
_reaction
;
...
...
@@ -430,8 +446,19 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler {
bool
get
isInteractive
=>
onChanged
!=
null
;
double
_getValueFromVisualPosition
(
double
visualPosition
)
{
switch
(
textDirection
)
{
case
TextDirection
.
rtl
:
return
1.0
-
visualPosition
;
case
TextDirection
.
ltr
:
return
visualPosition
;
}
return
null
;
}
double
_getValueFromGlobalPosition
(
Offset
globalPosition
)
{
return
(
globalToLocal
(
globalPosition
).
dx
-
_kReactionRadius
)
/
_trackLength
;
final
double
visualPosition
=
(
globalToLocal
(
globalPosition
).
dx
-
_kReactionRadius
)
/
_trackLength
;
return
_getValueFromVisualPosition
(
visualPosition
);
}
double
_discretize
(
double
value
)
{
...
...
@@ -452,7 +479,15 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler {
void
_handleDragUpdate
(
DragUpdateDetails
details
)
{
if
(
isInteractive
)
{
_currentDragValue
+=
details
.
primaryDelta
/
_trackLength
;
final
double
valueDelta
=
details
.
primaryDelta
/
_trackLength
;
switch
(
textDirection
)
{
case
TextDirection
.
rtl
:
_currentDragValue
-=
valueDelta
;
break
;
case
TextDirection
.
ltr
:
_currentDragValue
+=
valueDelta
;
break
;
}
onChanged
(
_discretize
(
_currentDragValue
));
}
}
...
...
@@ -523,6 +558,26 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler {
final
double
trackLength
=
size
.
width
-
2
*
_kReactionRadius
;
final
bool
enabled
=
isInteractive
;
final
double
value
=
_position
.
value
;
final
bool
thumbAtMin
=
value
==
0.0
;
final
Paint
primaryPaint
=
new
Paint
()..
color
=
enabled
?
_activeColor
:
_inactiveColor
;
final
Paint
trackPaint
=
new
Paint
()..
color
=
_inactiveColor
;
double
visualPosition
;
Paint
leftPaint
;
Paint
rightPaint
;
switch
(
textDirection
)
{
case
TextDirection
.
rtl
:
visualPosition
=
1.0
-
value
;
leftPaint
=
trackPaint
;
rightPaint
=
primaryPaint
;
break
;
case
TextDirection
.
ltr
:
visualPosition
=
value
;
leftPaint
=
primaryPaint
;
rightPaint
=
trackPaint
;
break
;
}
final
double
additionalHeightForLabel
=
_getAdditionalHeightForLabel
(
label
);
final
double
trackCenter
=
offset
.
dy
+
(
size
.
height
-
additionalHeightForLabel
)
/
2.0
+
additionalHeightForLabel
;
...
...
@@ -530,26 +585,23 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler {
final
double
trackTop
=
trackCenter
-
1.0
;
final
double
trackBottom
=
trackCenter
+
1.0
;
final
double
trackRight
=
trackLeft
+
trackLength
;
final
double
trackActive
=
trackLeft
+
trackLength
*
value
;
final
Paint
primaryPaint
=
new
Paint
()..
color
=
enabled
?
_activeColor
:
_inactiveColor
;
final
Paint
trackPaint
=
new
Paint
()..
color
=
_inactiveColor
;
final
double
trackActive
=
trackLeft
+
trackLength
*
visualPosition
;
final
Offset
thumbCenter
=
new
Offset
(
trackActive
,
trackCenter
);
final
double
thumbRadius
=
enabled
?
_kThumbRadiusTween
.
evaluate
(
_reaction
)
:
_kDisabledThumbRadius
;
if
(
enabled
)
{
if
(
v
alue
>
0.0
)
canvas
.
drawRect
(
new
Rect
.
fromLTRB
(
trackLeft
,
trackTop
,
trackActive
,
trackBottom
),
primary
Paint
);
if
(
v
alue
<
1.0
)
{
if
(
v
isualPosition
>
0.0
)
canvas
.
drawRect
(
new
Rect
.
fromLTRB
(
trackLeft
,
trackTop
,
trackActive
,
trackBottom
),
left
Paint
);
if
(
v
isualPosition
<
1.0
)
{
final
bool
hasBalloon
=
_reaction
.
status
!=
AnimationStatus
.
dismissed
&&
label
!=
null
;
final
double
trackActiveDelta
=
hasBalloon
?
0.0
:
thumbRadius
-
1.0
;
canvas
.
drawRect
(
new
Rect
.
fromLTRB
(
trackActive
+
trackActiveDelta
,
trackTop
,
trackRight
,
trackBottom
),
track
Paint
);
canvas
.
drawRect
(
new
Rect
.
fromLTRB
(
trackActive
+
trackActiveDelta
,
trackTop
,
trackRight
,
trackBottom
),
right
Paint
);
}
}
else
{
if
(
v
alue
>
0.0
)
if
(
v
isualPosition
>
0.0
)
canvas
.
drawRect
(
new
Rect
.
fromLTRB
(
trackLeft
,
trackTop
,
trackActive
-
_kDisabledThumbRadius
-
2
,
trackBottom
),
trackPaint
);
if
(
v
alue
<
1.0
)
if
(
v
isualPosition
<
1.0
)
canvas
.
drawRect
(
new
Rect
.
fromLTRB
(
trackActive
+
_kDisabledThumbRadius
+
2
,
trackTop
,
trackRight
,
trackBottom
),
trackPaint
);
}
...
...
@@ -589,7 +641,7 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler {
_labelPainter
.
paint
(
canvas
,
labelOffset
);
return
;
}
else
{
final
Color
reactionBaseColor
=
value
==
0.0
?
_kActiveTrackColor
:
_activeColor
;
final
Color
reactionBaseColor
=
thumbAtMin
?
_kActiveTrackColor
:
_activeColor
;
final
Paint
reactionPaint
=
new
Paint
()..
color
=
reactionBaseColor
.
withAlpha
(
kRadialReactionAlpha
);
canvas
.
drawCircle
(
thumbCenter
,
_kReactionRadiusTween
.
evaluate
(
_reaction
),
reactionPaint
);
}
...
...
@@ -597,7 +649,7 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler {
Paint
thumbPaint
=
primaryPaint
;
double
thumbRadiusDelta
=
0.0
;
if
(
value
==
0.0
&&
thumbOpenAtMin
)
{
if
(
thumbAtMin
&&
thumbOpenAtMin
)
{
thumbPaint
=
trackPaint
;
// This is destructive to trackPaint.
thumbPaint
...
...
packages/flutter/test/cupertino/slider_test.dart
View file @
7b549def
...
...
@@ -11,12 +11,13 @@ import 'package:flutter_test/flutter_test.dart';
import
'../widgets/semantics_tester.dart'
;
void
main
(
)
{
testWidgets
(
'Slider does not move when tapped'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Slider does not move when tapped
(LTR)
'
,
(
WidgetTester
tester
)
async
{
final
Key
sliderKey
=
new
UniqueKey
();
double
value
=
0.0
;
await
tester
.
pumpWidget
(
new
StatefulBuilder
(
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
new
Material
(
child:
new
Center
(
...
...
@@ -33,7 +34,41 @@ void main() {
);
},
),
);
));
expect
(
value
,
equals
(
0.0
));
await
tester
.
tap
(
find
.
byKey
(
sliderKey
));
expect
(
value
,
equals
(
0.0
));
await
tester
.
pump
();
// No animation should start.
// Check the transientCallbackCount before tearing down the widget to ensure
// that no animation is running.
expect
(
SchedulerBinding
.
instance
.
transientCallbackCount
,
equals
(
0
));
});
testWidgets
(
'Slider does not move when tapped (RTL)'
,
(
WidgetTester
tester
)
async
{
final
Key
sliderKey
=
new
UniqueKey
();
double
value
=
0.0
;
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
new
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
new
Material
(
child:
new
Center
(
child:
new
CupertinoSlider
(
key:
sliderKey
,
value:
value
,
onChanged:
(
double
newValue
)
{
setState
(()
{
value
=
newValue
;
});
},
),
),
);
},
),
));
expect
(
value
,
equals
(
0.0
));
await
tester
.
tap
(
find
.
byKey
(
sliderKey
));
...
...
@@ -44,12 +79,14 @@ void main() {
expect
(
SchedulerBinding
.
instance
.
transientCallbackCount
,
equals
(
0
));
});
testWidgets
(
'Slider moves when dragged'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Slider moves when dragged (LTR)'
,
(
WidgetTester
tester
)
async
{
final
Key
sliderKey
=
new
UniqueKey
();
double
value
=
0.0
;
await
tester
.
pumpWidget
(
new
StatefulBuilder
(
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
new
Material
(
child:
new
Center
(
...
...
@@ -66,7 +103,7 @@ void main() {
);
},
),
);
)
)
;
expect
(
value
,
equals
(
0.0
));
final
Offset
topLeft
=
tester
.
getTopLeft
(
find
.
byKey
(
sliderKey
));
...
...
@@ -81,15 +118,54 @@ void main() {
expect
(
SchedulerBinding
.
instance
.
transientCallbackCount
,
equals
(
0
));
});
testWidgets
(
'Slider moves when dragged (RTL)'
,
(
WidgetTester
tester
)
async
{
final
Key
sliderKey
=
new
UniqueKey
();
double
value
=
0.0
;
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
new
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
new
Material
(
child:
new
Center
(
child:
new
CupertinoSlider
(
key:
sliderKey
,
value:
value
,
onChanged:
(
double
newValue
)
{
setState
(()
{
value
=
newValue
;
});
},
),
),
);
},
),
));
expect
(
value
,
equals
(
0.0
));
final
Offset
bottomRight
=
tester
.
getBottomRight
(
find
.
byKey
(
sliderKey
));
const
double
unit
=
CupertinoThumbPainter
.
radius
;
const
double
delta
=
3.0
*
unit
;
await
tester
.
dragFrom
(
bottomRight
-
const
Offset
(
unit
,
unit
),
const
Offset
(-
delta
,
0.0
));
final
Size
size
=
tester
.
getSize
(
find
.
byKey
(
sliderKey
));
expect
(
value
,
equals
(
delta
/
(
size
.
width
-
2.0
*
(
8.0
+
CupertinoThumbPainter
.
radius
))));
await
tester
.
pump
();
// No animation should start.
// Check the transientCallbackCount before tearing down the widget to ensure
// that no animation is running.
expect
(
SchedulerBinding
.
instance
.
transientCallbackCount
,
equals
(
0
));
});
testWidgets
(
'Slider Semantics'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
new
SemanticsTester
(
tester
);
await
tester
.
pumpWidget
(
new
CupertinoSlider
(
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
CupertinoSlider
(
value:
0.5
,
onChanged:
(
double
v
)
{},
),
);
)
)
;
expect
(
semantics
,
hasSemantics
(
new
TestSemantics
.
root
(
...
...
@@ -105,12 +181,13 @@ void main() {
));
// Disable slider
await
tester
.
pumpWidget
(
new
CupertinoSlider
(
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
CupertinoSlider
(
value:
0.5
,
onChanged:
null
,
),
);
)
)
;
expect
(
semantics
,
hasSemantics
(
new
TestSemantics
.
root
(),
...
...
packages/flutter/test/material/slider_test.dart
View file @
7b549def
This diff is collapsed.
Click to expand it.
packages/flutter/test/widgets/semantics_debugger_test.dart
View file @
7b549def
...
...
@@ -219,13 +219,16 @@ void main() {
await
tester
.
pumpWidget
(
new
SemanticsDebugger
(
child:
new
Material
(
child:
new
Center
(
child:
new
Slider
(
value:
value
,
onChanged:
(
double
newValue
)
{
value
=
newValue
;
},
child:
new
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
new
Material
(
child:
new
Center
(
child:
new
Slider
(
value:
value
,
onChanged:
(
double
newValue
)
{
value
=
newValue
;
},
),
),
),
),
...
...
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