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
Expand all
Hide 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
This diff is collapsed.
Click to expand it.
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