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
87cbdddd
Unverified
Commit
87cbdddd
authored
Mar 20, 2020
by
LongCatIsLooong
Committed by
GitHub
Mar 20, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Let cupertino & material switches move to the right state after dragging (#51606)
parent
b1664a27
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
382 additions
and
221 deletions
+382
-221
switch.dart
packages/flutter/lib/src/cupertino/switch.dart
+180
-190
switch.dart
packages/flutter/lib/src/material/switch.dart
+40
-11
toggleable.dart
packages/flutter/lib/src/material/toggleable.dart
+2
-18
switch_test.dart
packages/flutter/test/cupertino/switch_test.dart
+77
-1
switch_test.dart
packages/flutter/test/material/switch_test.dart
+83
-1
No files found.
packages/flutter/lib/src/cupertino/switch.dart
View file @
87cbdddd
...
...
@@ -140,8 +140,158 @@ class CupertinoSwitch extends StatefulWidget {
}
class
_CupertinoSwitchState
extends
State
<
CupertinoSwitch
>
with
TickerProviderStateMixin
{
TapGestureRecognizer
_tap
;
HorizontalDragGestureRecognizer
_drag
;
AnimationController
_positionController
;
CurvedAnimation
position
;
AnimationController
_reactionController
;
Animation
<
double
>
_reaction
;
bool
get
isInteractive
=>
widget
.
onChanged
!=
null
;
// A non-null boolean value that changes to true at the end of a drag if the
// switch must be animated to the position indicated by the widget's value.
bool
needsPositionAnimation
=
false
;
@override
void
initState
()
{
super
.
initState
();
_tap
=
TapGestureRecognizer
()
..
onTapDown
=
_handleTapDown
..
onTapUp
=
_handleTapUp
..
onTap
=
_handleTap
..
onTapCancel
=
_handleTapCancel
;
_drag
=
HorizontalDragGestureRecognizer
()
..
onStart
=
_handleDragStart
..
onUpdate
=
_handleDragUpdate
..
onEnd
=
_handleDragEnd
..
dragStartBehavior
=
widget
.
dragStartBehavior
;
_positionController
=
AnimationController
(
duration:
_kToggleDuration
,
value:
widget
.
value
?
1.0
:
0.0
,
vsync:
this
,
);
position
=
CurvedAnimation
(
parent:
_positionController
,
curve:
Curves
.
linear
,
);
_reactionController
=
AnimationController
(
duration:
_kReactionDuration
,
vsync:
this
,
);
_reaction
=
CurvedAnimation
(
parent:
_reactionController
,
curve:
Curves
.
ease
,
);
}
@override
void
didUpdateWidget
(
CupertinoSwitch
oldWidget
)
{
super
.
didUpdateWidget
(
oldWidget
);
_drag
.
dragStartBehavior
=
widget
.
dragStartBehavior
;
if
(
needsPositionAnimation
||
oldWidget
.
value
!=
widget
.
value
)
_resumePositionAnimation
(
isLinear:
needsPositionAnimation
);
}
// `isLinear` must be true if the position animation is trying to move the
// thumb to the closest end after the most recent drag animation, so the curve
// does not change when the controller's value is not 0 or 1.
//
// It can be set to false when it's an implicit animation triggered by
// widget.value changes.
void
_resumePositionAnimation
({
bool
isLinear
=
true
})
{
needsPositionAnimation
=
false
;
position
..
curve
=
isLinear
?
null
:
Curves
.
ease
..
reverseCurve
=
isLinear
?
null
:
Curves
.
ease
.
flipped
;
if
(
widget
.
value
)
_positionController
.
forward
();
else
_positionController
.
reverse
();
}
void
_handleTapDown
(
TapDownDetails
details
)
{
if
(
isInteractive
)
needsPositionAnimation
=
false
;
_reactionController
.
forward
();
}
void
_handleTap
()
{
if
(
isInteractive
)
{
widget
.
onChanged
(!
widget
.
value
);
_emitVibration
();
}
}
void
_handleTapUp
(
TapUpDetails
details
)
{
if
(
isInteractive
)
{
needsPositionAnimation
=
false
;
_reactionController
.
reverse
();
}
}
void
_handleTapCancel
()
{
if
(
isInteractive
)
_reactionController
.
reverse
();
}
void
_handleDragStart
(
DragStartDetails
details
)
{
if
(
isInteractive
)
{
needsPositionAnimation
=
false
;
_reactionController
.
forward
();
_emitVibration
();
}
}
void
_handleDragUpdate
(
DragUpdateDetails
details
)
{
if
(
isInteractive
)
{
position
..
curve
=
null
..
reverseCurve
=
null
;
final
double
delta
=
details
.
primaryDelta
/
_kTrackInnerLength
;
switch
(
Directionality
.
of
(
context
))
{
case
TextDirection
.
rtl
:
_positionController
.
value
-=
delta
;
break
;
case
TextDirection
.
ltr
:
_positionController
.
value
+=
delta
;
break
;
}
}
}
void
_handleDragEnd
(
DragEndDetails
details
)
{
// Deferring the animation to the next build phase.
setState
(()
{
needsPositionAnimation
=
true
;
});
// Call onChanged when the user's intent to change value is clear.
if
(
position
.
value
>=
0.5
!=
widget
.
value
)
widget
.
onChanged
(!
widget
.
value
);
_reactionController
.
reverse
();
}
void
_emitVibration
()
{
switch
(
defaultTargetPlatform
)
{
case
TargetPlatform
.
iOS
:
HapticFeedback
.
lightImpact
();
break
;
case
TargetPlatform
.
android
:
case
TargetPlatform
.
fuchsia
:
case
TargetPlatform
.
linux
:
case
TargetPlatform
.
macOS
:
case
TargetPlatform
.
windows
:
break
;
}
}
@override
Widget
build
(
BuildContext
context
)
{
if
(
needsPositionAnimation
)
_resumePositionAnimation
();
return
Opacity
(
opacity:
widget
.
onChanged
==
null
?
_kCupertinoSwitchDisabledOpacity
:
1.0
,
child:
_CupertinoSwitchRenderObjectWidget
(
...
...
@@ -152,11 +302,21 @@ class _CupertinoSwitchState extends State<CupertinoSwitch> with TickerProviderSt
),
trackColor:
CupertinoDynamicColor
.
resolve
(
widget
.
trackColor
??
CupertinoColors
.
secondarySystemFill
,
context
),
onChanged:
widget
.
onChanged
,
vsync:
this
,
dragStartBehavior:
widget
.
dragStartBehavior
,
textDirection:
Directionality
.
of
(
context
)
,
state:
this
,
),
);
}
@override
void
dispose
()
{
_tap
.
dispose
();
_drag
.
dispose
();
_positionController
.
dispose
();
_reactionController
.
dispose
();
super
.
dispose
();
}
}
class
_CupertinoSwitchRenderObjectWidget
extends
LeafRenderObjectWidget
{
...
...
@@ -166,16 +326,16 @@ class _CupertinoSwitchRenderObjectWidget extends LeafRenderObjectWidget {
this
.
activeColor
,
this
.
trackColor
,
this
.
onChanged
,
this
.
vsync
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
textDirection
,
this
.
state
,
})
:
super
(
key:
key
);
final
bool
value
;
final
Color
activeColor
;
final
Color
trackColor
;
final
ValueChanged
<
bool
>
onChanged
;
final
TickerProvider
vsync
;
final
DragStartBehavior
dragStartBehavior
;
final
_CupertinoSwitchState
state
;
final
TextDirection
textDirection
;
@override
_RenderCupertinoSwitch
createRenderObject
(
BuildContext
context
)
{
...
...
@@ -184,9 +344,8 @@ class _CupertinoSwitchRenderObjectWidget extends LeafRenderObjectWidget {
activeColor:
activeColor
,
trackColor:
trackColor
,
onChanged:
onChanged
,
textDirection:
Directionality
.
of
(
context
),
vsync:
vsync
,
dragStartBehavior:
dragStartBehavior
,
textDirection:
textDirection
,
state:
state
,
);
}
...
...
@@ -197,9 +356,7 @@ class _CupertinoSwitchRenderObjectWidget extends LeafRenderObjectWidget {
..
activeColor
=
activeColor
..
trackColor
=
trackColor
..
onChanged
=
onChanged
..
textDirection
=
Directionality
.
of
(
context
)
..
vsync
=
vsync
..
dragStartBehavior
=
dragStartBehavior
;
..
textDirection
=
textDirection
;
}
}
...
...
@@ -224,53 +381,22 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
@required
Color
trackColor
,
ValueChanged
<
bool
>
onChanged
,
@required
TextDirection
textDirection
,
@required
TickerProvider
vsync
,
DragStartBehavior
dragStartBehavior
=
DragStartBehavior
.
start
,
@required
_CupertinoSwitchState
state
,
})
:
assert
(
value
!=
null
),
assert
(
activeColor
!=
null
),
assert
(
vsync
!=
null
),
assert
(
state
!=
null
),
_value
=
value
,
_activeColor
=
activeColor
,
_trackColor
=
trackColor
,
_onChanged
=
onChanged
,
_textDirection
=
textDirection
,
_
vsync
=
vsync
,
_
state
=
state
,
super
(
additionalConstraints:
const
BoxConstraints
.
tightFor
(
width:
_kSwitchWidth
,
height:
_kSwitchHeight
))
{
_tap
=
TapGestureRecognizer
()
..
onTapDown
=
_handleTapDown
..
onTap
=
_handleTap
..
onTapUp
=
_handleTapUp
..
onTapCancel
=
_handleTapCancel
;
_drag
=
HorizontalDragGestureRecognizer
()
..
onStart
=
_handleDragStart
..
onUpdate
=
_handleDragUpdate
..
onEnd
=
_handleDragEnd
..
dragStartBehavior
=
dragStartBehavior
;
_positionController
=
AnimationController
(
duration:
_kToggleDuration
,
value:
value
?
1.0
:
0.0
,
vsync:
vsync
,
);
_position
=
CurvedAnimation
(
parent:
_positionController
,
curve:
Curves
.
linear
,
)..
addListener
(
markNeedsPaint
)
..
addStatusListener
(
_handlePositionStateChanged
);
_reactionController
=
AnimationController
(
duration:
_kReactionDuration
,
vsync:
vsync
,
);
_reaction
=
CurvedAnimation
(
parent:
_reactionController
,
curve:
Curves
.
ease
,
)..
addListener
(
markNeedsPaint
);
state
.
position
.
addListener
(
markNeedsPaint
);
state
.
_reaction
.
addListener
(
markNeedsPaint
);
}
AnimationController
_positionController
;
CurvedAnimation
_position
;
AnimationController
_reactionController
;
Animation
<
double
>
_reaction
;
final
_CupertinoSwitchState
_state
;
bool
get
value
=>
_value
;
bool
_value
;
...
...
@@ -280,24 +406,6 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
return
;
_value
=
value
;
markNeedsSemanticsUpdate
();
_position
..
curve
=
Curves
.
ease
..
reverseCurve
=
Curves
.
ease
.
flipped
;
if
(
value
)
_positionController
.
forward
();
else
_positionController
.
reverse
();
}
TickerProvider
get
vsync
=>
_vsync
;
TickerProvider
_vsync
;
set
vsync
(
TickerProvider
value
)
{
assert
(
value
!=
null
);
if
(
value
==
_vsync
)
return
;
_vsync
=
value
;
_positionController
.
resync
(
vsync
);
_reactionController
.
resync
(
vsync
);
}
Color
get
activeColor
=>
_activeColor
;
...
...
@@ -343,126 +451,8 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
markNeedsPaint
();
}
DragStartBehavior
get
dragStartBehavior
=>
_drag
.
dragStartBehavior
;
set
dragStartBehavior
(
DragStartBehavior
value
)
{
assert
(
value
!=
null
);
if
(
_drag
.
dragStartBehavior
==
value
)
return
;
_drag
.
dragStartBehavior
=
value
;
}
bool
get
isInteractive
=>
onChanged
!=
null
;
TapGestureRecognizer
_tap
;
HorizontalDragGestureRecognizer
_drag
;
@override
void
attach
(
PipelineOwner
owner
)
{
super
.
attach
(
owner
);
if
(
value
)
_positionController
.
forward
();
else
_positionController
.
reverse
();
if
(
isInteractive
)
{
switch
(
_reactionController
.
status
)
{
case
AnimationStatus
.
forward
:
_reactionController
.
forward
();
break
;
case
AnimationStatus
.
reverse
:
_reactionController
.
reverse
();
break
;
case
AnimationStatus
.
dismissed
:
case
AnimationStatus
.
completed
:
// nothing to do
break
;
}
}
}
@override
void
detach
()
{
_positionController
.
stop
();
_reactionController
.
stop
();
super
.
detach
();
}
void
_handlePositionStateChanged
(
AnimationStatus
status
)
{
if
(
isInteractive
)
{
if
(
status
==
AnimationStatus
.
completed
&&
!
_value
)
onChanged
(
true
);
else
if
(
status
==
AnimationStatus
.
dismissed
&&
_value
)
onChanged
(
false
);
}
}
void
_handleTapDown
(
TapDownDetails
details
)
{
if
(
isInteractive
)
_reactionController
.
forward
();
}
void
_handleTap
()
{
if
(
isInteractive
)
{
onChanged
(!
_value
);
_emitVibration
();
}
}
void
_handleTapUp
(
TapUpDetails
details
)
{
if
(
isInteractive
)
_reactionController
.
reverse
();
}
void
_handleTapCancel
()
{
if
(
isInteractive
)
_reactionController
.
reverse
();
}
void
_handleDragStart
(
DragStartDetails
details
)
{
if
(
isInteractive
)
{
_reactionController
.
forward
();
_emitVibration
();
}
}
void
_handleDragUpdate
(
DragUpdateDetails
details
)
{
if
(
isInteractive
)
{
_position
..
curve
=
null
..
reverseCurve
=
null
;
final
double
delta
=
details
.
primaryDelta
/
_kTrackInnerLength
;
switch
(
textDirection
)
{
case
TextDirection
.
rtl
:
_positionController
.
value
-=
delta
;
break
;
case
TextDirection
.
ltr
:
_positionController
.
value
+=
delta
;
break
;
}
}
}
void
_handleDragEnd
(
DragEndDetails
details
)
{
if
(
_position
.
value
>=
0.5
)
_positionController
.
forward
();
else
_positionController
.
reverse
();
_reactionController
.
reverse
();
}
void
_emitVibration
()
{
switch
(
defaultTargetPlatform
)
{
case
TargetPlatform
.
iOS
:
HapticFeedback
.
lightImpact
();
break
;
case
TargetPlatform
.
android
:
case
TargetPlatform
.
fuchsia
:
case
TargetPlatform
.
linux
:
case
TargetPlatform
.
macOS
:
case
TargetPlatform
.
windows
:
break
;
}
}
@override
bool
hitTestSelf
(
Offset
position
)
=>
true
;
...
...
@@ -470,8 +460,8 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
void
handleEvent
(
PointerEvent
event
,
BoxHitTestEntry
entry
)
{
assert
(
debugHandleEvent
(
event
,
entry
));
if
(
event
is
PointerDownEvent
&&
isInteractive
)
{
_drag
.
addPointer
(
event
);
_tap
.
addPointer
(
event
);
_
state
.
_
drag
.
addPointer
(
event
);
_
state
.
_
tap
.
addPointer
(
event
);
}
}
...
...
@@ -480,7 +470,7 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
super
.
describeSemanticsConfiguration
(
config
);
if
(
isInteractive
)
config
.
onTap
=
_handleTap
;
config
.
onTap
=
_
state
.
_
handleTap
;
config
.
isEnabled
=
isInteractive
;
config
.
isToggled
=
_value
;
...
...
@@ -490,8 +480,8 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
final
Canvas
canvas
=
context
.
canvas
;
final
double
currentValue
=
_position
.
value
;
final
double
currentReactionValue
=
_reaction
.
value
;
final
double
currentValue
=
_
state
.
position
.
value
;
final
double
currentReactionValue
=
_
state
.
_
reaction
.
value
;
double
visualPosition
;
switch
(
textDirection
)
{
...
...
packages/flutter/lib/src/material/switch.dart
View file @
87cbdddd
...
...
@@ -267,6 +267,12 @@ class _SwitchState extends State<Switch> with TickerProviderStateMixin {
bool
get
enabled
=>
widget
.
onChanged
!=
null
;
void
_didFinishDragging
()
{
// The user has finished dragging the thumb of this switch. Rebuild the switch
// to update the animation.
setState
(()
{});
}
Widget
buildMaterialSwitch
(
BuildContext
context
)
{
assert
(
debugCheckHasMaterial
(
context
));
final
ThemeData
theme
=
Theme
.
of
(
context
);
...
...
@@ -313,7 +319,7 @@ class _SwitchState extends State<Switch> with TickerProviderStateMixin {
additionalConstraints:
BoxConstraints
.
tight
(
getSwitchSize
(
theme
)),
hasFocus:
_focused
,
hovering:
_hovering
,
vsync
:
this
,
state
:
this
,
);
},
),
...
...
@@ -380,11 +386,11 @@ class _SwitchRenderObjectWidget extends LeafRenderObjectWidget {
this
.
inactiveTrackColor
,
this
.
configuration
,
this
.
onChanged
,
this
.
vsync
,
this
.
additionalConstraints
,
this
.
dragStartBehavior
,
this
.
hasFocus
,
this
.
hovering
,
this
.
state
,
})
:
super
(
key:
key
);
final
bool
value
;
...
...
@@ -398,11 +404,11 @@ class _SwitchRenderObjectWidget extends LeafRenderObjectWidget {
final
Color
inactiveTrackColor
;
final
ImageConfiguration
configuration
;
final
ValueChanged
<
bool
>
onChanged
;
final
TickerProvider
vsync
;
final
BoxConstraints
additionalConstraints
;
final
DragStartBehavior
dragStartBehavior
;
final
bool
hasFocus
;
final
bool
hovering
;
final
_SwitchState
state
;
@override
_RenderSwitch
createRenderObject
(
BuildContext
context
)
{
...
...
@@ -423,7 +429,7 @@ class _SwitchRenderObjectWidget extends LeafRenderObjectWidget {
additionalConstraints:
additionalConstraints
,
hasFocus:
hasFocus
,
hovering:
hovering
,
vsync:
vsync
,
state:
state
,
);
}
...
...
@@ -446,7 +452,7 @@ class _SwitchRenderObjectWidget extends LeafRenderObjectWidget {
..
dragStartBehavior
=
dragStartBehavior
..
hasFocus
=
hasFocus
..
hovering
=
hovering
..
vsync
=
vsync
;
..
vsync
=
state
;
}
}
...
...
@@ -468,7 +474,7 @@ class _RenderSwitch extends RenderToggleable {
DragStartBehavior
dragStartBehavior
,
bool
hasFocus
,
bool
hovering
,
@required
TickerProvider
vsync
,
@required
this
.
state
,
})
:
assert
(
textDirection
!=
null
),
_activeThumbImage
=
activeThumbImage
,
_inactiveThumbImage
=
inactiveThumbImage
,
...
...
@@ -487,7 +493,7 @@ class _RenderSwitch extends RenderToggleable {
additionalConstraints:
additionalConstraints
,
hasFocus:
hasFocus
,
hovering:
hovering
,
vsync:
vsync
,
vsync:
state
,
)
{
_drag
=
HorizontalDragGestureRecognizer
()
..
onStart
=
_handleDragStart
...
...
@@ -562,6 +568,26 @@ class _RenderSwitch extends RenderToggleable {
_drag
.
dragStartBehavior
=
value
;
}
_SwitchState
state
;
@override
set
value
(
bool
newValue
)
{
assert
(
value
!=
null
);
super
.
value
=
newValue
;
// The widget is rebuilt and we have pending position animation to play.
if
(
_needsPositionAnimation
)
{
_needsPositionAnimation
=
false
;
position
..
curve
=
null
..
reverseCurve
=
null
;
if
(
newValue
)
positionController
.
forward
();
else
positionController
.
reverse
();
}
}
@override
void
detach
()
{
_cachedThumbPainter
?.
dispose
();
...
...
@@ -573,6 +599,8 @@ class _RenderSwitch extends RenderToggleable {
HorizontalDragGestureRecognizer
_drag
;
bool
_needsPositionAnimation
=
false
;
void
_handleDragStart
(
DragStartDetails
details
)
{
if
(
isInteractive
)
reactionController
.
forward
();
...
...
@@ -596,11 +624,12 @@ class _RenderSwitch extends RenderToggleable {
}
void
_handleDragEnd
(
DragEndDetails
details
)
{
if
(
position
.
value
>=
0.5
)
positionController
.
forward
();
else
positionController
.
reverse
(
);
_needsPositionAnimation
=
true
;
if
(
position
.
value
>=
0.5
!=
value
)
onChanged
(!
value
);
reactionController
.
reverse
();
state
.
_didFinishDragging
();
}
@override
...
...
packages/flutter/lib/src/material/toggleable.dart
View file @
87cbdddd
...
...
@@ -70,8 +70,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
_position
=
CurvedAnimation
(
parent:
_positionController
,
curve:
Curves
.
linear
,
)..
addListener
(
markNeedsPaint
)
..
addStatusListener
(
_handlePositionStateChanged
);
)..
addListener
(
markNeedsPaint
);
_reactionController
=
AnimationController
(
duration:
kRadialReactionDuration
,
vsync:
vsync
,
...
...
@@ -335,9 +334,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
/// Called when the control changes value.
///
/// If the control is tapped, [onChanged] is called immediately with the new
/// value. If the control changes value due to an animation (see
/// [positionController]), the callback is called when the animation
/// completes.
/// value.
///
/// The control is considered interactive (see [isInteractive]) if this
/// callback is non-null. If the callback is null, then the control is
...
...
@@ -397,19 +394,6 @@ abstract class RenderToggleable extends RenderConstrainedBox {
super
.
detach
();
}
// Handle the case where the _positionController's value changes because
// the user dragged the toggleable: we may reach 0.0 or 1.0 without
// seeing a tap. The Switch does this.
void
_handlePositionStateChanged
(
AnimationStatus
status
)
{
if
(
isInteractive
&&
!
tristate
)
{
if
(
status
==
AnimationStatus
.
completed
&&
_value
==
false
)
{
onChanged
(
true
);
}
else
if
(
status
==
AnimationStatus
.
dismissed
&&
_value
!=
false
)
{
onChanged
(
false
);
}
}
}
void
_handleTapDown
(
TapDownDetails
details
)
{
if
(
isInteractive
)
{
_downPosition
=
globalToLocal
(
details
.
globalPosition
);
...
...
packages/flutter/test/cupertino/switch_test.dart
View file @
87cbdddd
...
...
@@ -342,14 +342,16 @@ void main() {
);
await
tester
.
pumpAndSettle
();
final
Rect
switchRect
=
tester
.
getRect
(
find
.
byType
(
CupertinoSwitch
));
expect
(
value
,
isFalse
);
TestGesture
gesture
=
await
tester
.
startGesture
(
switchRect
.
center
);
// We have to execute the drag in two frames because the first update will
// just set the start position.
await
gesture
.
moveBy
(
const
Offset
(
20.0
,
0.0
));
await
gesture
.
moveBy
(
const
Offset
(
20.0
,
0.0
));
expect
(
value
,
is
Tru
e
);
expect
(
value
,
is
Fals
e
);
await
gesture
.
up
();
expect
(
value
,
isTrue
);
await
tester
.
pump
();
gesture
=
await
tester
.
startGesture
(
switchRect
.
center
);
...
...
@@ -362,7 +364,10 @@ void main() {
gesture
=
await
tester
.
startGesture
(
switchRect
.
center
);
await
gesture
.
moveBy
(
const
Offset
(-
20.0
,
0.0
));
await
gesture
.
moveBy
(
const
Offset
(-
20.0
,
0.0
));
expect
(
value
,
isTrue
);
await
gesture
.
up
();
expect
(
value
,
isFalse
);
await
tester
.
pump
();
});
testWidgets
(
'Switch can drag (RTL)'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -410,6 +415,77 @@ void main() {
expect
(
value
,
isFalse
);
});
testWidgets
(
'can veto switch dragging result'
,
(
WidgetTester
tester
)
async
{
bool
value
=
false
;
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
Material
(
child:
Center
(
child:
CupertinoSwitch
(
dragStartBehavior:
DragStartBehavior
.
down
,
value:
value
,
onChanged:
(
bool
newValue
)
{
setState
(()
{
value
=
value
||
newValue
;
});
},
),
),
);
},
),
),
);
// Move a little to the right, not past the middle.
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getRect
(
find
.
byType
(
CupertinoSwitch
)).
center
);
await
gesture
.
moveBy
(
const
Offset
(
kTouchSlop
+
0.1
,
0.0
));
await
tester
.
pump
();
await
gesture
.
moveBy
(
const
Offset
(-
kTouchSlop
+
5.1
,
0.0
));
await
tester
.
pump
();
await
gesture
.
up
();
await
tester
.
pump
();
expect
(
value
,
isFalse
);
final
CurvedAnimation
position
=
(
tester
.
state
(
find
.
byType
(
CupertinoSwitch
))
as
dynamic
).
position
as
CurvedAnimation
;
expect
(
position
.
value
,
lessThan
(
0.5
));
await
tester
.
pump
();
await
tester
.
pumpAndSettle
();
expect
(
value
,
isFalse
);
expect
(
position
.
value
,
0
);
// Move past the middle.
gesture
=
await
tester
.
startGesture
(
tester
.
getRect
(
find
.
byType
(
CupertinoSwitch
)).
center
);
await
gesture
.
moveBy
(
const
Offset
(
kTouchSlop
+
0.1
,
0.0
));
await
tester
.
pump
();
await
gesture
.
up
();
await
tester
.
pump
();
expect
(
value
,
isTrue
);
expect
(
position
.
value
,
greaterThan
(
0.5
));
await
tester
.
pump
();
await
tester
.
pumpAndSettle
();
expect
(
value
,
isTrue
);
expect
(
position
.
value
,
1.0
);
// Now move back to the left, the revert animation should play.
gesture
=
await
tester
.
startGesture
(
tester
.
getRect
(
find
.
byType
(
CupertinoSwitch
)).
center
);
await
gesture
.
moveBy
(
const
Offset
(-
kTouchSlop
-
0.1
,
0.0
));
await
tester
.
pump
();
await
gesture
.
up
();
await
tester
.
pump
();
expect
(
value
,
isTrue
);
expect
(
position
.
value
,
lessThan
(
0.5
));
await
tester
.
pump
();
await
tester
.
pumpAndSettle
();
expect
(
value
,
isTrue
);
expect
(
position
.
value
,
1.0
);
});
testWidgets
(
'Switch is translucent when disabled'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
Directionality
(
...
...
packages/flutter/test/material/switch_test.dart
View file @
87cbdddd
...
...
@@ -205,8 +205,9 @@ void main() {
// just set the start position.
await
gesture
.
moveBy
(
const
Offset
(
20.0
,
0.0
));
await
gesture
.
moveBy
(
const
Offset
(
20.0
,
0.0
));
expect
(
value
,
is
Tru
e
);
expect
(
value
,
is
Fals
e
);
await
gesture
.
up
();
expect
(
value
,
isTrue
);
await
tester
.
pump
();
gesture
=
await
tester
.
startGesture
(
switchRect
.
center
);
...
...
@@ -214,11 +215,14 @@ void main() {
await
gesture
.
moveBy
(
const
Offset
(
20.0
,
0.0
));
expect
(
value
,
isTrue
);
await
gesture
.
up
();
expect
(
value
,
isTrue
);
await
tester
.
pump
();
gesture
=
await
tester
.
startGesture
(
switchRect
.
center
);
await
gesture
.
moveBy
(
const
Offset
(-
20.0
,
0.0
));
await
gesture
.
moveBy
(
const
Offset
(-
20.0
,
0.0
));
expect
(
value
,
isTrue
);
await
gesture
.
up
();
expect
(
value
,
isFalse
);
});
...
...
@@ -489,6 +493,84 @@ void main() {
expect
(
tester
.
hasRunningAnimations
,
false
);
});
testWidgets
(
'can veto switch dragging result'
,
(
WidgetTester
tester
)
async
{
bool
value
=
false
;
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
Material
(
child:
Center
(
child:
Switch
(
dragStartBehavior:
DragStartBehavior
.
down
,
value:
value
,
onChanged:
(
bool
newValue
)
{
setState
(()
{
value
=
value
||
newValue
;
});
},
),
),
);
},
),
),
);
// Move a little to the right, not past the middle.
TestGesture
gesture
=
await
tester
.
startGesture
(
tester
.
getRect
(
find
.
byType
(
Switch
)).
center
);
await
gesture
.
moveBy
(
const
Offset
(
kTouchSlop
+
0.1
,
0.0
));
await
tester
.
pump
();
await
gesture
.
moveBy
(
const
Offset
(-
kTouchSlop
+
5.1
,
0.0
));
await
tester
.
pump
();
await
gesture
.
up
();
await
tester
.
pump
();
expect
(
value
,
isFalse
);
final
RenderToggleable
renderObject
=
tester
.
renderObject
<
RenderToggleable
>(
find
.
descendant
(
of:
find
.
byType
(
Switch
),
matching:
find
.
byWidgetPredicate
(
(
Widget
widget
)
=>
widget
.
runtimeType
.
toString
()
==
'_SwitchRenderObjectWidget'
,
),
),
);
expect
(
renderObject
.
position
.
value
,
lessThan
(
0.5
));
await
tester
.
pump
();
await
tester
.
pumpAndSettle
();
expect
(
value
,
isFalse
);
expect
(
renderObject
.
position
.
value
,
0
);
// Move past the middle.
gesture
=
await
tester
.
startGesture
(
tester
.
getRect
(
find
.
byType
(
Switch
)).
center
);
await
gesture
.
moveBy
(
const
Offset
(
kTouchSlop
+
0.1
,
0.0
));
await
tester
.
pump
();
await
gesture
.
up
();
await
tester
.
pump
();
expect
(
value
,
isTrue
);
expect
(
renderObject
.
position
.
value
,
greaterThan
(
0.5
));
await
tester
.
pump
();
await
tester
.
pumpAndSettle
();
expect
(
value
,
isTrue
);
expect
(
renderObject
.
position
.
value
,
1.0
);
// Now move back to the left, the revert animation should play.
gesture
=
await
tester
.
startGesture
(
tester
.
getRect
(
find
.
byType
(
Switch
)).
center
);
await
gesture
.
moveBy
(
const
Offset
(-
kTouchSlop
-
0.1
,
0.0
));
await
tester
.
pump
();
await
gesture
.
up
();
await
tester
.
pump
();
expect
(
value
,
isTrue
);
expect
(
renderObject
.
position
.
value
,
lessThan
(
0.5
));
await
tester
.
pump
();
await
tester
.
pumpAndSettle
();
expect
(
value
,
isTrue
);
expect
(
renderObject
.
position
.
value
,
1.0
);
});
testWidgets
(
'switch has semantic events'
,
(
WidgetTester
tester
)
async
{
dynamic
semanticEvent
;
bool
value
=
false
;
...
...
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