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
14552a96
Unverified
Commit
14552a96
authored
Mar 02, 2021
by
Michael Goderbauer
Committed by
GitHub
Mar 02, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Toggable Refactor (#76745)
parent
fc99e7c0
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
1048 additions
and
1307 deletions
+1048
-1307
checkbox.dart
packages/flutter/lib/src/material/checkbox.dart
+116
-229
radio.dart
packages/flutter/lib/src/material/radio.dart
+62
-201
switch.dart
packages/flutter/lib/src/material/switch.dart
+304
-416
toggleable.dart
packages/flutter/lib/src/material/toggleable.dart
+430
-356
checkbox_test.dart
packages/flutter/test/material/checkbox_test.dart
+46
-35
radio_test.dart
packages/flutter/test/material/radio_test.dart
+26
-1
switch_list_tile_test.dart
packages/flutter/test/material/switch_list_tile_test.dart
+1
-1
switch_test.dart
packages/flutter/test/material/switch_test.dart
+63
-68
No files found.
packages/flutter/lib/src/material/checkbox.dart
View file @
14552a96
...
@@ -290,56 +290,39 @@ class Checkbox extends StatefulWidget {
...
@@ -290,56 +290,39 @@ class Checkbox extends StatefulWidget {
_CheckboxState
createState
()
=>
_CheckboxState
();
_CheckboxState
createState
()
=>
_CheckboxState
();
}
}
class
_CheckboxState
extends
State
<
Checkbox
>
with
TickerProviderStateMixin
{
class
_CheckboxState
extends
State
<
Checkbox
>
with
TickerProviderStateMixin
,
ToggleableStateMixin
{
bool
get
enabled
=>
widget
.
onChanged
!=
null
;
final
_CheckboxPainter
_painter
=
_CheckboxPainter
()
;
late
Map
<
Type
,
Action
<
Intent
>>
_actionMap
;
bool
?
_previousValue
;
@override
@override
void
initState
()
{
void
initState
()
{
super
.
initState
();
super
.
initState
();
_actionMap
=
<
Type
,
Action
<
Intent
>>{
_previousValue
=
widget
.
value
;
ActivateIntent:
CallbackAction
<
ActivateIntent
>(
onInvoke:
_actionHandler
),
};
}
}
void
_actionHandler
(
ActivateIntent
intent
)
{
@override
if
(
widget
.
onChanged
!=
null
)
{
void
didUpdateWidget
(
Checkbox
oldWidget
)
{
switch
(
widget
.
value
)
{
super
.
didUpdateWidget
(
oldWidget
);
case
false
:
if
(
oldWidget
.
value
!=
widget
.
value
)
{
widget
.
onChanged
!(
true
);
_previousValue
=
oldWidget
.
value
;
break
;
animateToValue
();
case
true
:
widget
.
onChanged
!(
widget
.
tristate
?
null
:
false
);
break
;
case
null
:
widget
.
onChanged
!(
false
);
break
;
}
}
}
final
RenderObject
renderObject
=
context
.
findRenderObject
()!;
renderObject
.
sendSemanticsEvent
(
const
TapSemanticEvent
());
}
}
bool
_focused
=
false
;
@override
void
_handleFocusHighlightChanged
(
bool
focused
)
{
void
dispose
()
{
if
(
focused
!=
_focused
)
{
_painter
.
dispose
();
setState
(()
{
_focused
=
focused
;
});
super
.
dispose
();
}
}
}
bool
_hovering
=
false
;
@override
void
_handleHoverChanged
(
bool
hovering
)
{
ValueChanged
<
bool
?>?
get
onChanged
=>
widget
.
onChanged
;
if
(
hovering
!=
_hovering
)
{
setState
(()
{
_hovering
=
hovering
;
});
@override
}
bool
get
tristate
=>
widget
.
tristate
;
}
Set
<
MaterialState
>
get
_states
=>
<
MaterialState
>{
@override
if
(!
enabled
)
MaterialState
.
disabled
,
bool
?
get
value
=>
widget
.
value
;
if
(
_hovering
)
MaterialState
.
hovered
,
if
(
_focused
)
MaterialState
.
focused
,
if
(
widget
.
value
==
null
||
widget
.
value
!)
MaterialState
.
selected
,
};
MaterialStateProperty
<
Color
?>
get
_widgetFillColor
{
MaterialStateProperty
<
Color
?>
get
_widgetFillColor
{
return
MaterialStateProperty
.
resolveWith
((
Set
<
MaterialState
>
states
)
{
return
MaterialStateProperty
.
resolveWith
((
Set
<
MaterialState
>
states
)
{
...
@@ -386,14 +369,17 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
...
@@ -386,14 +369,17 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
break
;
break
;
}
}
size
+=
effectiveVisualDensity
.
baseSizeAdjustment
;
size
+=
effectiveVisualDensity
.
baseSizeAdjustment
;
final
BoxConstraints
additionalConstraints
=
BoxConstraints
.
tight
(
size
);
final
MouseCursor
effectiveMouseCursor
=
MaterialStateProperty
.
resolveAs
<
MouseCursor
?>(
widget
.
mouseCursor
,
_states
)
final
MaterialStateProperty
<
MouseCursor
>
effectiveMouseCursor
=
MaterialStateProperty
.
resolveWith
<
MouseCursor
>((
Set
<
MaterialState
>
states
)
{
??
themeData
.
checkboxTheme
.
mouseCursor
?.
resolve
(
_states
)
return
MaterialStateProperty
.
resolveAs
<
MouseCursor
?>(
widget
.
mouseCursor
,
states
)
??
MaterialStateProperty
.
resolveAs
<
MouseCursor
>(
MaterialStateMouseCursor
.
clickable
,
_states
);
??
themeData
.
checkboxTheme
.
mouseCursor
?.
resolve
(
states
)
??
MaterialStateMouseCursor
.
clickable
.
resolve
(
states
);
});
// Colors need to be resolved in selected and non selected states separately
// Colors need to be resolved in selected and non selected states separately
// so that they can be lerped between.
// so that they can be lerped between.
final
Set
<
MaterialState
>
activeStates
=
_
states
..
add
(
MaterialState
.
selected
);
final
Set
<
MaterialState
>
activeStates
=
states
..
add
(
MaterialState
.
selected
);
final
Set
<
MaterialState
>
inactiveStates
=
_
states
..
remove
(
MaterialState
.
selected
);
final
Set
<
MaterialState
>
inactiveStates
=
states
..
remove
(
MaterialState
.
selected
);
final
Color
effectiveActiveColor
=
widget
.
fillColor
?.
resolve
(
activeStates
)
final
Color
effectiveActiveColor
=
widget
.
fillColor
?.
resolve
(
activeStates
)
??
_widgetFillColor
.
resolve
(
activeStates
)
??
_widgetFillColor
.
resolve
(
activeStates
)
??
themeData
.
checkboxTheme
.
fillColor
?.
resolve
(
activeStates
)
??
themeData
.
checkboxTheme
.
fillColor
?.
resolve
(
activeStates
)
...
@@ -403,13 +389,13 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
...
@@ -403,13 +389,13 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
??
themeData
.
checkboxTheme
.
fillColor
?.
resolve
(
inactiveStates
)
??
themeData
.
checkboxTheme
.
fillColor
?.
resolve
(
inactiveStates
)
??
_defaultFillColor
.
resolve
(
inactiveStates
);
??
_defaultFillColor
.
resolve
(
inactiveStates
);
final
Set
<
MaterialState
>
focusedStates
=
_
states
..
add
(
MaterialState
.
focused
);
final
Set
<
MaterialState
>
focusedStates
=
states
..
add
(
MaterialState
.
focused
);
final
Color
effectiveFocusOverlayColor
=
widget
.
overlayColor
?.
resolve
(
focusedStates
)
final
Color
effectiveFocusOverlayColor
=
widget
.
overlayColor
?.
resolve
(
focusedStates
)
??
widget
.
focusColor
??
widget
.
focusColor
??
themeData
.
checkboxTheme
.
overlayColor
?.
resolve
(
focusedStates
)
??
themeData
.
checkboxTheme
.
overlayColor
?.
resolve
(
focusedStates
)
??
themeData
.
focusColor
;
??
themeData
.
focusColor
;
final
Set
<
MaterialState
>
hoveredStates
=
_
states
..
add
(
MaterialState
.
hovered
);
final
Set
<
MaterialState
>
hoveredStates
=
states
..
add
(
MaterialState
.
hovered
);
final
Color
effectiveHoverOverlayColor
=
widget
.
overlayColor
?.
resolve
(
hoveredStates
)
final
Color
effectiveHoverOverlayColor
=
widget
.
overlayColor
?.
resolve
(
hoveredStates
)
??
widget
.
hoverColor
??
widget
.
hoverColor
??
themeData
.
checkboxTheme
.
overlayColor
?.
resolve
(
hoveredStates
)
??
themeData
.
checkboxTheme
.
overlayColor
?.
resolve
(
hoveredStates
)
...
@@ -425,195 +411,96 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
...
@@ -425,195 +411,96 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
??
themeData
.
checkboxTheme
.
overlayColor
?.
resolve
(
inactivePressedStates
)
??
themeData
.
checkboxTheme
.
overlayColor
?.
resolve
(
inactivePressedStates
)
??
effectiveActiveColor
.
withAlpha
(
kRadialReactionAlpha
);
??
effectiveActiveColor
.
withAlpha
(
kRadialReactionAlpha
);
final
Color
effectiveCheckColor
=
widget
.
checkColor
final
Color
effectiveCheckColor
=
widget
.
checkColor
??
themeData
.
checkboxTheme
.
checkColor
?.
resolve
(
_
states
)
??
themeData
.
checkboxTheme
.
checkColor
?.
resolve
(
states
)
??
const
Color
(
0xFFFFFFFF
);
??
const
Color
(
0xFFFFFFFF
);
return
FocusableActionDetector
(
return
Semantics
(
actions:
_actionMap
,
checked:
widget
.
value
==
true
,
focusNode:
widget
.
focusNode
,
child:
buildToggleable
(
autofocus:
widget
.
autofocus
,
mouseCursor:
effectiveMouseCursor
,
enabled:
enabled
,
focusNode:
widget
.
focusNode
,
onShowFocusHighlight:
_handleFocusHighlightChanged
,
autofocus:
widget
.
autofocus
,
onShowHoverHighlight:
_handleHoverChanged
,
size:
size
,
mouseCursor:
effectiveMouseCursor
,
painter:
_painter
child:
Builder
(
..
position
=
position
builder:
(
BuildContext
context
)
{
..
reaction
=
reaction
return
_CheckboxRenderObjectWidget
(
..
reactionFocusFade
=
reactionFocusFade
value:
widget
.
value
,
..
reactionHoverFade
=
reactionHoverFade
tristate:
widget
.
tristate
,
..
inactiveReactionColor
=
effectiveInactivePressedOverlayColor
activeColor:
effectiveActiveColor
,
..
reactionColor
=
effectiveActivePressedOverlayColor
checkColor:
effectiveCheckColor
,
..
hoverColor
=
effectiveHoverOverlayColor
inactiveColor:
effectiveInactiveColor
,
..
focusColor
=
effectiveFocusOverlayColor
focusColor:
effectiveFocusOverlayColor
,
..
splashRadius
=
widget
.
splashRadius
??
themeData
.
checkboxTheme
.
splashRadius
??
kRadialReactionRadius
hoverColor:
effectiveHoverOverlayColor
,
..
downPosition
=
downPosition
reactionColor:
effectiveActivePressedOverlayColor
,
..
isFocused
=
states
.
contains
(
MaterialState
.
focused
)
inactiveReactionColor:
effectiveInactivePressedOverlayColor
,
..
isHovered
=
states
.
contains
(
MaterialState
.
hovered
)
splashRadius:
widget
.
splashRadius
??
themeData
.
checkboxTheme
.
splashRadius
??
kRadialReactionRadius
,
..
activeColor
=
effectiveActiveColor
onChanged:
widget
.
onChanged
,
..
inactiveColor
=
effectiveInactiveColor
additionalConstraints:
additionalConstraints
,
..
checkColor
=
effectiveCheckColor
vsync:
this
,
..
value
=
value
hasFocus:
_focused
,
..
previousValue
=
_previousValue
hovering:
_hovering
,
..
shape
=
widget
.
shape
??
themeData
.
checkboxTheme
.
shape
??
const
RoundedRectangleBorder
(
side:
widget
.
side
??
themeData
.
checkboxTheme
.
side
,
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
1.0
))
shape:
widget
.
shape
??
themeData
.
checkboxTheme
.
shape
??
const
RoundedRectangleBorder
(
)
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
1.0
)),
..
side
=
widget
.
side
??
themeData
.
checkboxTheme
.
side
,
),
);
},
),
),
);
);
}
}
}
}
class
_CheckboxRenderObjectWidget
extends
LeafRenderObjectWidget
{
const
double
_kEdgeSize
=
Checkbox
.
width
;
const
_CheckboxRenderObjectWidget
({
const
double
_kStrokeWidth
=
2.0
;
Key
?
key
,
required
this
.
value
,
required
this
.
tristate
,
required
this
.
activeColor
,
required
this
.
checkColor
,
required
this
.
inactiveColor
,
required
this
.
focusColor
,
required
this
.
hoverColor
,
required
this
.
reactionColor
,
required
this
.
inactiveReactionColor
,
required
this
.
splashRadius
,
required
this
.
onChanged
,
required
this
.
vsync
,
required
this
.
additionalConstraints
,
required
this
.
hasFocus
,
required
this
.
hovering
,
required
this
.
shape
,
required
this
.
side
,
})
:
assert
(
tristate
!=
null
),
assert
(
tristate
||
value
!=
null
),
assert
(
activeColor
!=
null
),
assert
(
inactiveColor
!=
null
),
assert
(
vsync
!=
null
),
super
(
key:
key
);
final
bool
?
value
;
final
bool
tristate
;
final
bool
hasFocus
;
final
bool
hovering
;
final
Color
activeColor
;
final
Color
checkColor
;
final
Color
inactiveColor
;
final
Color
focusColor
;
final
Color
hoverColor
;
final
Color
reactionColor
;
final
Color
inactiveReactionColor
;
final
double
splashRadius
;
final
ValueChanged
<
bool
?>?
onChanged
;
final
TickerProvider
vsync
;
final
BoxConstraints
additionalConstraints
;
final
OutlinedBorder
shape
;
final
BorderSide
?
side
;
@override
_RenderCheckbox
createRenderObject
(
BuildContext
context
)
=>
_RenderCheckbox
(
value:
value
,
tristate:
tristate
,
activeColor:
activeColor
,
checkColor:
checkColor
,
inactiveColor:
inactiveColor
,
focusColor:
focusColor
,
hoverColor:
hoverColor
,
reactionColor:
reactionColor
,
inactiveReactionColor:
inactiveReactionColor
,
splashRadius:
splashRadius
,
onChanged:
onChanged
,
vsync:
vsync
,
additionalConstraints:
additionalConstraints
,
hasFocus:
hasFocus
,
hovering:
hovering
,
shape:
shape
,
side:
side
,
);
@override
class
_CheckboxPainter
extends
ToggleablePainter
{
void
updateRenderObject
(
BuildContext
context
,
_RenderCheckbox
renderObject
)
{
Color
get
checkColor
=>
_checkColor
!;
renderObject
Color
?
_checkColor
;
// The `tristate` must be changed before `value` due to the assertion at
set
checkColor
(
Color
value
)
{
// the beginning of `set value`.
if
(
_checkColor
==
value
)
{
..
tristate
=
tristate
return
;
..
value
=
value
}
..
activeColor
=
activeColor
_checkColor
=
value
;
..
checkColor
=
checkColor
notifyListeners
();
..
inactiveColor
=
inactiveColor
..
focusColor
=
focusColor
..
hoverColor
=
hoverColor
..
reactionColor
=
reactionColor
..
inactiveReactionColor
=
inactiveReactionColor
..
splashRadius
=
splashRadius
..
onChanged
=
onChanged
..
additionalConstraints
=
additionalConstraints
..
vsync
=
vsync
..
hasFocus
=
hasFocus
..
hovering
=
hovering
..
shape
=
shape
..
side
=
side
;
}
}
}
const
double
_kEdgeSize
=
Checkbox
.
width
;
bool
?
get
value
=>
_value
;
const
double
_kStrokeWidth
=
2.0
;
bool
?
_value
;
set
value
(
bool
?
value
)
{
if
(
_value
==
value
)
{
return
;
}
_value
=
value
;
notifyListeners
();
}
class
_RenderCheckbox
extends
RenderToggleable
{
bool
?
get
previousValue
=>
_previousValue
;
_RenderCheckbox
({
bool
?
_previousValue
;
bool
?
value
,
set
previousValue
(
bool
?
value
)
{
required
bool
tristate
,
if
(
_previousValue
==
value
)
{
required
Color
activeColor
,
return
;
required
this
.
checkColor
,
}
required
Color
inactiveColor
,
_previousValue
=
value
;
Color
?
focusColor
,
notifyListeners
();
Color
?
hoverColor
,
}
Color
?
reactionColor
,
Color
?
inactiveReactionColor
,
required
double
splashRadius
,
required
BoxConstraints
additionalConstraints
,
ValueChanged
<
bool
?>?
onChanged
,
required
bool
hasFocus
,
required
bool
hovering
,
required
this
.
shape
,
required
this
.
side
,
required
TickerProvider
vsync
,
})
:
_oldValue
=
value
,
super
(
value:
value
,
tristate:
tristate
,
activeColor:
activeColor
,
inactiveColor:
inactiveColor
,
focusColor:
focusColor
,
hoverColor:
hoverColor
,
reactionColor:
reactionColor
,
inactiveReactionColor:
inactiveReactionColor
,
splashRadius:
splashRadius
,
onChanged:
onChanged
,
additionalConstraints:
additionalConstraints
,
vsync:
vsync
,
hasFocus:
hasFocus
,
hovering:
hovering
,
);
bool
?
_oldValue
;
Color
checkColor
;
OutlinedBorder
shape
;
BorderSide
?
side
;
@override
OutlinedBorder
get
shape
=>
_shape
!;
set
value
(
bool
?
newValue
)
{
OutlinedBorder
?
_shape
;
if
(
newValue
==
value
)
set
shape
(
OutlinedBorder
value
)
{
if
(
_shape
==
value
)
{
return
;
return
;
_oldValue
=
value
;
}
super
.
value
=
newValue
;
_shape
=
value
;
notifyListeners
();
}
}
@override
BorderSide
?
get
side
=>
_side
;
void
describeSemanticsConfiguration
(
SemanticsConfiguration
config
)
{
BorderSide
?
_side
;
super
.
describeSemanticsConfiguration
(
config
);
set
side
(
BorderSide
?
value
)
{
config
.
isChecked
=
value
==
true
;
if
(
_side
==
value
)
{
return
;
}
_side
=
value
;
notifyListeners
();
}
}
// The square outer bounds of the checkbox at t, with the specified origin.
// The square outer bounds of the checkbox at t, with the specified origin.
...
@@ -644,10 +531,11 @@ class _RenderCheckbox extends RenderToggleable {
...
@@ -644,10 +531,11 @@ class _RenderCheckbox extends RenderToggleable {
void
_drawBorder
(
Canvas
canvas
,
Rect
outer
,
double
t
,
Paint
paint
)
{
void
_drawBorder
(
Canvas
canvas
,
Rect
outer
,
double
t
,
Paint
paint
)
{
assert
(
t
>=
0.0
&&
t
<=
0.5
);
assert
(
t
>=
0.0
&&
t
<=
0.5
);
OutlinedBorder
resolvedShape
=
shape
;
if
(
side
==
null
)
{
if
(
side
==
null
)
{
shape
=
s
hape
.
copyWith
(
side:
BorderSide
(
width:
2
,
color:
paint
.
color
));
resolvedShape
=
resolvedS
hape
.
copyWith
(
side:
BorderSide
(
width:
2
,
color:
paint
.
color
));
}
}
s
hape
.
copyWith
(
side:
side
).
paint
(
canvas
,
outer
);
resolvedS
hape
.
copyWith
(
side:
side
).
paint
(
canvas
,
outer
);
}
}
void
_drawCheck
(
Canvas
canvas
,
Offset
origin
,
double
t
,
Paint
paint
)
{
void
_drawCheck
(
Canvas
canvas
,
Offset
origin
,
double
t
,
Paint
paint
)
{
...
@@ -686,19 +574,18 @@ class _RenderCheckbox extends RenderToggleable {
...
@@ -686,19 +574,18 @@ class _RenderCheckbox extends RenderToggleable {
}
}
@override
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
void
paint
(
Canvas
canvas
,
Size
size
)
{
final
Canvas
canvas
=
context
.
canvas
;
paintRadialReaction
(
canvas:
canvas
,
origin:
size
.
center
(
Offset
.
zero
));
paintRadialReaction
(
canvas
,
offset
,
size
.
center
(
Offset
.
zero
));
final
Paint
strokePaint
=
_createStrokePaint
();
final
Paint
strokePaint
=
_createStrokePaint
();
final
Offset
origin
=
offset
+
(
size
/
2.0
-
const
Size
.
square
(
_kEdgeSize
)
/
2.0
as
Offset
)
;
final
Offset
origin
=
size
/
2.0
-
const
Size
.
square
(
_kEdgeSize
)
/
2.0
as
Offset
;
final
AnimationStatus
status
=
position
.
status
;
final
AnimationStatus
status
=
position
.
status
;
final
double
tNormalized
=
status
==
AnimationStatus
.
forward
||
status
==
AnimationStatus
.
completed
final
double
tNormalized
=
status
==
AnimationStatus
.
forward
||
status
==
AnimationStatus
.
completed
?
position
.
value
?
position
.
value
:
1.0
-
position
.
value
;
:
1.0
-
position
.
value
;
// Four cases: false to null, false to true, null to false, true to false
// Four cases: false to null, false to true, null to false, true to false
if
(
_old
Value
==
false
||
value
==
false
)
{
if
(
previous
Value
==
false
||
value
==
false
)
{
final
double
t
=
value
==
false
?
1.0
-
tNormalized
:
tNormalized
;
final
double
t
=
value
==
false
?
1.0
-
tNormalized
:
tNormalized
;
final
Rect
outer
=
_outerRectAt
(
origin
,
t
);
final
Rect
outer
=
_outerRectAt
(
origin
,
t
);
final
Path
emptyCheckboxPath
=
shape
.
copyWith
(
side:
side
).
getOuterPath
(
outer
);
final
Path
emptyCheckboxPath
=
shape
.
copyWith
(
side:
side
).
getOuterPath
(
outer
);
...
@@ -710,7 +597,7 @@ class _RenderCheckbox extends RenderToggleable {
...
@@ -710,7 +597,7 @@ class _RenderCheckbox extends RenderToggleable {
canvas
.
drawPath
(
emptyCheckboxPath
,
paint
);
canvas
.
drawPath
(
emptyCheckboxPath
,
paint
);
final
double
tShrink
=
(
t
-
0.5
)
*
2.0
;
final
double
tShrink
=
(
t
-
0.5
)
*
2.0
;
if
(
_old
Value
==
null
||
value
==
null
)
if
(
previous
Value
==
null
||
value
==
null
)
_drawDash
(
canvas
,
origin
,
tShrink
,
strokePaint
);
_drawDash
(
canvas
,
origin
,
tShrink
,
strokePaint
);
else
else
_drawCheck
(
canvas
,
origin
,
tShrink
,
strokePaint
);
_drawCheck
(
canvas
,
origin
,
tShrink
,
strokePaint
);
...
@@ -722,7 +609,7 @@ class _RenderCheckbox extends RenderToggleable {
...
@@ -722,7 +609,7 @@ class _RenderCheckbox extends RenderToggleable {
if
(
tNormalized
<=
0.5
)
{
if
(
tNormalized
<=
0.5
)
{
final
double
tShrink
=
1.0
-
tNormalized
*
2.0
;
final
double
tShrink
=
1.0
-
tNormalized
*
2.0
;
if
(
_old
Value
==
true
)
if
(
previous
Value
==
true
)
_drawCheck
(
canvas
,
origin
,
tShrink
,
strokePaint
);
_drawCheck
(
canvas
,
origin
,
tShrink
,
strokePaint
);
else
else
_drawDash
(
canvas
,
origin
,
tShrink
,
strokePaint
);
_drawDash
(
canvas
,
origin
,
tShrink
,
strokePaint
);
...
...
packages/flutter/lib/src/material/radio.dart
View file @
14552a96
...
@@ -353,45 +353,14 @@ class Radio<T> extends StatefulWidget {
...
@@ -353,45 +353,14 @@ class Radio<T> extends StatefulWidget {
/// {@macro flutter.widgets.Focus.autofocus}
/// {@macro flutter.widgets.Focus.autofocus}
final
bool
autofocus
;
final
bool
autofocus
;
bool
get
_selected
=>
value
==
groupValue
;
@override
@override
_RadioState
<
T
>
createState
()
=>
_RadioState
<
T
>();
_RadioState
<
T
>
createState
()
=>
_RadioState
<
T
>();
}
}
class
_RadioState
<
T
>
extends
State
<
Radio
<
T
>>
with
TickerProviderStateMixin
{
class
_RadioState
<
T
>
extends
State
<
Radio
<
T
>>
with
TickerProviderStateMixin
,
ToggleableStateMixin
{
bool
get
enabled
=>
widget
.
onChanged
!=
null
;
final
_RadioPainter
_painter
=
_RadioPainter
();
late
Map
<
Type
,
Action
<
Intent
>>
_actionMap
;
@override
void
initState
()
{
super
.
initState
();
_actionMap
=
<
Type
,
Action
<
Intent
>>{
ActivateIntent:
CallbackAction
<
ActivateIntent
>(
onInvoke:
_actionHandler
,
),
};
}
void
_actionHandler
(
ActivateIntent
intent
)
{
if
(
widget
.
onChanged
!=
null
)
{
widget
.
onChanged
!(
widget
.
value
);
}
final
RenderObject
renderObject
=
context
.
findRenderObject
()!;
renderObject
.
sendSemanticsEvent
(
const
TapSemanticEvent
());
}
bool
_focused
=
false
;
void
_handleHighlightChanged
(
bool
focused
)
{
if
(
_focused
!=
focused
)
{
setState
(()
{
_focused
=
focused
;
});
}
}
bool
_hovering
=
false
;
void
_handleHoverChanged
(
bool
hovering
)
{
if
(
_hovering
!=
hovering
)
{
setState
(()
{
_hovering
=
hovering
;
});
}
}
void
_handleChanged
(
bool
?
selected
)
{
void
_handleChanged
(
bool
?
selected
)
{
if
(
selected
==
null
)
{
if
(
selected
==
null
)
{
...
@@ -403,14 +372,28 @@ class _RadioState<T> extends State<Radio<T>> with TickerProviderStateMixin {
...
@@ -403,14 +372,28 @@ class _RadioState<T> extends State<Radio<T>> with TickerProviderStateMixin {
}
}
}
}
bool
get
_selected
=>
widget
.
value
==
widget
.
groupValue
;
@override
void
didUpdateWidget
(
Radio
<
T
>
oldWidget
)
{
super
.
didUpdateWidget
(
oldWidget
);
if
(
widget
.
_selected
!=
oldWidget
.
_selected
)
{
animateToValue
();
}
}
@override
void
dispose
()
{
_painter
.
dispose
();
super
.
dispose
();
}
@override
ValueChanged
<
bool
?>?
get
onChanged
=>
widget
.
onChanged
!=
null
?
_handleChanged
:
null
;
@override
bool
get
tristate
=>
widget
.
toggleable
;
Set
<
MaterialState
>
get
_states
=>
<
MaterialState
>{
@override
if
(!
enabled
)
MaterialState
.
disabled
,
bool
?
get
value
=>
widget
.
_selected
;
if
(
_hovering
)
MaterialState
.
hovered
,
if
(
_focused
)
MaterialState
.
focused
,
if
(
_selected
)
MaterialState
.
selected
,
};
MaterialStateProperty
<
Color
?>
get
_widgetFillColor
{
MaterialStateProperty
<
Color
?>
get
_widgetFillColor
{
return
MaterialStateProperty
.
resolveWith
((
Set
<
MaterialState
>
states
)
{
return
MaterialStateProperty
.
resolveWith
((
Set
<
MaterialState
>
states
)
{
...
@@ -457,15 +440,17 @@ class _RadioState<T> extends State<Radio<T>> with TickerProviderStateMixin {
...
@@ -457,15 +440,17 @@ class _RadioState<T> extends State<Radio<T>> with TickerProviderStateMixin {
break
;
break
;
}
}
size
+=
effectiveVisualDensity
.
baseSizeAdjustment
;
size
+=
effectiveVisualDensity
.
baseSizeAdjustment
;
final
BoxConstraints
additionalConstraints
=
BoxConstraints
.
tight
(
size
);
final
MouseCursor
effectiveMouseCursor
=
MaterialStateProperty
.
resolveAs
<
MouseCursor
?>(
widget
.
mouseCursor
,
_states
)
final
MaterialStateProperty
<
MouseCursor
>
effectiveMouseCursor
=
MaterialStateProperty
.
resolveWith
<
MouseCursor
>((
Set
<
MaterialState
>
states
)
{
??
themeData
.
radioTheme
.
mouseCursor
?.
resolve
(
_states
)
return
MaterialStateProperty
.
resolveAs
<
MouseCursor
?>(
widget
.
mouseCursor
,
states
)
??
MaterialStateProperty
.
resolveAs
<
MouseCursor
>(
MaterialStateMouseCursor
.
clickable
,
_states
);
??
themeData
.
radioTheme
.
mouseCursor
?.
resolve
(
states
)
??
MaterialStateProperty
.
resolveAs
<
MouseCursor
>(
MaterialStateMouseCursor
.
clickable
,
states
);
});
// Colors need to be resolved in selected and non selected states separately
// Colors need to be resolved in selected and non selected states separately
// so that they can be lerped between.
// so that they can be lerped between.
final
Set
<
MaterialState
>
activeStates
=
_
states
..
add
(
MaterialState
.
selected
);
final
Set
<
MaterialState
>
activeStates
=
states
..
add
(
MaterialState
.
selected
);
final
Set
<
MaterialState
>
inactiveStates
=
_
states
..
remove
(
MaterialState
.
selected
);
final
Set
<
MaterialState
>
inactiveStates
=
states
..
remove
(
MaterialState
.
selected
);
final
Color
effectiveActiveColor
=
widget
.
fillColor
?.
resolve
(
activeStates
)
final
Color
effectiveActiveColor
=
widget
.
fillColor
?.
resolve
(
activeStates
)
??
_widgetFillColor
.
resolve
(
activeStates
)
??
_widgetFillColor
.
resolve
(
activeStates
)
??
themeData
.
radioTheme
.
fillColor
?.
resolve
(
activeStates
)
??
themeData
.
radioTheme
.
fillColor
?.
resolve
(
activeStates
)
...
@@ -475,13 +460,13 @@ class _RadioState<T> extends State<Radio<T>> with TickerProviderStateMixin {
...
@@ -475,13 +460,13 @@ class _RadioState<T> extends State<Radio<T>> with TickerProviderStateMixin {
??
themeData
.
radioTheme
.
fillColor
?.
resolve
(
inactiveStates
)
??
themeData
.
radioTheme
.
fillColor
?.
resolve
(
inactiveStates
)
??
_defaultFillColor
.
resolve
(
inactiveStates
);
??
_defaultFillColor
.
resolve
(
inactiveStates
);
final
Set
<
MaterialState
>
focusedStates
=
_
states
..
add
(
MaterialState
.
focused
);
final
Set
<
MaterialState
>
focusedStates
=
states
..
add
(
MaterialState
.
focused
);
final
Color
effectiveFocusOverlayColor
=
widget
.
overlayColor
?.
resolve
(
focusedStates
)
final
Color
effectiveFocusOverlayColor
=
widget
.
overlayColor
?.
resolve
(
focusedStates
)
??
widget
.
focusColor
??
widget
.
focusColor
??
themeData
.
radioTheme
.
overlayColor
?.
resolve
(
focusedStates
)
??
themeData
.
radioTheme
.
overlayColor
?.
resolve
(
focusedStates
)
??
themeData
.
focusColor
;
??
themeData
.
focusColor
;
final
Set
<
MaterialState
>
hoveredStates
=
_
states
..
add
(
MaterialState
.
hovered
);
final
Set
<
MaterialState
>
hoveredStates
=
states
..
add
(
MaterialState
.
hovered
);
final
Color
effectiveHoverOverlayColor
=
widget
.
overlayColor
?.
resolve
(
hoveredStates
)
final
Color
effectiveHoverOverlayColor
=
widget
.
overlayColor
?.
resolve
(
hoveredStates
)
??
widget
.
hoverColor
??
widget
.
hoverColor
??
themeData
.
radioTheme
.
overlayColor
?.
resolve
(
hoveredStates
)
??
themeData
.
radioTheme
.
overlayColor
?.
resolve
(
hoveredStates
)
...
@@ -497,156 +482,40 @@ class _RadioState<T> extends State<Radio<T>> with TickerProviderStateMixin {
...
@@ -497,156 +482,40 @@ class _RadioState<T> extends State<Radio<T>> with TickerProviderStateMixin {
??
themeData
.
radioTheme
.
overlayColor
?.
resolve
(
inactivePressedStates
)
??
themeData
.
radioTheme
.
overlayColor
?.
resolve
(
inactivePressedStates
)
??
effectiveActiveColor
.
withAlpha
(
kRadialReactionAlpha
);
??
effectiveActiveColor
.
withAlpha
(
kRadialReactionAlpha
);
return
Semantics
(
return
FocusableActionDetector
(
inMutuallyExclusiveGroup:
true
,
actions:
_actionMap
,
checked:
widget
.
_selected
,
focusNode:
widget
.
focusNode
,
child:
buildToggleable
(
autofocus:
widget
.
autofocus
,
focusNode:
widget
.
focusNode
,
mouseCursor:
effectiveMouseCursor
,
autofocus:
widget
.
autofocus
,
enabled:
enabled
,
mouseCursor:
effectiveMouseCursor
,
onShowFocusHighlight:
_handleHighlightChanged
,
size:
size
,
onShowHoverHighlight:
_handleHoverChanged
,
painter:
_painter
child:
Builder
(
..
position
=
position
builder:
(
BuildContext
context
)
{
..
reaction
=
reaction
return
_RadioRenderObjectWidget
(
..
reactionFocusFade
=
reactionFocusFade
selected:
_selected
,
..
reactionHoverFade
=
reactionHoverFade
activeColor:
effectiveActiveColor
,
..
inactiveReactionColor
=
effectiveInactivePressedOverlayColor
inactiveColor:
effectiveInactiveColor
,
..
reactionColor
=
effectiveActivePressedOverlayColor
focusColor:
effectiveFocusOverlayColor
,
..
hoverColor
=
effectiveHoverOverlayColor
hoverColor:
effectiveHoverOverlayColor
,
..
focusColor
=
effectiveFocusOverlayColor
reactionColor:
effectiveActivePressedOverlayColor
,
..
splashRadius
=
widget
.
splashRadius
??
themeData
.
radioTheme
.
splashRadius
??
kRadialReactionRadius
inactiveReactionColor:
effectiveInactivePressedOverlayColor
,
..
downPosition
=
downPosition
splashRadius:
widget
.
splashRadius
??
themeData
.
radioTheme
.
splashRadius
??
kRadialReactionRadius
,
..
isFocused
=
states
.
contains
(
MaterialState
.
focused
)
onChanged:
enabled
?
_handleChanged
:
null
,
..
isHovered
=
states
.
contains
(
MaterialState
.
hovered
)
toggleable:
widget
.
toggleable
,
..
activeColor
=
effectiveActiveColor
additionalConstraints:
additionalConstraints
,
..
inactiveColor
=
effectiveInactiveColor
vsync:
this
,
hasFocus:
_focused
,
hovering:
_hovering
,
);
},
),
),
);
);
}
}
}
}
class
_RadioRenderObjectWidget
extends
LeafRenderObjectWidget
{
class
_RadioPainter
extends
ToggleablePainter
{
const
_RadioRenderObjectWidget
({
Key
?
key
,
required
this
.
selected
,
required
this
.
activeColor
,
required
this
.
inactiveColor
,
required
this
.
focusColor
,
required
this
.
hoverColor
,
required
this
.
reactionColor
,
required
this
.
inactiveReactionColor
,
required
this
.
additionalConstraints
,
this
.
onChanged
,
required
this
.
toggleable
,
required
this
.
vsync
,
required
this
.
hasFocus
,
required
this
.
hovering
,
required
this
.
splashRadius
,
})
:
assert
(
selected
!=
null
),
assert
(
activeColor
!=
null
),
assert
(
inactiveColor
!=
null
),
assert
(
vsync
!=
null
),
assert
(
toggleable
!=
null
),
super
(
key:
key
);
final
bool
selected
;
final
bool
hasFocus
;
final
bool
hovering
;
final
Color
inactiveColor
;
final
Color
activeColor
;
final
Color
focusColor
;
final
Color
hoverColor
;
final
Color
reactionColor
;
final
Color
inactiveReactionColor
;
final
double
splashRadius
;
final
ValueChanged
<
bool
?>?
onChanged
;
final
bool
toggleable
;
final
TickerProvider
vsync
;
final
BoxConstraints
additionalConstraints
;
@override
@override
_RenderRadio
createRenderObject
(
BuildContext
context
)
=>
_RenderRadio
(
void
paint
(
Canvas
canvas
,
Size
size
)
{
value:
selected
,
paintRadialReaction
(
canvas:
canvas
,
origin:
size
.
center
(
Offset
.
zero
));
activeColor:
activeColor
,
inactiveColor:
inactiveColor
,
focusColor:
focusColor
,
hoverColor:
hoverColor
,
reactionColor:
reactionColor
,
inactiveReactionColor:
inactiveReactionColor
,
splashRadius:
splashRadius
,
onChanged:
onChanged
,
tristate:
toggleable
,
vsync:
vsync
,
additionalConstraints:
additionalConstraints
,
hasFocus:
hasFocus
,
hovering:
hovering
,
);
@override
final
Offset
center
=
(
Offset
.
zero
&
size
).
center
;
void
updateRenderObject
(
BuildContext
context
,
_RenderRadio
renderObject
)
{
renderObject
..
value
=
selected
..
activeColor
=
activeColor
..
inactiveColor
=
inactiveColor
..
focusColor
=
focusColor
..
hoverColor
=
hoverColor
..
reactionColor
=
reactionColor
..
inactiveReactionColor
=
inactiveReactionColor
..
splashRadius
=
splashRadius
..
onChanged
=
onChanged
..
tristate
=
toggleable
..
additionalConstraints
=
additionalConstraints
..
vsync
=
vsync
..
hasFocus
=
hasFocus
..
hovering
=
hovering
;
}
}
class
_RenderRadio
extends
RenderToggleable
{
_RenderRadio
({
required
bool
value
,
required
Color
activeColor
,
required
Color
inactiveColor
,
required
Color
focusColor
,
required
Color
hoverColor
,
required
Color
reactionColor
,
required
Color
inactiveReactionColor
,
required
double
splashRadius
,
required
ValueChanged
<
bool
?>?
onChanged
,
required
bool
tristate
,
required
BoxConstraints
additionalConstraints
,
required
TickerProvider
vsync
,
required
bool
hasFocus
,
required
bool
hovering
,
})
:
super
(
value:
value
,
activeColor:
activeColor
,
inactiveColor:
inactiveColor
,
focusColor:
focusColor
,
hoverColor:
hoverColor
,
reactionColor:
reactionColor
,
inactiveReactionColor:
inactiveReactionColor
,
splashRadius:
splashRadius
,
onChanged:
onChanged
,
tristate:
tristate
,
additionalConstraints:
additionalConstraints
,
vsync:
vsync
,
hasFocus:
hasFocus
,
hovering:
hovering
,
);
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
final
Canvas
canvas
=
context
.
canvas
;
paintRadialReaction
(
canvas
,
offset
,
size
.
center
(
Offset
.
zero
));
final
Offset
center
=
(
offset
&
size
).
center
;
// Outer circle
// Outer circle
final
Paint
paint
=
Paint
()
final
Paint
paint
=
Paint
()
...
@@ -661,12 +530,4 @@ class _RenderRadio extends RenderToggleable {
...
@@ -661,12 +530,4 @@ class _RenderRadio extends RenderToggleable {
canvas
.
drawCircle
(
center
,
_kInnerRadius
*
position
.
value
,
paint
);
canvas
.
drawCircle
(
center
,
_kInnerRadius
*
position
.
value
,
paint
);
}
}
}
}
@override
void
describeSemanticsConfiguration
(
SemanticsConfiguration
config
)
{
super
.
describeSemanticsConfiguration
(
config
);
config
..
isInMutuallyExclusiveGroup
=
true
..
isChecked
=
value
==
true
;
}
}
}
packages/flutter/lib/src/material/switch.dart
View file @
14552a96
...
@@ -51,7 +51,7 @@ enum _SwitchType { material, adaptive }
...
@@ -51,7 +51,7 @@ enum _SwitchType { material, adaptive }
/// * [Radio], for selecting among a set of explicit values.
/// * [Radio], for selecting among a set of explicit values.
/// * [Slider], for selecting a value in a range.
/// * [Slider], for selecting a value in a range.
/// * <https://material.io/design/components/selection-controls.html#switches>
/// * <https://material.io/design/components/selection-controls.html#switches>
class
Switch
extends
State
ful
Widget
{
class
Switch
extends
State
less
Widget
{
/// Creates a material design switch.
/// Creates a material design switch.
///
///
/// The switch itself does not maintain any state. Instead, when the state of
/// The switch itself does not maintain any state. Instead, when the state of
...
@@ -359,8 +359,87 @@ class Switch extends StatefulWidget {
...
@@ -359,8 +359,87 @@ class Switch extends StatefulWidget {
/// {@macro flutter.widgets.Focus.autofocus}
/// {@macro flutter.widgets.Focus.autofocus}
final
bool
autofocus
;
final
bool
autofocus
;
Size
_getSwitchSize
(
ThemeData
theme
)
{
final
MaterialTapTargetSize
effectiveMaterialTapTargetSize
=
materialTapTargetSize
??
theme
.
switchTheme
.
materialTapTargetSize
??
theme
.
materialTapTargetSize
;
switch
(
effectiveMaterialTapTargetSize
)
{
case
MaterialTapTargetSize
.
padded
:
return
const
Size
(
_kSwitchWidth
,
_kSwitchHeight
);
case
MaterialTapTargetSize
.
shrinkWrap
:
return
const
Size
(
_kSwitchWidth
,
_kSwitchHeightCollapsed
);
}
}
Widget
_buildCupertinoSwitch
(
BuildContext
context
)
{
final
Size
size
=
_getSwitchSize
(
Theme
.
of
(
context
));
return
Focus
(
focusNode:
focusNode
,
autofocus:
autofocus
,
child:
Container
(
width:
size
.
width
,
// Same size as the Material switch.
height:
size
.
height
,
alignment:
Alignment
.
center
,
child:
CupertinoSwitch
(
dragStartBehavior:
dragStartBehavior
,
value:
value
,
onChanged:
onChanged
,
activeColor:
activeColor
,
trackColor:
inactiveTrackColor
),
),
);
}
Widget
_buildMaterialSwitch
(
BuildContext
context
)
{
return
_MaterialSwitch
(
value:
value
,
onChanged:
onChanged
,
size:
_getSwitchSize
(
Theme
.
of
(
context
)),
activeColor:
activeColor
,
activeTrackColor:
activeTrackColor
,
inactiveThumbColor:
inactiveThumbColor
,
inactiveTrackColor:
inactiveTrackColor
,
activeThumbImage:
activeThumbImage
,
onActiveThumbImageError:
onActiveThumbImageError
,
inactiveThumbImage:
inactiveThumbImage
,
onInactiveThumbImageError:
onInactiveThumbImageError
,
thumbColor:
thumbColor
,
trackColor:
trackColor
,
materialTapTargetSize:
materialTapTargetSize
,
dragStartBehavior:
dragStartBehavior
,
mouseCursor:
mouseCursor
,
focusColor:
focusColor
,
hoverColor:
hoverColor
,
overlayColor:
overlayColor
,
splashRadius:
splashRadius
,
focusNode:
focusNode
,
autofocus:
autofocus
,
);
}
@override
@override
_SwitchState
createState
()
=>
_SwitchState
();
Widget
build
(
BuildContext
context
)
{
switch
(
_switchType
)
{
case
_SwitchType
.
material
:
return
_buildMaterialSwitch
(
context
);
case
_SwitchType
.
adaptive
:
{
final
ThemeData
theme
=
Theme
.
of
(
context
);
assert
(
theme
.
platform
!=
null
);
switch
(
theme
.
platform
)
{
case
TargetPlatform
.
android
:
case
TargetPlatform
.
fuchsia
:
case
TargetPlatform
.
linux
:
case
TargetPlatform
.
windows
:
return
_buildMaterialSwitch
(
context
);
case
TargetPlatform
.
iOS
:
case
TargetPlatform
.
macOS
:
return
_buildCupertinoSwitch
(
context
);
}
}
}
}
@override
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
...
@@ -370,65 +449,95 @@ class Switch extends StatefulWidget {
...
@@ -370,65 +449,95 @@ class Switch extends StatefulWidget {
}
}
}
}
class
_SwitchState
extends
State
<
Switch
>
with
TickerProviderStateMixin
{
class
_MaterialSwitch
extends
StatefulWidget
{
late
Map
<
Type
,
Action
<
Intent
>>
_actionMap
;
const
_MaterialSwitch
({
Key
?
key
,
required
this
.
value
,
required
this
.
onChanged
,
required
this
.
size
,
this
.
activeColor
,
this
.
activeTrackColor
,
this
.
inactiveThumbColor
,
this
.
inactiveTrackColor
,
this
.
activeThumbImage
,
this
.
onActiveThumbImageError
,
this
.
inactiveThumbImage
,
this
.
onInactiveThumbImageError
,
this
.
thumbColor
,
this
.
trackColor
,
this
.
materialTapTargetSize
,
this
.
dragStartBehavior
=
DragStartBehavior
.
start
,
this
.
mouseCursor
,
this
.
focusColor
,
this
.
hoverColor
,
this
.
overlayColor
,
this
.
splashRadius
,
this
.
focusNode
,
this
.
autofocus
=
false
,
})
:
assert
(
dragStartBehavior
!=
null
),
assert
(
activeThumbImage
!=
null
||
onActiveThumbImageError
==
null
),
assert
(
inactiveThumbImage
!=
null
||
onInactiveThumbImageError
==
null
),
super
(
key:
key
);
@override
final
bool
value
;
void
initState
()
{
final
ValueChanged
<
bool
>?
onChanged
;
super
.
initState
();
final
Color
?
activeColor
;
_actionMap
=
<
Type
,
Action
<
Intent
>>{
final
Color
?
activeTrackColor
;
ActivateIntent:
CallbackAction
<
ActivateIntent
>(
onInvoke:
_actionHandler
),
final
Color
?
inactiveThumbColor
;
};
final
Color
?
inactiveTrackColor
;
}
final
ImageProvider
?
activeThumbImage
;
final
ImageErrorListener
?
onActiveThumbImageError
;
final
ImageProvider
?
inactiveThumbImage
;
final
ImageErrorListener
?
onInactiveThumbImageError
;
final
MaterialStateProperty
<
Color
?>?
thumbColor
;
final
MaterialStateProperty
<
Color
?>?
trackColor
;
final
MaterialTapTargetSize
?
materialTapTargetSize
;
final
DragStartBehavior
dragStartBehavior
;
final
MouseCursor
?
mouseCursor
;
final
Color
?
focusColor
;
final
Color
?
hoverColor
;
final
MaterialStateProperty
<
Color
?>?
overlayColor
;
final
double
?
splashRadius
;
final
FocusNode
?
focusNode
;
final
bool
autofocus
;
final
Size
size
;
void
_actionHandler
(
ActivateIntent
intent
)
{
@override
if
(
widget
.
onChanged
!=
null
)
{
State
<
StatefulWidget
>
createState
()
=>
_MaterialSwitchState
();
widget
.
onChanged
!(!
widget
.
value
);
}
}
final
RenderObject
renderObject
=
context
.
findRenderObject
()!;
renderObject
.
sendSemanticsEvent
(
const
TapSemanticEvent
());
}
bool
_focused
=
false
;
class
_MaterialSwitchState
extends
State
<
_MaterialSwitch
>
with
TickerProviderStateMixin
,
ToggleableStateMixin
{
void
_handleFocusHighlightChanged
(
bool
focused
)
{
final
_SwitchPainter
_painter
=
_SwitchPainter
();
if
(
focused
!=
_focused
)
{
setState
(()
{
_focused
=
focused
;
});
}
}
bool
_hovering
=
false
;
@override
void
_handleHoverChanged
(
bool
hovering
)
{
void
didUpdateWidget
(
_MaterialSwitch
oldWidget
)
{
if
(
hovering
!=
_hovering
)
{
super
.
didUpdateWidget
(
oldWidget
);
setState
(()
{
_hovering
=
hovering
;
});
if
(
oldWidget
.
value
!=
widget
.
value
)
{
// During a drag we may have modified the curve, reset it if its possible
// to do without visual discontinuation.
if
(
position
.
value
==
0.0
||
position
.
value
==
1.0
)
{
position
..
curve
=
Curves
.
easeIn
..
reverseCurve
=
Curves
.
easeOut
;
}
animateToValue
();
}
}
}
}
Size
getSwitchSize
(
ThemeData
theme
)
{
@override
final
MaterialTapTargetSize
effectiveMaterialTapTargetSize
=
widget
.
materialTapTargetSize
void
dispose
()
{
??
theme
.
switchTheme
.
materialTapTargetSize
_painter
.
dispose
();
??
theme
.
materialTapTargetSize
;
super
.
dispose
();
switch
(
effectiveMaterialTapTargetSize
)
{
case
MaterialTapTargetSize
.
padded
:
return
const
Size
(
_kSwitchWidth
,
_kSwitchHeight
);
case
MaterialTapTargetSize
.
shrinkWrap
:
return
const
Size
(
_kSwitchWidth
,
_kSwitchHeightCollapsed
);
}
}
}
bool
get
enabled
=>
widget
.
onChanged
!=
null
;
@override
ValueChanged
<
bool
?>?
get
onChanged
=>
widget
.
onChanged
!=
null
?
_handleChanged
:
null
;
void
_didFinishDragging
()
{
@override
// The user has finished dragging the thumb of this switch. Rebuild the switch
bool
get
tristate
=>
false
;
// to update the animation.
setState
(()
{});
}
Set
<
MaterialState
>
get
_states
=>
<
MaterialState
>{
@override
if
(!
enabled
)
MaterialState
.
disabled
,
bool
?
get
value
=>
widget
.
value
;
if
(
_hovering
)
MaterialState
.
hovered
,
if
(
_focused
)
MaterialState
.
focused
,
if
(
widget
.
value
)
MaterialState
.
selected
,
};
MaterialStateProperty
<
Color
?>
get
_widgetThumbColor
{
MaterialStateProperty
<
Color
?>
get
_widgetThumbColor
{
return
MaterialStateProperty
.
resolveWith
((
Set
<
MaterialState
>
states
)
{
return
MaterialStateProperty
.
resolveWith
((
Set
<
MaterialState
>
states
)
{
...
@@ -487,14 +596,68 @@ class _SwitchState extends State<Switch> with TickerProviderStateMixin {
...
@@ -487,14 +596,68 @@ class _SwitchState extends State<Switch> with TickerProviderStateMixin {
});
});
}
}
Widget
buildMaterialSwitch
(
BuildContext
context
)
{
double
get
_trackInnerLength
=>
widget
.
size
.
width
-
_kSwitchMinSize
;
void
_handleDragStart
(
DragStartDetails
details
)
{
if
(
isInteractive
)
reactionController
.
forward
();
}
void
_handleDragUpdate
(
DragUpdateDetails
details
)
{
if
(
isInteractive
)
{
position
..
curve
=
Curves
.
linear
..
reverseCurve
=
null
;
final
double
delta
=
details
.
primaryDelta
!
/
_trackInnerLength
;
switch
(
Directionality
.
of
(
context
))
{
case
TextDirection
.
rtl
:
positionController
.
value
-=
delta
;
break
;
case
TextDirection
.
ltr
:
positionController
.
value
+=
delta
;
break
;
}
}
}
bool
_needsPositionAnimation
=
false
;
void
_handleDragEnd
(
DragEndDetails
details
)
{
if
(
position
.
value
>=
0.5
!=
widget
.
value
)
{
widget
.
onChanged
!(!
widget
.
value
);
// Wait with finishing the animation until widget.value has changed to
// !widget.value as part of the widget.onChanged call above.
setState
(()
{
_needsPositionAnimation
=
true
;
});
}
else
{
animateToValue
();
}
reactionController
.
reverse
();
}
void
_handleChanged
(
bool
?
value
)
{
assert
(
value
!=
null
);
assert
(
widget
.
onChanged
!=
null
);
widget
.
onChanged
!(
value
!);
}
@override
Widget
build
(
BuildContext
context
)
{
assert
(
debugCheckHasMaterial
(
context
));
assert
(
debugCheckHasMaterial
(
context
));
if
(
_needsPositionAnimation
)
{
_needsPositionAnimation
=
false
;
animateToValue
();
}
final
ThemeData
theme
=
Theme
.
of
(
context
);
final
ThemeData
theme
=
Theme
.
of
(
context
);
// Colors need to be resolved in selected and non selected states separately
// Colors need to be resolved in selected and non selected states separately
// so that they can be lerped between.
// so that they can be lerped between.
final
Set
<
MaterialState
>
activeStates
=
_
states
..
add
(
MaterialState
.
selected
);
final
Set
<
MaterialState
>
activeStates
=
states
..
add
(
MaterialState
.
selected
);
final
Set
<
MaterialState
>
inactiveStates
=
_
states
..
remove
(
MaterialState
.
selected
);
final
Set
<
MaterialState
>
inactiveStates
=
states
..
remove
(
MaterialState
.
selected
);
final
Color
effectiveActiveThumbColor
=
widget
.
thumbColor
?.
resolve
(
activeStates
)
final
Color
effectiveActiveThumbColor
=
widget
.
thumbColor
?.
resolve
(
activeStates
)
??
_widgetThumbColor
.
resolve
(
activeStates
)
??
_widgetThumbColor
.
resolve
(
activeStates
)
??
theme
.
switchTheme
.
thumbColor
?.
resolve
(
activeStates
)
??
theme
.
switchTheme
.
thumbColor
?.
resolve
(
activeStates
)
...
@@ -512,14 +675,13 @@ class _SwitchState extends State<Switch> with TickerProviderStateMixin {
...
@@ -512,14 +675,13 @@ class _SwitchState extends State<Switch> with TickerProviderStateMixin {
??
theme
.
switchTheme
.
trackColor
?.
resolve
(
inactiveStates
)
??
theme
.
switchTheme
.
trackColor
?.
resolve
(
inactiveStates
)
??
_defaultTrackColor
.
resolve
(
inactiveStates
);
??
_defaultTrackColor
.
resolve
(
inactiveStates
);
final
Set
<
MaterialState
>
focusedStates
=
states
..
add
(
MaterialState
.
focused
);
final
Set
<
MaterialState
>
focusedStates
=
_states
..
add
(
MaterialState
.
focused
);
final
Color
effectiveFocusOverlayColor
=
widget
.
overlayColor
?.
resolve
(
focusedStates
)
final
Color
effectiveFocusOverlayColor
=
widget
.
overlayColor
?.
resolve
(
focusedStates
)
??
widget
.
focusColor
??
widget
.
focusColor
??
theme
.
switchTheme
.
overlayColor
?.
resolve
(
focusedStates
)
??
theme
.
switchTheme
.
overlayColor
?.
resolve
(
focusedStates
)
??
theme
.
focusColor
;
??
theme
.
focusColor
;
final
Set
<
MaterialState
>
hoveredStates
=
_
states
..
add
(
MaterialState
.
hovered
);
final
Set
<
MaterialState
>
hoveredStates
=
states
..
add
(
MaterialState
.
hovered
);
final
Color
effectiveHoverOverlayColor
=
widget
.
overlayColor
?.
resolve
(
hoveredStates
)
final
Color
effectiveHoverOverlayColor
=
widget
.
overlayColor
?.
resolve
(
hoveredStates
)
??
widget
.
hoverColor
??
widget
.
hoverColor
??
theme
.
switchTheme
.
overlayColor
?.
resolve
(
hoveredStates
)
??
theme
.
switchTheme
.
overlayColor
?.
resolve
(
hoveredStates
)
...
@@ -535,277 +697,65 @@ class _SwitchState extends State<Switch> with TickerProviderStateMixin {
...
@@ -535,277 +697,65 @@ class _SwitchState extends State<Switch> with TickerProviderStateMixin {
??
theme
.
switchTheme
.
overlayColor
?.
resolve
(
inactivePressedStates
)
??
theme
.
switchTheme
.
overlayColor
?.
resolve
(
inactivePressedStates
)
??
effectiveActiveThumbColor
.
withAlpha
(
kRadialReactionAlpha
);
??
effectiveActiveThumbColor
.
withAlpha
(
kRadialReactionAlpha
);
final
MouseCursor
effectiveMouseCursor
=
MaterialStateProperty
.
resolveAs
<
MouseCursor
?>(
widget
.
mouseCursor
,
_states
)
final
MaterialStateProperty
<
MouseCursor
>
effectiveMouseCursor
=
MaterialStateProperty
.
resolveWith
<
MouseCursor
>((
Set
<
MaterialState
>
states
)
{
??
theme
.
switchTheme
.
mouseCursor
?.
resolve
(
_states
)
return
MaterialStateProperty
.
resolveAs
<
MouseCursor
?>(
widget
.
mouseCursor
,
states
)
??
MaterialStateProperty
.
resolveAs
<
MouseCursor
>(
MaterialStateMouseCursor
.
clickable
,
_states
);
??
theme
.
switchTheme
.
mouseCursor
?.
resolve
(
states
)
??
MaterialStateProperty
.
resolveAs
<
MouseCursor
>(
MaterialStateMouseCursor
.
clickable
,
states
);
return
FocusableActionDetector
(
});
actions:
_actionMap
,
focusNode:
widget
.
focusNode
,
autofocus:
widget
.
autofocus
,
enabled:
enabled
,
onShowFocusHighlight:
_handleFocusHighlightChanged
,
onShowHoverHighlight:
_handleHoverChanged
,
mouseCursor:
effectiveMouseCursor
,
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
_SwitchRenderObjectWidget
(
dragStartBehavior:
widget
.
dragStartBehavior
,
value:
widget
.
value
,
activeColor:
effectiveActiveThumbColor
,
inactiveColor:
effectiveInactiveThumbColor
,
surfaceColor:
theme
.
colorScheme
.
surface
,
focusColor:
effectiveFocusOverlayColor
,
hoverColor:
effectiveHoverOverlayColor
,
reactionColor:
effectiveActivePressedOverlayColor
,
inactiveReactionColor:
effectiveInactivePressedOverlayColor
,
splashRadius:
widget
.
splashRadius
??
theme
.
switchTheme
.
splashRadius
??
kRadialReactionRadius
,
activeThumbImage:
widget
.
activeThumbImage
,
onActiveThumbImageError:
widget
.
onActiveThumbImageError
,
inactiveThumbImage:
widget
.
inactiveThumbImage
,
onInactiveThumbImageError:
widget
.
onInactiveThumbImageError
,
activeTrackColor:
effectiveActiveTrackColor
,
inactiveTrackColor:
effectiveInactiveTrackColor
,
configuration:
createLocalImageConfiguration
(
context
),
onChanged:
widget
.
onChanged
,
additionalConstraints:
BoxConstraints
.
tight
(
getSwitchSize
(
theme
)),
hasFocus:
_focused
,
hovering:
_hovering
,
state:
this
,
);
},
),
);
}
Widget
buildCupertinoSwitch
(
BuildContext
context
)
{
return
Semantics
(
final
Size
size
=
getSwitchSize
(
Theme
.
of
(
context
));
toggled:
widget
.
value
,
return
Focus
(
child:
GestureDetector
(
focusNode:
widget
.
focusNode
,
excludeFromSemantics:
true
,
autofocus:
widget
.
autofocus
,
onHorizontalDragStart:
_handleDragStart
,
child:
Container
(
onHorizontalDragUpdate:
_handleDragUpdate
,
width:
size
.
width
,
// Same size as the Material switch.
onHorizontalDragEnd:
_handleDragEnd
,
height:
size
.
height
,
dragStartBehavior:
widget
.
dragStartBehavior
,
alignment:
Alignment
.
center
,
child:
buildToggleable
(
child:
CupertinoSwitch
(
mouseCursor:
effectiveMouseCursor
,
dragStartBehavior:
widget
.
dragStartBehavior
,
focusNode:
widget
.
focusNode
,
value:
widget
.
value
,
autofocus:
widget
.
autofocus
,
onChanged:
widget
.
onChanged
,
size:
widget
.
size
,
activeColor:
widget
.
activeColor
,
painter:
_painter
trackColor:
widget
.
inactiveTrackColor
..
position
=
position
..
reaction
=
reaction
..
reactionFocusFade
=
reactionFocusFade
..
reactionHoverFade
=
reactionHoverFade
..
inactiveReactionColor
=
effectiveInactivePressedOverlayColor
..
reactionColor
=
effectiveActivePressedOverlayColor
..
hoverColor
=
effectiveHoverOverlayColor
..
focusColor
=
effectiveFocusOverlayColor
..
splashRadius
=
widget
.
splashRadius
??
theme
.
switchTheme
.
splashRadius
??
kRadialReactionRadius
..
downPosition
=
downPosition
..
isFocused
=
states
.
contains
(
MaterialState
.
focused
)
..
isHovered
=
states
.
contains
(
MaterialState
.
hovered
)
..
activeColor
=
effectiveActiveThumbColor
..
inactiveColor
=
effectiveInactiveThumbColor
..
activeThumbImage
=
widget
.
activeThumbImage
..
onActiveThumbImageError
=
widget
.
onActiveThumbImageError
..
inactiveThumbImage
=
widget
.
inactiveThumbImage
..
onInactiveThumbImageError
=
widget
.
onInactiveThumbImageError
..
activeTrackColor
=
effectiveActiveTrackColor
..
inactiveTrackColor
=
effectiveInactiveTrackColor
..
configuration
=
createLocalImageConfiguration
(
context
)
..
isInteractive
=
isInteractive
..
trackInnerLength
=
_trackInnerLength
..
textDirection
=
Directionality
.
of
(
context
)
..
surfaceColor
=
theme
.
colorScheme
.
surface
),
),
),
),
);
);
}
}
@override
Widget
build
(
BuildContext
context
)
{
switch
(
widget
.
_switchType
)
{
case
_SwitchType
.
material
:
return
buildMaterialSwitch
(
context
);
case
_SwitchType
.
adaptive
:
{
final
ThemeData
theme
=
Theme
.
of
(
context
);
assert
(
theme
.
platform
!=
null
);
switch
(
theme
.
platform
)
{
case
TargetPlatform
.
android
:
case
TargetPlatform
.
fuchsia
:
case
TargetPlatform
.
linux
:
case
TargetPlatform
.
windows
:
return
buildMaterialSwitch
(
context
);
case
TargetPlatform
.
iOS
:
case
TargetPlatform
.
macOS
:
return
buildCupertinoSwitch
(
context
);
}
}
}
}
}
class
_SwitchRenderObjectWidget
extends
LeafRenderObjectWidget
{
const
_SwitchRenderObjectWidget
({
Key
?
key
,
required
this
.
value
,
required
this
.
activeColor
,
required
this
.
inactiveColor
,
required
this
.
hoverColor
,
required
this
.
focusColor
,
required
this
.
reactionColor
,
required
this
.
inactiveReactionColor
,
required
this
.
splashRadius
,
required
this
.
activeThumbImage
,
required
this
.
onActiveThumbImageError
,
required
this
.
inactiveThumbImage
,
required
this
.
onInactiveThumbImageError
,
required
this
.
activeTrackColor
,
required
this
.
inactiveTrackColor
,
required
this
.
configuration
,
required
this
.
onChanged
,
required
this
.
additionalConstraints
,
required
this
.
dragStartBehavior
,
required
this
.
hasFocus
,
required
this
.
hovering
,
required
this
.
state
,
required
this
.
surfaceColor
,
})
:
super
(
key:
key
);
final
bool
value
;
final
Color
activeColor
;
final
Color
inactiveColor
;
final
Color
hoverColor
;
final
Color
focusColor
;
final
Color
reactionColor
;
final
Color
inactiveReactionColor
;
final
double
splashRadius
;
final
ImageProvider
?
activeThumbImage
;
final
ImageErrorListener
?
onActiveThumbImageError
;
final
ImageProvider
?
inactiveThumbImage
;
final
ImageErrorListener
?
onInactiveThumbImageError
;
final
Color
activeTrackColor
;
final
Color
inactiveTrackColor
;
final
ImageConfiguration
configuration
;
final
ValueChanged
<
bool
>?
onChanged
;
final
BoxConstraints
additionalConstraints
;
final
DragStartBehavior
dragStartBehavior
;
final
bool
hasFocus
;
final
bool
hovering
;
final
_SwitchState
state
;
final
Color
surfaceColor
;
@override
_RenderSwitch
createRenderObject
(
BuildContext
context
)
{
return
_RenderSwitch
(
dragStartBehavior:
dragStartBehavior
,
value:
value
,
activeColor:
activeColor
,
inactiveColor:
inactiveColor
,
hoverColor:
hoverColor
,
focusColor:
focusColor
,
reactionColor:
reactionColor
,
inactiveReactionColor:
inactiveReactionColor
,
splashRadius:
splashRadius
,
activeThumbImage:
activeThumbImage
,
onActiveThumbImageError:
onActiveThumbImageError
,
inactiveThumbImage:
inactiveThumbImage
,
onInactiveThumbImageError:
onInactiveThumbImageError
,
activeTrackColor:
activeTrackColor
,
inactiveTrackColor:
inactiveTrackColor
,
configuration:
configuration
,
onChanged:
onChanged
!=
null
?
_handleValueChanged
:
null
,
textDirection:
Directionality
.
of
(
context
),
additionalConstraints:
additionalConstraints
,
hasFocus:
hasFocus
,
hovering:
hovering
,
state:
state
,
surfaceColor:
surfaceColor
,
);
}
@override
void
updateRenderObject
(
BuildContext
context
,
_RenderSwitch
renderObject
)
{
renderObject
..
value
=
value
..
activeColor
=
activeColor
..
inactiveColor
=
inactiveColor
..
hoverColor
=
hoverColor
..
focusColor
=
focusColor
..
reactionColor
=
reactionColor
..
inactiveReactionColor
=
inactiveReactionColor
..
splashRadius
=
splashRadius
..
activeThumbImage
=
activeThumbImage
..
onActiveThumbImageError
=
onActiveThumbImageError
..
inactiveThumbImage
=
inactiveThumbImage
..
onInactiveThumbImageError
=
onInactiveThumbImageError
..
activeTrackColor
=
activeTrackColor
..
inactiveTrackColor
=
inactiveTrackColor
..
configuration
=
configuration
..
onChanged
=
onChanged
!=
null
?
_handleValueChanged
:
null
..
textDirection
=
Directionality
.
of
(
context
)
..
additionalConstraints
=
additionalConstraints
..
dragStartBehavior
=
dragStartBehavior
..
hasFocus
=
hasFocus
..
hovering
=
hovering
..
vsync
=
state
..
surfaceColor
=
surfaceColor
;
}
void
_handleValueChanged
(
bool
?
value
)
{
// Wrap the onChanged callback because the RenderToggleable supports tri-state
// values (i.e. value can be null), but the Switch doesn't. We pass false
// for the tristate param to RenderToggleable, so value should never
// be null.
assert
(
value
!=
null
);
if
(
onChanged
!=
null
)
{
onChanged
!(
value
!);
}
}
}
}
class
_RenderSwitch
extends
RenderToggleable
{
class
_SwitchPainter
extends
ToggleablePainter
{
_RenderSwitch
({
required
bool
value
,
required
Color
activeColor
,
required
Color
inactiveColor
,
required
Color
hoverColor
,
required
Color
focusColor
,
required
Color
reactionColor
,
required
Color
inactiveReactionColor
,
required
double
splashRadius
,
required
ImageProvider
?
activeThumbImage
,
required
ImageErrorListener
?
onActiveThumbImageError
,
required
ImageProvider
?
inactiveThumbImage
,
required
ImageErrorListener
?
onInactiveThumbImageError
,
required
Color
activeTrackColor
,
required
Color
inactiveTrackColor
,
required
ImageConfiguration
configuration
,
required
BoxConstraints
additionalConstraints
,
required
TextDirection
textDirection
,
required
ValueChanged
<
bool
?>?
onChanged
,
required
DragStartBehavior
dragStartBehavior
,
required
bool
hasFocus
,
required
bool
hovering
,
required
this
.
state
,
required
Color
surfaceColor
,
})
:
assert
(
textDirection
!=
null
),
_activeThumbImage
=
activeThumbImage
,
_onActiveThumbImageError
=
onActiveThumbImageError
,
_inactiveThumbImage
=
inactiveThumbImage
,
_onInactiveThumbImageError
=
onInactiveThumbImageError
,
_activeTrackColor
=
activeTrackColor
,
_inactiveTrackColor
=
inactiveTrackColor
,
_configuration
=
configuration
,
_textDirection
=
textDirection
,
_surfaceColor
=
surfaceColor
,
super
(
value:
value
,
tristate:
false
,
activeColor:
activeColor
,
inactiveColor:
inactiveColor
,
hoverColor:
hoverColor
,
focusColor:
focusColor
,
reactionColor:
reactionColor
,
inactiveReactionColor:
inactiveReactionColor
,
splashRadius:
splashRadius
,
onChanged:
onChanged
,
additionalConstraints:
additionalConstraints
,
hasFocus:
hasFocus
,
hovering:
hovering
,
vsync:
state
,
)
{
_drag
=
HorizontalDragGestureRecognizer
()
..
onStart
=
_handleDragStart
..
onUpdate
=
_handleDragUpdate
..
onEnd
=
_handleDragEnd
..
dragStartBehavior
=
dragStartBehavior
;
}
ImageProvider
?
get
activeThumbImage
=>
_activeThumbImage
;
ImageProvider
?
get
activeThumbImage
=>
_activeThumbImage
;
ImageProvider
?
_activeThumbImage
;
ImageProvider
?
_activeThumbImage
;
set
activeThumbImage
(
ImageProvider
?
value
)
{
set
activeThumbImage
(
ImageProvider
?
value
)
{
if
(
value
==
_activeThumbImage
)
if
(
value
==
_activeThumbImage
)
return
;
return
;
_activeThumbImage
=
value
;
_activeThumbImage
=
value
;
markNeedsPaint
();
notifyListeners
();
}
}
ImageErrorListener
?
get
onActiveThumbImageError
=>
_onActiveThumbImageError
;
ImageErrorListener
?
get
onActiveThumbImageError
=>
_onActiveThumbImageError
;
...
@@ -815,7 +765,7 @@ class _RenderSwitch extends RenderToggleable {
...
@@ -815,7 +765,7 @@ class _RenderSwitch extends RenderToggleable {
return
;
return
;
}
}
_onActiveThumbImageError
=
value
;
_onActiveThumbImageError
=
value
;
markNeedsPaint
();
notifyListeners
();
}
}
ImageProvider
?
get
inactiveThumbImage
=>
_inactiveThumbImage
;
ImageProvider
?
get
inactiveThumbImage
=>
_inactiveThumbImage
;
...
@@ -824,7 +774,7 @@ class _RenderSwitch extends RenderToggleable {
...
@@ -824,7 +774,7 @@ class _RenderSwitch extends RenderToggleable {
if
(
value
==
_inactiveThumbImage
)
if
(
value
==
_inactiveThumbImage
)
return
;
return
;
_inactiveThumbImage
=
value
;
_inactiveThumbImage
=
value
;
markNeedsPaint
();
notifyListeners
();
}
}
ImageErrorListener
?
get
onInactiveThumbImageError
=>
_onInactiveThumbImageError
;
ImageErrorListener
?
get
onInactiveThumbImageError
=>
_onInactiveThumbImageError
;
...
@@ -834,132 +784,77 @@ class _RenderSwitch extends RenderToggleable {
...
@@ -834,132 +784,77 @@ class _RenderSwitch extends RenderToggleable {
return
;
return
;
}
}
_onInactiveThumbImageError
=
value
;
_onInactiveThumbImageError
=
value
;
markNeedsPaint
();
notifyListeners
();
}
}
Color
get
activeTrackColor
=>
_activeTrackColor
;
Color
get
activeTrackColor
=>
_activeTrackColor
!
;
Color
_activeTrackColor
;
Color
?
_activeTrackColor
;
set
activeTrackColor
(
Color
value
)
{
set
activeTrackColor
(
Color
value
)
{
assert
(
value
!=
null
);
assert
(
value
!=
null
);
if
(
value
==
_activeTrackColor
)
if
(
value
==
_activeTrackColor
)
return
;
return
;
_activeTrackColor
=
value
;
_activeTrackColor
=
value
;
markNeedsPaint
();
notifyListeners
();
}
}
Color
get
inactiveTrackColor
=>
_inactiveTrackColor
;
Color
get
inactiveTrackColor
=>
_inactiveTrackColor
!
;
Color
_inactiveTrackColor
;
Color
?
_inactiveTrackColor
;
set
inactiveTrackColor
(
Color
value
)
{
set
inactiveTrackColor
(
Color
value
)
{
assert
(
value
!=
null
);
assert
(
value
!=
null
);
if
(
value
==
_inactiveTrackColor
)
if
(
value
==
_inactiveTrackColor
)
return
;
return
;
_inactiveTrackColor
=
value
;
_inactiveTrackColor
=
value
;
markNeedsPaint
();
notifyListeners
();
}
}
ImageConfiguration
get
configuration
=>
_configuration
;
ImageConfiguration
get
configuration
=>
_configuration
!
;
ImageConfiguration
_configuration
;
ImageConfiguration
?
_configuration
;
set
configuration
(
ImageConfiguration
value
)
{
set
configuration
(
ImageConfiguration
value
)
{
assert
(
value
!=
null
);
assert
(
value
!=
null
);
if
(
value
==
_configuration
)
if
(
value
==
_configuration
)
return
;
return
;
_configuration
=
value
;
_configuration
=
value
;
markNeedsPaint
();
notifyListeners
();
}
}
TextDirection
get
textDirection
=>
_textDirection
;
TextDirection
get
textDirection
=>
_textDirection
!
;
TextDirection
_textDirection
;
TextDirection
?
_textDirection
;
set
textDirection
(
TextDirection
value
)
{
set
textDirection
(
TextDirection
value
)
{
assert
(
value
!=
null
);
assert
(
value
!=
null
);
if
(
_textDirection
==
value
)
if
(
_textDirection
==
value
)
return
;
return
;
_textDirection
=
value
;
_textDirection
=
value
;
markNeedsPaint
();
notifyListeners
();
}
}
DragStartBehavior
get
dragStartBehavior
=>
_drag
.
dragStartBehavior
;
Color
get
surfaceColor
=>
_surfaceColor
!;
set
dragStartBehavior
(
DragStartBehavior
value
)
{
Color
?
_surfaceColor
;
assert
(
value
!=
null
);
if
(
_drag
.
dragStartBehavior
==
value
)
return
;
_drag
.
dragStartBehavior
=
value
;
}
Color
get
surfaceColor
=>
_surfaceColor
;
Color
_surfaceColor
;
set
surfaceColor
(
Color
value
)
{
set
surfaceColor
(
Color
value
)
{
assert
(
value
!=
null
);
assert
(
value
!=
null
);
if
(
value
==
_surfaceColor
)
if
(
value
==
_surfaceColor
)
return
;
return
;
_surfaceColor
=
value
;
_surfaceColor
=
value
;
markNeedsPaint
();
notifyListeners
();
}
}
_SwitchState
state
;
bool
get
isInteractive
=>
_isInteractive
!;
bool
?
_isInteractive
;
@override
set
isInteractive
(
bool
value
)
{
set
value
(
bool
?
newValue
)
{
if
(
value
==
_isInteractive
)
{
assert
(
value
!=
null
);
return
;
super
.
value
=
newValue
;
// The widget is rebuilt and we have pending position animation to play.
if
(
_needsPositionAnimation
)
{
_needsPositionAnimation
=
false
;
position
.
reverseCurve
=
null
;
if
(
newValue
!)
positionController
.
forward
();
else
positionController
.
reverse
();
}
}
_isInteractive
=
value
;
notifyListeners
();
}
}
@override
double
get
trackInnerLength
=>
_trackInnerLength
!;
void
detach
()
{
double
?
_trackInnerLength
;
_cachedThumbPainter
?.
dispose
();
set
trackInnerLength
(
double
value
)
{
_cachedThumbPainter
=
null
;
if
(
value
==
_trackInnerLength
)
{
super
.
detach
();
return
;
}
double
get
_trackInnerLength
=>
size
.
width
-
_kSwitchMinSize
;
late
HorizontalDragGestureRecognizer
_drag
;
bool
_needsPositionAnimation
=
false
;
void
_handleDragStart
(
DragStartDetails
details
)
{
if
(
isInteractive
)
reactionController
.
forward
();
}
void
_handleDragUpdate
(
DragUpdateDetails
details
)
{
if
(
isInteractive
)
{
position
.
reverseCurve
=
null
;
final
double
delta
=
details
.
primaryDelta
!
/
_trackInnerLength
;
switch
(
textDirection
)
{
case
TextDirection
.
rtl
:
positionController
.
value
-=
delta
;
break
;
case
TextDirection
.
ltr
:
positionController
.
value
+=
delta
;
break
;
}
}
}
}
_trackInnerLength
=
value
;
notifyListeners
();
void
_handleDragEnd
(
DragEndDetails
details
)
{
_needsPositionAnimation
=
true
;
if
(
position
.
value
>=
0.5
!=
value
)
onChanged
!(!
value
!);
reactionController
.
reverse
();
state
.
_didFinishDragging
();
}
@override
void
handleEvent
(
PointerEvent
event
,
BoxHitTestEntry
entry
)
{
assert
(
debugHandleEvent
(
event
,
entry
));
if
(
event
is
PointerDownEvent
&&
onChanged
!=
null
)
_drag
.
addPointer
(
event
);
super
.
handleEvent
(
event
,
entry
);
}
}
Color
?
_cachedThumbColor
;
Color
?
_cachedThumbColor
;
...
@@ -984,19 +879,12 @@ class _RenderSwitch extends RenderToggleable {
...
@@ -984,19 +879,12 @@ class _RenderSwitch extends RenderToggleable {
// are already in the middle of painting. (In fact, doing so would trigger
// are already in the middle of painting. (In fact, doing so would trigger
// an assert).
// an assert).
if
(!
_isPainting
)
if
(!
_isPainting
)
markNeedsPaint
();
notifyListeners
();
}
@override
void
describeSemanticsConfiguration
(
SemanticsConfiguration
config
)
{
super
.
describeSemanticsConfiguration
(
config
);
config
.
isToggled
=
value
==
true
;
}
}
@override
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
void
paint
(
Canvas
canvas
,
Size
size
)
{
final
Canvas
canvas
=
context
.
canvas
;
final
bool
isEnabled
=
isInteractive
;
final
bool
isEnabled
=
onChanged
!=
null
;
final
double
currentValue
=
position
.
value
;
final
double
currentValue
=
position
.
value
;
final
double
visualPosition
;
final
double
visualPosition
;
...
@@ -1029,8 +917,8 @@ class _RenderSwitch extends RenderToggleable {
...
@@ -1029,8 +917,8 @@ class _RenderSwitch extends RenderToggleable {
..
color
=
trackColor
;
..
color
=
trackColor
;
const
double
trackHorizontalPadding
=
kRadialReactionRadius
-
_kTrackRadius
;
const
double
trackHorizontalPadding
=
kRadialReactionRadius
-
_kTrackRadius
;
final
Rect
trackRect
=
Rect
.
fromLTWH
(
final
Rect
trackRect
=
Rect
.
fromLTWH
(
offset
.
dx
+
trackHorizontalPadding
,
trackHorizontalPadding
,
offset
.
dy
+
(
size
.
height
-
_kTrackHeight
)
/
2.0
,
(
size
.
height
-
_kTrackHeight
)
/
2.0
,
size
.
width
-
2.0
*
trackHorizontalPadding
,
size
.
width
-
2.0
*
trackHorizontalPadding
,
_kTrackHeight
,
_kTrackHeight
,
);
);
...
@@ -1038,11 +926,11 @@ class _RenderSwitch extends RenderToggleable {
...
@@ -1038,11 +926,11 @@ class _RenderSwitch extends RenderToggleable {
canvas
.
drawRRect
(
trackRRect
,
paint
);
canvas
.
drawRRect
(
trackRRect
,
paint
);
final
Offset
thumbPosition
=
Offset
(
final
Offset
thumbPosition
=
Offset
(
kRadialReactionRadius
+
visualPosition
*
_
trackInnerLength
,
kRadialReactionRadius
+
visualPosition
*
trackInnerLength
,
size
.
height
/
2.0
,
size
.
height
/
2.0
,
);
);
paintRadialReaction
(
canvas
,
offset
,
thumbPosition
);
paintRadialReaction
(
canvas
:
canvas
,
origin:
thumbPosition
);
try
{
try
{
_isPainting
=
true
;
_isPainting
=
true
;
...
@@ -1059,7 +947,7 @@ class _RenderSwitch extends RenderToggleable {
...
@@ -1059,7 +947,7 @@ class _RenderSwitch extends RenderToggleable {
final
double
radius
=
_kThumbRadius
-
inset
;
final
double
radius
=
_kThumbRadius
-
inset
;
thumbPainter
.
paint
(
thumbPainter
.
paint
(
canvas
,
canvas
,
thumbPosition
+
offset
-
Offset
(
radius
,
radius
),
thumbPosition
-
Offset
(
radius
,
radius
),
configuration
.
copyWith
(
size:
Size
.
fromRadius
(
radius
)),
configuration
.
copyWith
(
size:
Size
.
fromRadius
(
radius
)),
);
);
}
finally
{
}
finally
{
...
...
packages/flutter/lib/src/material/toggleable.dart
View file @
14552a96
...
@@ -6,9 +6,10 @@ import 'package:flutter/animation.dart';
...
@@ -6,9 +6,10 @@ import 'package:flutter/animation.dart';
import
'package:flutter/foundation.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/
scheduler
.dart'
;
import
'package:flutter/
widgets
.dart'
;
import
'constants.dart'
;
import
'constants.dart'
;
import
'material_state.dart'
;
// Duration of the animation that moves the toggle from one state to another.
// Duration of the animation that moves the toggle from one state to another.
const
Duration
_kToggleDuration
=
Duration
(
milliseconds:
200
);
const
Duration
_kToggleDuration
=
Duration
(
milliseconds:
200
);
...
@@ -16,92 +17,21 @@ const Duration _kToggleDuration = Duration(milliseconds: 200);
...
@@ -16,92 +17,21 @@ const Duration _kToggleDuration = Duration(milliseconds: 200);
// Duration of the fade animation for the reaction when focus and hover occur.
// Duration of the fade animation for the reaction when focus and hover occur.
const
Duration
_kReactionFadeDuration
=
Duration
(
milliseconds:
50
);
const
Duration
_kReactionFadeDuration
=
Duration
(
milliseconds:
50
);
/// A base class for material style toggleable controls with toggle animations.
/// A mixin for [StatefulWidget]s that implement material-themed toggleable
/// controls with toggle animations (e.g. [Switch]es, [Checkbox]es, and
/// [Radio]s).
///
///
/// This class handles storing the current value, dispatching ValueChanged on a
/// The mixin implements the logic for toggling the control (e.g. when tapped)
/// tap gesture and driving a changed animation. Subclasses are responsible for
/// and provides a series of animation controllers to transition the control
/// painting.
/// from one state to another. It does not have any opinion about the visual
abstract
class
RenderToggleable
extends
RenderConstrainedBox
{
/// representation of the toggleable widget. The visuals are defined by a
/// Creates a toggleable render object.
/// [CustomPainter] passed to the [buildToggleable]. [State] objects using this
///
/// mixin should call that method from their [build] method.
/// The [activeColor], and [inactiveColor] arguments must not be
///
/// null. The [value] can only be null if tristate is true.
/// This mixin is used to implement the material components for [Switch],
RenderToggleable
({
/// [Checkbox], and [Radio] controls.
required
bool
?
value
,
@optionalTypeArgs
bool
tristate
=
false
,
mixin
ToggleableStateMixin
<
S
extends
StatefulWidget
>
on
TickerProviderStateMixin
<
S
>
{
required
Color
activeColor
,
required
Color
inactiveColor
,
Color
?
hoverColor
,
Color
?
focusColor
,
Color
?
reactionColor
,
Color
?
inactiveReactionColor
,
required
double
splashRadius
,
ValueChanged
<
bool
?>?
onChanged
,
required
BoxConstraints
additionalConstraints
,
required
TickerProvider
vsync
,
bool
hasFocus
=
false
,
bool
hovering
=
false
,
})
:
assert
(
tristate
!=
null
),
assert
(
tristate
||
value
!=
null
),
assert
(
activeColor
!=
null
),
assert
(
inactiveColor
!=
null
),
assert
(
vsync
!=
null
),
_value
=
value
,
_tristate
=
tristate
,
_activeColor
=
activeColor
,
_inactiveColor
=
inactiveColor
,
_hoverColor
=
hoverColor
??
activeColor
.
withAlpha
(
kRadialReactionAlpha
),
_focusColor
=
focusColor
??
activeColor
.
withAlpha
(
kRadialReactionAlpha
),
_reactionColor
=
reactionColor
??
activeColor
.
withAlpha
(
kRadialReactionAlpha
),
_inactiveReactionColor
=
inactiveReactionColor
??
activeColor
.
withAlpha
(
kRadialReactionAlpha
),
_splashRadius
=
splashRadius
,
_onChanged
=
onChanged
,
_hasFocus
=
hasFocus
,
_hovering
=
hovering
,
_vsync
=
vsync
,
super
(
additionalConstraints:
additionalConstraints
)
{
_tap
=
TapGestureRecognizer
()
..
onTapDown
=
_handleTapDown
..
onTap
=
_handleTap
..
onTapUp
=
_handleTapUp
..
onTapCancel
=
_handleTapCancel
;
_positionController
=
AnimationController
(
duration:
_kToggleDuration
,
value:
value
==
false
?
0.0
:
1.0
,
vsync:
vsync
,
);
_position
=
CurvedAnimation
(
parent:
_positionController
,
curve:
Curves
.
linear
,
)..
addListener
(
markNeedsPaint
);
_reactionController
=
AnimationController
(
duration:
kRadialReactionDuration
,
vsync:
vsync
,
);
_reaction
=
CurvedAnimation
(
parent:
_reactionController
,
curve:
Curves
.
fastOutSlowIn
,
)..
addListener
(
markNeedsPaint
);
_reactionHoverFadeController
=
AnimationController
(
duration:
_kReactionFadeDuration
,
value:
hovering
||
hasFocus
?
1.0
:
0.0
,
vsync:
vsync
,
);
_reactionHoverFade
=
CurvedAnimation
(
parent:
_reactionHoverFadeController
,
curve:
Curves
.
fastOutSlowIn
,
)..
addListener
(
markNeedsPaint
);
_reactionFocusFadeController
=
AnimationController
(
duration:
_kReactionFadeDuration
,
value:
hovering
||
hasFocus
?
1.0
:
0.0
,
vsync:
vsync
,
);
_reactionFocusFade
=
CurvedAnimation
(
parent:
_reactionFocusFadeController
,
curve:
Curves
.
fastOutSlowIn
,
)..
addListener
(
markNeedsPaint
);
}
/// Used by subclasses to manipulate the visual value of the control.
/// Used by subclasses to manipulate the visual value of the control.
///
///
/// Some controls respond to user input by updating their visual value. For
/// Some controls respond to user input by updating their visual value. For
...
@@ -109,16 +39,15 @@ abstract class RenderToggleable extends RenderConstrainedBox {
...
@@ -109,16 +39,15 @@ abstract class RenderToggleable extends RenderConstrainedBox {
/// dragged. These controls manipulate this animation controller to update
/// dragged. These controls manipulate this animation controller to update
/// their [position] and eventually trigger an [onChanged] callback when the
/// their [position] and eventually trigger an [onChanged] callback when the
/// animation reaches either 0.0 or 1.0.
/// animation reaches either 0.0 or 1.0.
@protected
AnimationController
get
positionController
=>
_positionController
;
AnimationController
get
positionController
=>
_positionController
;
late
AnimationController
_positionController
;
late
AnimationController
_positionController
;
/// The visual value of the control.
/// The visual value of the control.
///
///
/// When the control is inactive, the [value] is false and this animation has
/// When the control is inactive, the [value] is false and this animation has
/// the value 0.0. When the control is active, the value
either true or tristate
/// the value 0.0. When the control is active, the value
is either true or
///
is true and the value is null. When the control is active the animation
///
tristate is true and the value is null. When the control is active the
/// has a value of 1.0. When the control is changing from inactive
///
animation
has a value of 1.0. When the control is changing from inactive
/// to active (or vice versa), [value] is the target value and this animation
/// to active (or vice versa), [value] is the target value and this animation
/// gradually updates from 0.0 to 1.0 (or vice versa).
/// gradually updates from 0.0 to 1.0 (or vice versa).
CurvedAnimation
get
position
=>
_position
;
CurvedAnimation
get
position
=>
_position
;
...
@@ -129,84 +58,66 @@ abstract class RenderToggleable extends RenderConstrainedBox {
...
@@ -129,84 +58,66 @@ abstract class RenderToggleable extends RenderConstrainedBox {
/// Some controls have a radial ink reaction to user input. This animation
/// Some controls have a radial ink reaction to user input. This animation
/// controller can be used to start or stop these ink reactions.
/// controller can be used to start or stop these ink reactions.
///
///
/// Subclasses should call [paintRadialReaction] to actually paint the radial
/// To paint the actual radial reaction, [ToggleablePainter.paintRadialReaction]
/// reaction.
/// may be used.
@protected
AnimationController
get
reactionController
=>
_reactionController
;
AnimationController
get
reactionController
=>
_reactionController
;
late
AnimationController
_reactionController
;
late
AnimationController
_reactionController
;
late
Animation
<
double
>
_reaction
;
/// Used by subclasses to control the radial reaction's opacity animation for
/// The visual value of the radial reaction animation.
/// [hasFocus] changes.
///
///
/// Some controls have a radial ink reaction to focus. This animation
/// Some controls have a radial ink reaction to user input. This animation
/// controller can be used to start or stop these ink reaction fade-ins and
/// controls the progress of these ink reactions.
/// fade-outs.
///
///
/// Subclasses should call [paintRadialReaction] to actually paint the radial
/// To paint the actual radial reaction, [ToggleablePainter.paintRadialReaction]
/// reaction.
/// may be used.
@protected
Animation
<
double
>
get
reaction
=>
_reaction
;
AnimationController
get
reactionFocusFadeController
=>
_reactionFocusFadeController
;
late
Animation
<
double
>
_reaction
;
late
AnimationController
_reactionFocusFadeController
;
late
Animation
<
double
>
_reactionFocusFade
;
/// Used by subclasses to control the radial reaction's opacity animation for
/// Controls the radial reaction's opacity animation for hover changes.
/// [hovering] changes.
///
///
/// Some controls have a radial ink reaction to pointer hover. This animation
/// Some controls have a radial ink reaction to pointer hover. This animation
/// control
ler can be used to start or stop
these ink reaction fade-ins and
/// control
s
these ink reaction fade-ins and
/// fade-outs.
/// fade-outs.
///
///
/// Subclasses should call [paintRadialReaction] to actually paint the radial
/// To paint the actual radial reaction, [ToggleablePainter.paintRadialReaction]
/// reaction.
/// may be used.
@protected
Animation
<
double
>
get
reactionHoverFade
=>
_reactionHoverFade
;
AnimationController
get
reactionHoverFadeController
=>
_reactionHoverFadeController
;
late
AnimationController
_reactionHoverFadeController
;
late
Animation
<
double
>
_reactionHoverFade
;
late
Animation
<
double
>
_reactionHoverFade
;
late
AnimationController
_reactionHoverFadeController
;
/// True if this toggleable has the input focus.
/// Controls the radial reaction's opacity animation for focus changes.
bool
get
hasFocus
=>
_hasFocus
;
///
bool
_hasFocus
;
/// Some controls have a radial ink reaction to focus. This animation
set
hasFocus
(
bool
value
)
{
/// controls these ink reaction fade-ins and fade-outs.
assert
(
value
!=
null
);
///
if
(
value
==
_hasFocus
)
/// To paint the actual radial reaction, [ToggleablePainter.paintRadialReaction]
return
;
/// may be used.
_hasFocus
=
value
;
Animation
<
double
>
get
reactionFocusFade
=>
_reactionFocusFade
;
if
(
_hasFocus
)
{
late
Animation
<
double
>
_reactionFocusFade
;
_reactionFocusFadeController
.
forward
();
late
AnimationController
_reactionFocusFadeController
;
}
else
{
_reactionFocusFadeController
.
reverse
();
}
markNeedsPaint
();
}
/// True if this toggleable is being hovered over by a pointer.
/// Whether [value] of this control can be changed by user interaction.
bool
get
hovering
=>
_hovering
;
///
bool
_hovering
;
/// The control is considered interactive if the [onChanged] callback is
set
hovering
(
bool
value
)
{
/// non-null. If the callback is null, then the control is disabled, and
assert
(
value
!=
null
);
/// non-interactive. A disabled checkbox, for example, is displayed using a
if
(
value
==
_hovering
)
/// grey color and its value cannot be changed.
return
;
bool
get
isInteractive
=>
onChanged
!=
null
;
_hovering
=
value
;
if
(
_hovering
)
{
_reactionHoverFadeController
.
forward
();
}
else
{
_reactionHoverFadeController
.
reverse
();
}
markNeedsPaint
();
}
/// The [TickerProvider] for the [AnimationController]s that run the animations.
late
final
Map
<
Type
,
Action
<
Intent
>>
_actionMap
=
<
Type
,
Action
<
Intent
>>{
TickerProvider
get
vsync
=>
_vsync
;
ActivateIntent:
CallbackAction
<
ActivateIntent
>(
onInvoke:
_handleTap
),
TickerProvider
_vsync
;
};
set
vsync
(
TickerProvider
value
)
{
assert
(
value
!=
null
);
/// Called when the control changes value.
if
(
value
==
_vsync
)
///
return
;
/// If the control is tapped, [onChanged] is called immediately with the new
_vsync
=
value
;
/// value.
positionController
.
resync
(
vsync
);
///
reactionController
.
resync
(
vsync
);
/// The control is considered interactive (see [isInteractive]) if this
}
/// callback is non-null. If the callback is null, then the control is
/// disabled, and non-interactive. A disabled checkbox, for example, is
/// displayed using a grey color and its value cannot be changed.
ValueChanged
<
bool
?>?
get
onChanged
;
/// False if this control is "inactive" (not checked, off, or unselected).
/// False if this control is "inactive" (not checked, off, or unselected).
///
///
...
@@ -217,17 +128,62 @@ abstract class RenderToggleable extends RenderConstrainedBox {
...
@@ -217,17 +128,62 @@ abstract class RenderToggleable extends RenderConstrainedBox {
/// When the value changes, this object starts the [positionController] and
/// When the value changes, this object starts the [positionController] and
/// [position] animations to animate the visual appearance of the control to
/// [position] animations to animate the visual appearance of the control to
/// the new value.
/// the new value.
bool
?
get
value
=>
_value
;
bool
?
get
value
;
bool
?
_value
;
set
value
(
bool
?
value
)
{
/// If true, [value] can be true, false, or null, otherwise [value] must
assert
(
tristate
||
value
!=
null
);
/// be true or false.
if
(
value
==
_value
)
///
return
;
/// When [tristate] is true and [value] is null, then the control is
_value
=
value
;
/// considered to be in its third or "indeterminate" state.
markNeedsSemanticsUpdate
();
bool
get
tristate
;
_position
..
curve
=
Curves
.
easeIn
@override
..
reverseCurve
=
Curves
.
easeOut
;
void
initState
()
{
super
.
initState
();
_positionController
=
AnimationController
(
duration:
_kToggleDuration
,
value:
value
==
false
?
0.0
:
1.0
,
vsync:
this
,
);
_position
=
CurvedAnimation
(
parent:
_positionController
,
curve:
Curves
.
easeIn
,
reverseCurve:
Curves
.
easeOut
,
);
_reactionController
=
AnimationController
(
duration:
kRadialReactionDuration
,
vsync:
this
,
);
_reaction
=
CurvedAnimation
(
parent:
_reactionController
,
curve:
Curves
.
fastOutSlowIn
,
);
_reactionHoverFadeController
=
AnimationController
(
duration:
_kReactionFadeDuration
,
value:
_hovering
||
_focused
?
1.0
:
0.0
,
vsync:
this
,
);
_reactionHoverFade
=
CurvedAnimation
(
parent:
_reactionHoverFadeController
,
curve:
Curves
.
fastOutSlowIn
,
);
_reactionFocusFadeController
=
AnimationController
(
duration:
_kReactionFadeDuration
,
value:
_hovering
||
_focused
?
1.0
:
0.0
,
vsync:
this
,
);
_reactionFocusFade
=
CurvedAnimation
(
parent:
_reactionFocusFadeController
,
curve:
Curves
.
fastOutSlowIn
,
);
}
/// Runs the [position] animation to transition the Toggleable's appearance
/// to match [value].
///
/// This method must be called whenever [value] changes to ensure that the
/// visual representation of the Toggleable matches the current [value].
void
animateToValue
()
{
if
(
tristate
)
{
if
(
tristate
)
{
if
(
value
==
null
)
if
(
value
==
null
)
_positionController
.
value
=
0.0
;
_positionController
.
value
=
0.0
;
...
@@ -243,94 +199,235 @@ abstract class RenderToggleable extends RenderConstrainedBox {
...
@@ -243,94 +199,235 @@ abstract class RenderToggleable extends RenderConstrainedBox {
}
}
}
}
/// If true, [value] can be true, false, or null, otherwise [value] must
@override
/// be true or false.
void
dispose
()
{
_positionController
.
dispose
();
_reactionController
.
dispose
();
_reactionHoverFadeController
.
dispose
();
_reactionFocusFadeController
.
dispose
();
super
.
dispose
();
}
/// The most recent [Offset] at which a pointer touched the Toggleable.
///
///
/// When [tristate] is true and [value] is null, then the control is
/// This is null if currently no pointer is touching the Toggleable or if
/// considered to be in its third or "indeterminate" state.
/// [isInteractive] is false.
bool
get
tristate
=>
_tristate
;
Offset
?
get
downPosition
=>
_downPosition
;
bool
_tristate
;
Offset
?
_downPosition
;
set
tristate
(
bool
value
)
{
assert
(
tristate
!=
null
);
void
_handleTapDown
(
TapDownDetails
details
)
{
if
(
value
==
_tristate
)
if
(
isInteractive
)
{
setState
(()
{
_downPosition
=
details
.
localPosition
;
});
_reactionController
.
forward
();
}
}
void
_handleTap
([
Intent
?
_
])
{
if
(!
isInteractive
)
return
;
return
;
_tristate
=
value
;
switch
(
value
)
{
markNeedsSemanticsUpdate
();
case
false
:
onChanged
!(
true
);
break
;
case
true
:
onChanged
!(
tristate
?
null
:
false
);
break
;
case
null
:
onChanged
!(
false
);
break
;
}
context
.
findRenderObject
()!.
sendSemanticsEvent
(
const
TapSemanticEvent
());
}
void
_handleTapEnd
([
TapUpDetails
?
_
])
{
if
(
_downPosition
!=
null
)
{
setState
(()
{
_downPosition
=
null
;
});
}
_reactionController
.
reverse
();
}
bool
_focused
=
false
;
void
_handleFocusHighlightChanged
(
bool
focused
)
{
if
(
focused
!=
_focused
)
{
setState
(()
{
_focused
=
focused
;
});
if
(
focused
)
{
_reactionFocusFadeController
.
forward
();
}
else
{
_reactionFocusFadeController
.
reverse
();
}
}
}
}
/// The color that should be used in the active state (i.e., when [value] is true).
bool
_hovering
=
false
;
void
_handleHoverChanged
(
bool
hovering
)
{
if
(
hovering
!=
_hovering
)
{
setState
(()
{
_hovering
=
hovering
;
});
if
(
hovering
)
{
_reactionHoverFadeController
.
forward
();
}
else
{
_reactionHoverFadeController
.
reverse
();
}
}
}
/// Describes the current [MaterialState] of the Toggleable.
///
///
/// For example, a checkbox should use this color when checked.
/// The returned set will include:
Color
get
activeColor
=>
_activeColor
;
///
Color
_activeColor
;
/// * [MaterialState.disabled], if [isInteractive] is false
set
activeColor
(
Color
value
)
{
/// * [MaterialState.hovered], if a pointer is hovering over the Toggleable
assert
(
value
!=
null
);
/// * [MaterialState.focused], if the Toggleable has input focus
if
(
value
==
_activeColor
)
/// * [MaterialState.selected], if [value] is true or null
return
;
Set
<
MaterialState
>
get
states
=>
<
MaterialState
>{
_activeColor
=
value
;
if
(!
isInteractive
)
MaterialState
.
disabled
,
markNeedsPaint
();
if
(
_hovering
)
MaterialState
.
hovered
,
if
(
_focused
)
MaterialState
.
focused
,
if
(
value
!=
false
)
MaterialState
.
selected
,
};
/// Typically wraps a `painter` that draws the actual visuals of the
/// Toggleable with logic to toggle it.
///
/// Consider providing a subclass of [ToggleablePainter] as a `painter`, which
/// implements logic to draw a radial ink reaction for this control. The
/// painter is usually configured with the [reaction], [position],
/// [reactionHoverFade], and [reactionFocusFade] animation provided by this
/// mixin. It is expected to draw the visuals of the Toggleable based on the
/// current value of these animations. The animations are triggered by
/// this mixin to transition the Toggleable from one state to another.
///
/// This method must be called from the [build] method of the [State] class
/// that uses this mixin. The returned [Widget] must be returned from the
/// build method - potentially after wrapping it in other widgets.
Widget
buildToggleable
({
FocusNode
?
focusNode
,
bool
autofocus
=
false
,
required
MaterialStateProperty
<
MouseCursor
>
mouseCursor
,
required
Size
size
,
required
CustomPainter
painter
,
})
{
return
FocusableActionDetector
(
actions:
_actionMap
,
focusNode:
focusNode
,
autofocus:
autofocus
,
enabled:
isInteractive
,
onShowFocusHighlight:
_handleFocusHighlightChanged
,
onShowHoverHighlight:
_handleHoverChanged
,
mouseCursor:
mouseCursor
.
resolve
(
states
),
child:
GestureDetector
(
excludeFromSemantics:
!
isInteractive
,
onTapDown:
_handleTapDown
,
onTap:
_handleTap
,
onTapUp:
_handleTapEnd
,
onTapCancel:
_handleTapEnd
,
child:
Semantics
(
enabled:
isInteractive
,
child:
CustomPaint
(
size:
size
,
painter:
painter
,
),
),
),
);
}
}
}
/// The color that should be used in the inactive state (i.e., when [value] is false).
/// A base class for a [CustomPainter] that may be passed to
/// [ToggleableStateMixin.buildToggleable] to draw the visual representation of
/// a Toggleable.
///
/// Subclasses must implement the [paint] method to draw the actual visuals of
/// the Toggleable. In their [paint] method subclasses may call
/// [paintRadialReaction] to draw a radial ink reaction for this control.
abstract
class
ToggleablePainter
extends
ChangeNotifier
implements
CustomPainter
{
/// The visual value of the control.
///
///
/// For example, a checkbox should use this color when unchecked.
/// Usually set to [ToggleableStateMixin.position].
Color
get
inactiveColor
=>
_inactiveColor
;
Animation
<
double
>
get
position
=>
_position
!;
Color
_inactiveColor
;
Animation
<
double
>?
_position
;
set
inactiveColor
(
Color
value
)
{
set
position
(
Animation
<
double
>
value
)
{
assert
(
value
!=
null
);
if
(
value
==
_position
)
{
if
(
value
==
_inactiveColor
)
return
;
return
;
_inactiveColor
=
value
;
}
markNeedsPaint
();
_position
?.
removeListener
(
notifyListeners
);
value
.
addListener
(
notifyListeners
);
_position
=
value
;
notifyListeners
();
}
}
/// The
color that should be used for the reaction when [hovering] is true
.
/// The
visual value of the radial reaction animation
.
///
///
/// Used when the toggleable needs to change the reaction color/transparency,
/// Usually set to [ToggleableStateMixin.reaction].
/// when it is being hovered over.
Animation
<
double
>
get
reaction
=>
_reaction
!;
///
Animation
<
double
>?
_reaction
;
/// Defaults to the [activeColor] at alpha [kRadialReactionAlpha].
set
reaction
(
Animation
<
double
>
value
)
{
Color
get
hoverColor
=>
_hoverColor
;
if
(
value
==
_reaction
)
{
Color
_hoverColor
;
set
hoverColor
(
Color
value
)
{
assert
(
value
!=
null
);
if
(
value
==
_hoverColor
)
return
;
return
;
_hoverColor
=
value
;
}
markNeedsPaint
();
_reaction
?.
removeListener
(
notifyListeners
);
value
.
addListener
(
notifyListeners
);
_reaction
=
value
;
notifyListeners
();
}
}
///
The color that should be used for the reaction when [hasFocus] is true
.
///
Controls the radial reaction's opacity animation for focus changes
.
///
///
/// Used when the toggleable needs to change the reaction color/transparency,
/// Usually set to [ToggleableStateMixin.reactionFocusFade].
/// when it has focus.
Animation
<
double
>
get
reactionFocusFade
=>
_reactionFocusFade
!;
Animation
<
double
>?
_reactionFocusFade
;
set
reactionFocusFade
(
Animation
<
double
>
value
)
{
if
(
value
==
_reactionFocusFade
)
{
return
;
}
_reactionFocusFade
?.
removeListener
(
notifyListeners
);
value
.
addListener
(
notifyListeners
);
_reactionFocusFade
=
value
;
notifyListeners
();
}
/// Controls the radial reaction's opacity animation for hover changes.
///
///
/// Defaults to the [activeColor] at alpha [kRadialReactionAlpha].
/// Usually set to [ToggleableStateMixin.reactionHoverFade].
Color
get
focusColor
=>
_focusColor
;
Animation
<
double
>
get
reactionHoverFade
=>
_reactionHoverFade
!;
Color
_focusColor
;
Animation
<
double
>?
_reactionHoverFade
;
set
focusColor
(
Color
value
)
{
set
reactionHoverFade
(
Animation
<
double
>
value
)
{
assert
(
value
!=
null
);
if
(
value
==
_reactionHoverFade
)
{
if
(
value
==
_focusColor
)
return
;
return
;
_focusColor
=
value
;
}
markNeedsPaint
();
_reactionHoverFade
?.
removeListener
(
notifyListeners
);
value
.
addListener
(
notifyListeners
);
_reactionHoverFade
=
value
;
notifyListeners
();
}
}
/// The color that should be used
for the reaction when the toggleable is
/// The color that should be used
in the active state (i.e., when
///
active
.
///
[ToggleableStateMixin.value] is true)
.
///
///
/// Used when the toggleable needs to change the reaction color/transparency
/// For example, a checkbox should use this color when checked.
/// that is displayed when the toggleable is active and tapped.
Color
get
activeColor
=>
_activeColor
!;
Color
?
_activeColor
;
set
activeColor
(
Color
value
)
{
if
(
_activeColor
==
value
)
{
return
;
}
_activeColor
=
value
;
notifyListeners
();
}
/// The color that should be used in the inactive state (i.e., when
/// [ToggleableStateMixin.value] is false).
///
///
/// Defaults to the [activeColor] at alpha [kRadialReactionAlpha].
/// For example, a checkbox should use this color when unchecked.
Color
?
get
reactionColor
=>
_reactionColor
;
Color
get
inactiveColor
=>
_inactiveColor
!;
Color
?
_reactionColor
;
Color
?
_inactiveColor
;
set
reactionColor
(
Color
?
value
)
{
set
inactiveColor
(
Color
value
)
{
assert
(
value
!=
null
);
if
(
_inactiveColor
==
value
)
{
if
(
value
==
_reactionColor
)
return
;
return
;
_reactionColor
=
value
;
}
markNeedsPaint
();
_inactiveColor
=
value
;
notifyListeners
();
}
}
/// The color that should be used for the reaction when the toggleable is
/// The color that should be used for the reaction when the toggleable is
...
@@ -338,184 +435,161 @@ abstract class RenderToggleable extends RenderConstrainedBox {
...
@@ -338,184 +435,161 @@ abstract class RenderToggleable extends RenderConstrainedBox {
///
///
/// Used when the toggleable needs to change the reaction color/transparency
/// Used when the toggleable needs to change the reaction color/transparency
/// that is displayed when the toggleable is inactive and tapped.
/// that is displayed when the toggleable is inactive and tapped.
///
Color
get
inactiveReactionColor
=>
_inactiveReactionColor
!;
/// Defaults to the [activeColor] at alpha [kRadialReactionAlpha].
Color
?
get
inactiveReactionColor
=>
_inactiveReactionColor
;
Color
?
_inactiveReactionColor
;
Color
?
_inactiveReactionColor
;
set
inactiveReactionColor
(
Color
?
value
)
{
set
inactiveReactionColor
(
Color
value
)
{
assert
(
value
!=
null
);
if
(
value
==
_inactiveReactionColor
)
{
if
(
value
==
_inactiveReactionColor
)
return
;
return
;
}
_inactiveReactionColor
=
value
;
_inactiveReactionColor
=
value
;
markNeedsPaint
();
notifyListeners
();
}
}
/// The splash radius for the radial reaction.
/// The color that should be used for the reaction when the toggleable is
double
get
splashRadius
=>
_splashRadius
;
/// active.
double
_splashRadius
;
///
set
splashRadius
(
double
value
)
{
/// Used when the toggleable needs to change the reaction color/transparency
if
(
value
==
_splashRadius
)
/// that is displayed when the toggleable is active and tapped.
Color
get
reactionColor
=>
_reactionColor
!;
Color
?
_reactionColor
;
set
reactionColor
(
Color
value
)
{
if
(
value
==
_reactionColor
)
{
return
;
return
;
_splashRadius
=
value
;
}
markNeedsPaint
();
_reactionColor
=
value
;
notifyListeners
();
}
}
///
Called when the control changes val
ue.
///
The color that should be used for the reaction when [isHovered] is tr
ue.
///
///
/// If the control is tapped, [onChanged] is called immediately with the new
/// Used when the toggleable needs to change the reaction color/transparency,
/// value.
/// when it is being hovered over.
///
Color
get
hoverColor
=>
_hoverColor
!;
/// The control is considered interactive (see [isInteractive]) if this
Color
?
_hoverColor
;
/// callback is non-null. If the callback is null, then the control is
set
hoverColor
(
Color
value
)
{
/// disabled, and non-interactive. A disabled checkbox, for example, is
if
(
value
==
_hoverColor
)
{
/// displayed using a grey color and its value cannot be changed.
ValueChanged
<
bool
?>?
get
onChanged
=>
_onChanged
;
ValueChanged
<
bool
?>?
_onChanged
;
set
onChanged
(
ValueChanged
<
bool
?>?
value
)
{
if
(
value
==
_onChanged
)
return
;
return
;
final
bool
wasInteractive
=
isInteractive
;
_onChanged
=
value
;
if
(
wasInteractive
!=
isInteractive
)
{
markNeedsPaint
();
markNeedsSemanticsUpdate
();
}
}
_hoverColor
=
value
;
notifyListeners
();
}
}
///
Whether [value] of this control can be changed by user interaction
.
///
The color that should be used for the reaction when [isFocused] is true
.
///
///
/// The control is considered interactive if the [onChanged] callback is
/// Used when the toggleable needs to change the reaction color/transparency,
/// non-null. If the callback is null, then the control is disabled, and
/// when it has focus.
/// non-interactive. A disabled checkbox, for example, is displayed using a
Color
get
focusColor
=>
_focusColor
!;
/// grey color and its value cannot be changed.
Color
?
_focusColor
;
bool
get
isInteractive
=>
onChanged
!=
null
;
set
focusColor
(
Color
value
)
{
if
(
value
==
_focusColor
)
{
late
TapGestureRecognizer
_tap
;
return
;
Offset
?
_downPosition
;
@override
void
attach
(
PipelineOwner
owner
)
{
super
.
attach
(
owner
);
if
(
value
==
false
)
_positionController
.
reverse
();
else
_positionController
.
forward
();
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
;
}
}
}
_focusColor
=
value
;
notifyListeners
();
}
}
@override
/// The splash radius for the radial reaction.
void
detach
()
{
double
get
splashRadius
=>
_splashRadius
!;
_positionController
.
stop
();
double
?
_splashRadius
;
_reactionController
.
stop
();
set
splashRadius
(
double
value
)
{
_reactionHoverFadeController
.
stop
();
if
(
value
==
_splashRadius
)
{
_reactionFocusFadeController
.
stop
();
return
;
super
.
detach
();
}
void
_handleTapDown
(
TapDownDetails
details
)
{
if
(
isInteractive
)
{
_downPosition
=
globalToLocal
(
details
.
globalPosition
);
_reactionController
.
forward
();
}
}
_splashRadius
=
value
;
notifyListeners
();
}
}
void
_handleTap
()
{
/// The [Offset] within the Toggleable at which a pointer touched the Toggleable.
if
(!
isInteractive
)
///
/// This is null if currently no pointer is touching the Toggleable.
///
/// Usually set to [ToggleableStateMixin.downPosition].
Offset
?
get
downPosition
=>
_downPosition
;
Offset
?
_downPosition
;
set
downPosition
(
Offset
?
value
)
{
if
(
value
==
_downPosition
)
{
return
;
return
;
switch
(
value
)
{
case
false
:
onChanged
!(
true
);
break
;
case
true
:
onChanged
!(
tristate
?
null
:
false
);
break
;
case
null
:
onChanged
!(
false
);
break
;
}
}
sendSemanticsEvent
(
const
TapSemanticEvent
());
_downPosition
=
value
;
}
notifyListeners
();
void
_handleTapUp
(
TapUpDetails
details
)
{
_downPosition
=
null
;
if
(
isInteractive
)
_reactionController
.
reverse
();
}
}
void
_handleTapCancel
()
{
/// True if this toggleable has the input focus.
_downPosition
=
null
;
bool
get
isFocused
=>
_isFocused
!;
if
(
isInteractive
)
bool
?
_isFocused
;
_reactionController
.
reverse
();
set
isFocused
(
bool
?
value
)
{
if
(
value
==
_isFocused
)
{
return
;
}
_isFocused
=
value
;
notifyListeners
();
}
}
@override
/// True if this toggleable is being hovered over by a pointer.
bool
hitTestSelf
(
Offset
position
)
=>
true
;
bool
get
isHovered
=>
_isHovered
!;
bool
?
_isHovered
;
@override
set
isHovered
(
bool
?
value
)
{
void
handleEvent
(
PointerEvent
event
,
BoxHitTestEntry
entry
)
{
if
(
value
==
_isHovered
)
{
assert
(
debugHandleEvent
(
event
,
entry
));
return
;
if
(
event
is
PointerDownEvent
&&
isInteractive
)
}
_tap
.
addPointer
(
event
);
_isHovered
=
value
;
notifyListeners
();
}
}
/// Used by subclasses to paint the radial ink reaction for this control.
/// Used by subclasses to paint the radial ink reaction for this control.
///
///
/// The reaction is painted on the given canvas at the given offset. The
/// The reaction is painted on the given canvas at the given offset. The
/// origin is the center point of the reaction (usually distinct from the
/// origin is the center point of the reaction (usually distinct from the
/// point at which the user interacted with the control, which is handled
/// [downPosition] at which the user interacted with the control).
/// automatically).
void
paintRadialReaction
({
void
paintRadialReaction
(
Canvas
canvas
,
Offset
offset
,
Offset
origin
)
{
required
Canvas
canvas
,
if
(!
_reaction
.
isDismissed
||
!
_reactionFocusFade
.
isDismissed
||
!
_reactionHoverFade
.
isDismissed
)
{
Offset
offset
=
Offset
.
zero
,
required
Offset
origin
,
})
{
if
(!
reaction
.
isDismissed
||
!
reactionFocusFade
.
isDismissed
||
!
reactionHoverFade
.
isDismissed
)
{
final
Paint
reactionPaint
=
Paint
()
final
Paint
reactionPaint
=
Paint
()
..
color
=
Color
.
lerp
(
..
color
=
Color
.
lerp
(
Color
.
lerp
(
Color
.
lerp
(
Color
.
lerp
(
inactiveReactionColor
,
reactionColor
,
_
position
.
value
),
Color
.
lerp
(
inactiveReactionColor
,
reactionColor
,
position
.
value
),
hoverColor
,
hoverColor
,
_
reactionHoverFade
.
value
,
reactionHoverFade
.
value
,
),
),
focusColor
,
focusColor
,
_
reactionFocusFade
.
value
,
reactionFocusFade
.
value
,
)!;
)!;
final
Offset
center
=
Offset
.
lerp
(
_downPosition
??
origin
,
origin
,
_
reaction
.
value
)!;
final
Offset
center
=
Offset
.
lerp
(
downPosition
??
origin
,
origin
,
reaction
.
value
)!;
final
Animatable
<
double
>
radialReactionRadiusTween
=
Tween
<
double
>(
final
Animatable
<
double
>
radialReactionRadiusTween
=
Tween
<
double
>(
begin:
0.0
,
begin:
0.0
,
end:
splashRadius
,
end:
splashRadius
,
);
);
final
double
reactionRadius
=
hasFocus
||
hovering
final
double
reactionRadius
=
isFocused
||
isHovered
?
splashRadius
?
splashRadius
:
radialReactionRadiusTween
.
evaluate
(
_
reaction
);
:
radialReactionRadiusTween
.
evaluate
(
reaction
);
if
(
reactionRadius
>
0.0
)
{
if
(
reactionRadius
>
0.0
)
{
canvas
.
drawCircle
(
center
+
offset
,
reactionRadius
,
reactionPaint
);
canvas
.
drawCircle
(
center
+
offset
,
reactionRadius
,
reactionPaint
);
}
}
}
}
}
}
@override
void
describeSemanticsConfiguration
(
SemanticsConfiguration
config
)
{
super
.
describeSemanticsConfiguration
(
config
);
config
.
isEnabled
=
isInteractive
;
@override
if
(
isInteractive
)
void
dispose
()
{
config
.
onTap
=
_handleTap
;
_position
?.
removeListener
(
notifyListeners
);
_reaction
?.
removeListener
(
notifyListeners
);
_reactionFocusFade
?.
removeListener
(
notifyListeners
);
_reactionHoverFade
?.
removeListener
(
notifyListeners
);
super
.
dispose
();
}
}
@override
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
bool
shouldRepaint
(
covariant
CustomPainter
oldDelegate
)
=>
true
;
super
.
debugFillProperties
(
properties
);
properties
.
add
(
FlagProperty
(
'value'
,
value:
value
,
ifTrue:
'checked'
,
ifFalse:
'unchecked'
,
showName:
true
));
@override
properties
.
add
(
FlagProperty
(
'isInteractive'
,
value:
isInteractive
,
ifTrue:
'enabled'
,
ifFalse:
'disabled'
,
defaultValue:
true
));
bool
?
hitTest
(
Offset
position
)
=>
null
;
}
@override
SemanticsBuilderCallback
?
get
semanticsBuilder
=>
null
;
@override
bool
shouldRebuildSemantics
(
covariant
CustomPainter
oldDelegate
)
=>
false
;
}
}
packages/flutter/test/material/checkbox_test.dart
View file @
14552a96
...
@@ -99,7 +99,7 @@ void main() {
...
@@ -99,7 +99,7 @@ void main() {
),
),
));
));
expect
(
tester
.
getSemantics
(
find
.
by
WidgetPredicate
((
Widget
widget
)
=>
widget
.
runtimeType
.
toString
()
==
'_CheckboxRenderObjectWidget'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
by
Type
(
Checkbox
)),
matchesSemantics
(
hasCheckedState:
true
,
hasCheckedState:
true
,
hasEnabledState:
true
,
hasEnabledState:
true
,
// isFocusable is delayed by 1 frame.
// isFocusable is delayed by 1 frame.
...
@@ -108,7 +108,7 @@ void main() {
...
@@ -108,7 +108,7 @@ void main() {
await
tester
.
pump
();
await
tester
.
pump
();
// isFocusable should be false now after the 1 frame delay.
// isFocusable should be false now after the 1 frame delay.
expect
(
tester
.
getSemantics
(
find
.
by
WidgetPredicate
((
Widget
widget
)
=>
widget
.
runtimeType
.
toString
()
==
'_CheckboxRenderObjectWidget'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
by
Type
(
Checkbox
)),
matchesSemantics
(
hasCheckedState:
true
,
hasCheckedState:
true
,
hasEnabledState:
true
,
hasEnabledState:
true
,
));
));
...
@@ -120,7 +120,7 @@ void main() {
...
@@ -120,7 +120,7 @@ void main() {
),
),
));
));
expect
(
tester
.
getSemantics
(
find
.
by
WidgetPredicate
((
Widget
widget
)
=>
widget
.
runtimeType
.
toString
()
==
'_CheckboxRenderObjectWidget'
)),
matchesSemantics
(
expect
(
tester
.
getSemantics
(
find
.
by
Type
(
Checkbox
)),
matchesSemantics
(
hasCheckedState:
true
,
hasCheckedState:
true
,
hasEnabledState:
true
,
hasEnabledState:
true
,
isChecked:
true
,
isChecked:
true
,
...
@@ -290,7 +290,7 @@ void main() {
...
@@ -290,7 +290,7 @@ void main() {
);
);
await
tester
.
tap
(
find
.
byType
(
Checkbox
));
await
tester
.
tap
(
find
.
byType
(
Checkbox
));
final
RenderObject
object
=
tester
.
firstRenderObject
(
find
.
byType
(
Focus
));
final
RenderObject
object
=
tester
.
firstRenderObject
(
find
.
byType
(
Checkbox
));
expect
(
checkboxValue
,
true
);
expect
(
checkboxValue
,
true
);
expect
(
semanticEvent
,
<
String
,
dynamic
>{
expect
(
semanticEvent
,
<
String
,
dynamic
>{
...
@@ -319,10 +319,8 @@ void main() {
...
@@ -319,10 +319,8 @@ void main() {
);
);
}
}
RenderToggleable
getCheckboxRenderer
()
{
RenderBox
getCheckboxRenderer
()
{
return
tester
.
renderObject
<
RenderToggleable
>(
find
.
byWidgetPredicate
((
Widget
widget
)
{
return
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
Checkbox
));
return
widget
.
runtimeType
.
toString
()
==
'_CheckboxRenderObjectWidget'
;
}));
}
}
await
tester
.
pumpWidget
(
buildFrame
(
false
));
await
tester
.
pumpWidget
(
buildFrame
(
false
));
...
@@ -377,10 +375,8 @@ void main() {
...
@@ -377,10 +375,8 @@ void main() {
);
);
}
}
RenderToggleable
getCheckboxRenderer
()
{
RenderBox
getCheckboxRenderer
()
{
return
tester
.
renderObject
<
RenderToggleable
>(
find
.
byWidgetPredicate
((
Widget
widget
)
{
return
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
Checkbox
));
return
widget
.
runtimeType
.
toString
()
==
'_CheckboxRenderObjectWidget'
;
}));
}
}
await
tester
.
pumpWidget
(
buildFrame
(
checkColor:
checkColor
));
await
tester
.
pumpWidget
(
buildFrame
(
checkColor:
checkColor
));
...
@@ -453,11 +449,10 @@ void main() {
...
@@ -453,11 +449,10 @@ void main() {
paints
paints
..
circle
(
color:
Colors
.
orange
[
500
])
..
circle
(
color:
Colors
.
orange
[
500
])
..
drrect
(
..
drrect
(
color:
const
Color
(
0x8a000000
),
color:
const
Color
(
0x8a000000
),
outer:
RRect
.
fromLTRBR
(
outer:
RRect
.
fromLTRBR
(
15.0
,
15.0
,
33.0
,
33.0
,
const
Radius
.
circular
(
1.0
)),
391.0
,
291.0
,
409.0
,
309.0
,
const
Radius
.
circular
(
1.0
)),
inner:
RRect
.
fromLTRBR
(
17.0
,
17.0
,
31.0
,
31.0
,
const
Radius
.
circular
(-
1.0
)),
inner:
RRect
.
fromLTRBR
(
393.0
,
),
293.0
,
407.0
,
307.0
,
const
Radius
.
circular
(-
1.0
))),
);
);
// Check what happens when disabled.
// Check what happens when disabled.
...
@@ -469,11 +464,10 @@ void main() {
...
@@ -469,11 +464,10 @@ void main() {
Material
.
of
(
tester
.
element
(
find
.
byType
(
Checkbox
))),
Material
.
of
(
tester
.
element
(
find
.
byType
(
Checkbox
))),
paints
paints
..
drrect
(
..
drrect
(
color:
const
Color
(
0x61000000
),
color:
const
Color
(
0x61000000
),
outer:
RRect
.
fromLTRBR
(
outer:
RRect
.
fromLTRBR
(
15.0
,
15.0
,
33.0
,
33.0
,
const
Radius
.
circular
(
1.0
)),
391.0
,
291.0
,
409.0
,
309.0
,
const
Radius
.
circular
(
1.0
)),
inner:
RRect
.
fromLTRBR
(
17.0
,
17.0
,
31.0
,
31.0
,
const
Radius
.
circular
(-
1.0
)),
inner:
RRect
.
fromLTRBR
(
393.0
,
),
293.0
,
407.0
,
307.0
,
const
Radius
.
circular
(-
1.0
))),
);
);
});
});
...
@@ -825,10 +819,8 @@ void main() {
...
@@ -825,10 +819,8 @@ void main() {
);
);
}
}
RenderToggleable
getCheckboxRenderer
()
{
RenderBox
getCheckboxRenderer
()
{
return
tester
.
renderObject
<
RenderToggleable
>(
find
.
byWidgetPredicate
((
Widget
widget
)
{
return
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
Checkbox
));
return
widget
.
runtimeType
.
toString
()
==
'_CheckboxRenderObjectWidget'
;
}));
}
}
await
tester
.
pumpWidget
(
buildFrame
(
enabled:
true
));
await
tester
.
pumpWidget
(
buildFrame
(
enabled:
true
));
...
@@ -878,10 +870,8 @@ void main() {
...
@@ -878,10 +870,8 @@ void main() {
);
);
}
}
RenderToggleable
getCheckboxRenderer
()
{
RenderBox
getCheckboxRenderer
()
{
return
tester
.
renderObject
<
RenderToggleable
>(
find
.
byWidgetPredicate
((
Widget
widget
)
{
return
tester
.
renderObject
<
RenderBox
>(
find
.
byType
(
Checkbox
));
return
widget
.
runtimeType
.
toString
()
==
'_CheckboxRenderObjectWidget'
;
}));
}
}
await
tester
.
pumpWidget
(
buildFrame
());
await
tester
.
pumpWidget
(
buildFrame
());
...
@@ -937,11 +927,9 @@ void main() {
...
@@ -937,11 +927,9 @@ void main() {
paints
paints
..
drrect
(
..
drrect
(
color:
const
Color
(
0xfff44336
),
color:
const
Color
(
0xfff44336
),
outer:
RRect
.
fromLTRBR
(
outer:
RRect
.
fromLTRBR
(
15.0
,
15.0
,
33.0
,
33.0
,
const
Radius
.
circular
(
5
)),
391.0
,
291.0
,
409.0
,
309.0
,
const
Radius
.
circular
(
5
)),
inner:
RRect
.
fromLTRBR
(
19.0
,
19.0
,
29.0
,
29.0
,
const
Radius
.
circular
(
1
)),
inner:
RRect
.
fromLTRBR
(
),
395.0
,
295.0
,
405.0
,
305.0
,
const
Radius
.
circular
(
1
)))
,
);
);
});
});
...
@@ -1184,6 +1172,29 @@ void main() {
...
@@ -1184,6 +1172,29 @@ void main() {
await
gesture
.
up
();
await
gesture
.
up
();
});
});
testWidgets
(
'Do not crash when widget disappears while pointer is down'
,
(
WidgetTester
tester
)
async
{
Widget
buildCheckbox
(
bool
show
)
{
return
MaterialApp
(
home:
Material
(
child:
Center
(
child:
show
?
Checkbox
(
value:
true
,
onChanged:
(
_
)
{
})
:
Container
(),
),
),
);
}
await
tester
.
pumpWidget
(
buildCheckbox
(
true
));
final
Offset
center
=
tester
.
getCenter
(
find
.
byType
(
Checkbox
));
// Put a pointer down on the screen.
final
TestGesture
gesture
=
await
tester
.
startGesture
(
center
);
await
tester
.
pump
();
// While the pointer is down, the widget disappears.
await
tester
.
pumpWidget
(
buildCheckbox
(
false
));
expect
(
find
.
byType
(
Checkbox
),
findsNothing
);
// Release pointer after widget disappeared.
gesture
.
up
();
});
}
}
class
_SelectedGrabMouseCursor
extends
MaterialStateMouseCursor
{
class
_SelectedGrabMouseCursor
extends
MaterialStateMouseCursor
{
...
...
packages/flutter/test/material/radio_test.dart
View file @
14552a96
...
@@ -308,7 +308,7 @@ void main() {
...
@@ -308,7 +308,7 @@ void main() {
));
));
await
tester
.
tap
(
find
.
byKey
(
key
));
await
tester
.
tap
(
find
.
byKey
(
key
));
final
RenderObject
object
=
tester
.
firstRenderObject
(
find
.
by
Type
(
Focus
));
final
RenderObject
object
=
tester
.
firstRenderObject
(
find
.
by
Key
(
key
));
expect
(
radioValue
,
1
);
expect
(
radioValue
,
1
);
expect
(
semanticEvent
,
<
String
,
dynamic
>{
expect
(
semanticEvent
,
<
String
,
dynamic
>{
...
@@ -1078,4 +1078,29 @@ void main() {
...
@@ -1078,4 +1078,29 @@ void main() {
reason:
'Hovered Radio should use overlay color
$hoverOverlayColor
over
$hoverColor
'
,
reason:
'Hovered Radio should use overlay color
$hoverOverlayColor
over
$hoverColor
'
,
);
);
});
});
testWidgets
(
'Do not crash when widget disappears while pointer is down'
,
(
WidgetTester
tester
)
async
{
final
Key
key
=
UniqueKey
();
Widget
buildRadio
(
bool
show
)
{
return
MaterialApp
(
home:
Material
(
child:
Center
(
child:
show
?
Radio
<
bool
>(
key:
key
,
value:
true
,
groupValue:
false
,
onChanged:
(
_
)
{
})
:
Container
(),
)
),
);
}
await
tester
.
pumpWidget
(
buildRadio
(
true
));
final
Offset
center
=
tester
.
getCenter
(
find
.
byKey
(
key
));
// Put a pointer down on the screen.
final
TestGesture
gesture
=
await
tester
.
startGesture
(
center
);
await
tester
.
pump
();
// While the pointer is down, the widget disappears.
await
tester
.
pumpWidget
(
buildRadio
(
false
));
expect
(
find
.
byKey
(
key
),
findsNothing
);
// Release pointer after widget disappeared.
gesture
.
up
();
});
}
}
packages/flutter/test/material/switch_list_tile_test.dart
View file @
14552a96
...
@@ -37,7 +37,7 @@ void main() {
...
@@ -37,7 +37,7 @@ void main() {
expect
(
log
,
equals
(<
dynamic
>[
false
,
'-'
,
false
]));
expect
(
log
,
equals
(<
dynamic
>[
false
,
'-'
,
false
]));
});
});
testWidgets
(
'SwitchListTile
control
test'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'SwitchListTile
semantics
test'
,
(
WidgetTester
tester
)
async
{
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
final
SemanticsTester
semantics
=
SemanticsTester
(
tester
);
await
tester
.
pumpWidget
(
wrap
(
await
tester
.
pumpWidget
(
wrap
(
child:
Column
(
child:
Column
(
...
...
packages/flutter/test/material/switch_test.dart
View file @
14552a96
...
@@ -301,8 +301,7 @@ void main() {
...
@@ -301,8 +301,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
const
Color
(
0x52000000
),
// Black with 32% opacity
color:
const
Color
(
0x52000000
),
// Black with 32% opacity
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
...
@@ -317,8 +316,7 @@ void main() {
...
@@ -317,8 +316,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
Colors
.
blue
[
600
]!.
withAlpha
(
0x80
),
color:
Colors
.
blue
[
600
]!.
withAlpha
(
0x80
),
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
...
@@ -351,8 +349,7 @@ void main() {
...
@@ -351,8 +349,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
Colors
.
black12
,
color:
Colors
.
black12
,
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
...
@@ -383,8 +380,7 @@ void main() {
...
@@ -383,8 +380,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
Colors
.
black12
,
color:
Colors
.
black12
,
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
...
@@ -427,8 +423,7 @@ void main() {
...
@@ -427,8 +423,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
Colors
.
blue
[
500
],
color:
Colors
.
blue
[
500
],
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
...
@@ -442,8 +437,7 @@ void main() {
...
@@ -442,8 +437,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
Colors
.
green
[
500
],
color:
Colors
.
green
[
500
],
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
...
@@ -528,19 +522,19 @@ void main() {
...
@@ -528,19 +522,19 @@ void main() {
await
gesture
.
up
();
await
gesture
.
up
();
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
value
,
isFalse
);
expect
(
value
,
isFalse
);
final
RenderToggleable
renderObject
=
tester
.
renderObject
<
RenderToggleable
>(
final
ToggleableStateMixin
state
=
tester
.
state
<
ToggleableStateMixin
>(
find
.
descendant
(
find
.
descendant
(
of:
find
.
byType
(
Switch
),
of:
find
.
byType
(
Switch
),
matching:
find
.
byWidgetPredicate
(
matching:
find
.
byWidgetPredicate
(
(
Widget
widget
)
=>
widget
.
runtimeType
.
toString
()
==
'_
SwitchRenderObjectWidget
'
,
(
Widget
widget
)
=>
widget
.
runtimeType
.
toString
()
==
'_
MaterialSwitch
'
,
),
),
),
),
);
);
expect
(
renderObject
.
position
.
value
,
lessThan
(
0.5
));
expect
(
state
.
position
.
value
,
lessThan
(
0.5
));
await
tester
.
pump
();
await
tester
.
pump
();
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
value
,
isFalse
);
expect
(
value
,
isFalse
);
expect
(
renderObject
.
position
.
value
,
0
);
expect
(
state
.
position
.
value
,
0
);
// Move past the middle.
// Move past the middle.
gesture
=
await
tester
.
startGesture
(
tester
.
getRect
(
find
.
byType
(
Switch
)).
center
);
gesture
=
await
tester
.
startGesture
(
tester
.
getRect
(
find
.
byType
(
Switch
)).
center
);
...
@@ -549,12 +543,12 @@ void main() {
...
@@ -549,12 +543,12 @@ void main() {
await
gesture
.
up
();
await
gesture
.
up
();
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
value
,
isTrue
);
expect
(
value
,
isTrue
);
expect
(
renderObject
.
position
.
value
,
greaterThan
(
0.5
));
expect
(
state
.
position
.
value
,
greaterThan
(
0.5
));
await
tester
.
pump
();
await
tester
.
pump
();
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
value
,
isTrue
);
expect
(
value
,
isTrue
);
expect
(
renderObject
.
position
.
value
,
1.0
);
expect
(
state
.
position
.
value
,
1.0
);
// Now move back to the left, the revert animation should play.
// Now move back to the left, the revert animation should play.
gesture
=
await
tester
.
startGesture
(
tester
.
getRect
(
find
.
byType
(
Switch
)).
center
);
gesture
=
await
tester
.
startGesture
(
tester
.
getRect
(
find
.
byType
(
Switch
)).
center
);
...
@@ -563,12 +557,12 @@ void main() {
...
@@ -563,12 +557,12 @@ void main() {
await
gesture
.
up
();
await
gesture
.
up
();
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
value
,
isTrue
);
expect
(
value
,
isTrue
);
expect
(
renderObject
.
position
.
value
,
lessThan
(
0.5
));
expect
(
state
.
position
.
value
,
lessThan
(
0.5
));
await
tester
.
pump
();
await
tester
.
pump
();
await
tester
.
pumpAndSettle
();
await
tester
.
pumpAndSettle
();
expect
(
value
,
isTrue
);
expect
(
value
,
isTrue
);
expect
(
renderObject
.
position
.
value
,
1.0
);
expect
(
state
.
position
.
value
,
1.0
);
});
});
testWidgets
(
'switch has semantic events'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'switch has semantic events'
,
(
WidgetTester
tester
)
async
{
...
@@ -601,7 +595,7 @@ void main() {
...
@@ -601,7 +595,7 @@ void main() {
),
),
);
);
await
tester
.
tap
(
find
.
byType
(
Switch
));
await
tester
.
tap
(
find
.
byType
(
Switch
));
final
RenderObject
object
=
tester
.
firstRenderObject
(
find
.
byType
(
Focus
));
final
RenderObject
object
=
tester
.
firstRenderObject
(
find
.
byType
(
Switch
));
expect
(
value
,
true
);
expect
(
value
,
true
);
expect
(
semanticEvent
,
<
String
,
dynamic
>{
expect
(
semanticEvent
,
<
String
,
dynamic
>{
...
@@ -750,8 +744,7 @@ void main() {
...
@@ -750,8 +744,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
const
Color
(
0x801e88e5
),
color:
const
Color
(
0x801e88e5
),
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
Colors
.
orange
[
500
])
..
circle
(
color:
Colors
.
orange
[
500
])
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
...
@@ -769,8 +762,7 @@ void main() {
...
@@ -769,8 +762,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
const
Color
(
0x52000000
),
color:
const
Color
(
0x52000000
),
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
Colors
.
orange
[
500
])
..
circle
(
color:
Colors
.
orange
[
500
])
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
...
@@ -788,8 +780,7 @@ void main() {
...
@@ -788,8 +780,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
const
Color
(
0x1f000000
),
color:
const
Color
(
0x1f000000
),
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
...
@@ -854,8 +845,7 @@ void main() {
...
@@ -854,8 +845,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
const
Color
(
0x801e88e5
),
color:
const
Color
(
0x801e88e5
),
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
...
@@ -875,8 +865,7 @@ void main() {
...
@@ -875,8 +865,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
const
Color
(
0x801e88e5
),
color:
const
Color
(
0x801e88e5
),
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
Colors
.
orange
[
500
])
..
circle
(
color:
Colors
.
orange
[
500
])
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
...
@@ -892,8 +881,7 @@ void main() {
...
@@ -892,8 +881,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
const
Color
(
0x1f000000
),
color:
const
Color
(
0x1f000000
),
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
...
@@ -1070,8 +1058,7 @@ void main() {
...
@@ -1070,8 +1058,7 @@ void main() {
),
),
);
);
final
RenderToggleable
oldSwitchRenderObject
=
tester
final
ToggleableStateMixin
oldSwitchState
=
tester
.
state
(
find
.
byWidgetPredicate
((
Widget
widget
)
=>
widget
.
runtimeType
.
toString
()
==
'_MaterialSwitch'
));
.
renderObject
(
find
.
byWidgetPredicate
((
Widget
widget
)
=>
widget
is
LeafRenderObjectWidget
));
stateSetter
(()
{
value
=
false
;
});
stateSetter
(()
{
value
=
false
;
});
await
tester
.
pump
();
await
tester
.
pump
();
...
@@ -1079,14 +1066,12 @@ void main() {
...
@@ -1079,14 +1066,12 @@ void main() {
stateSetter
(()
{
enabled
=
false
;
});
stateSetter
(()
{
enabled
=
false
;
});
await
tester
.
pump
();
await
tester
.
pump
();
final
RenderToggleable
updatedSwitchRenderObject
=
tester
final
ToggleableStateMixin
updatedSwitchState
=
tester
.
state
(
find
.
byWidgetPredicate
((
Widget
widget
)
=>
widget
.
runtimeType
.
toString
()
==
'_MaterialSwitch'
));
.
renderObject
(
find
.
byWidgetPredicate
((
Widget
widget
)
=>
widget
is
LeafRenderObjectWidget
));
expect
(
updatedSwitchState
.
isInteractive
,
false
);
expect
(
updatedSwitchRenderObject
.
isInteractive
,
false
);
expect
(
updatedSwitchState
,
oldSwitchState
);
expect
(
updatedSwitchRenderObject
,
oldSwitchRenderObject
);
expect
(
updatedSwitchState
.
position
.
isCompleted
,
false
);
expect
(
updatedSwitchRenderObject
.
position
.
isCompleted
,
false
);
expect
(
updatedSwitchState
.
position
.
isDismissed
,
false
);
expect
(
updatedSwitchRenderObject
.
position
.
isDismissed
,
false
);
});
});
testWidgets
(
'Switch thumb color resolves in active/enabled states'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Switch thumb color resolves in active/enabled states'
,
(
WidgetTester
tester
)
async
{
...
@@ -1137,8 +1122,7 @@ void main() {
...
@@ -1137,8 +1122,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
Colors
.
black12
,
color:
Colors
.
black12
,
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
...
@@ -1154,8 +1138,7 @@ void main() {
...
@@ -1154,8 +1138,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
Colors
.
black12
,
color:
Colors
.
black12
,
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
...
@@ -1171,8 +1154,7 @@ void main() {
...
@@ -1171,8 +1154,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
const
Color
(
0x52000000
),
// Black with 32% opacity,
color:
const
Color
(
0x52000000
),
// Black with 32% opacity,
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
...
@@ -1188,8 +1170,7 @@ void main() {
...
@@ -1188,8 +1170,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
Colors
.
black12
,
color:
Colors
.
black12
,
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
...
@@ -1246,8 +1227,7 @@ void main() {
...
@@ -1246,8 +1227,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
const
Color
(
0x801e88e5
),
color:
const
Color
(
0x801e88e5
),
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
...
@@ -1268,8 +1248,7 @@ void main() {
...
@@ -1268,8 +1248,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
const
Color
(
0x801e88e5
),
color:
const
Color
(
0x801e88e5
),
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
...
@@ -1327,8 +1306,7 @@ void main() {
...
@@ -1327,8 +1306,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
inactiveDisabledTrackColor
,
color:
inactiveDisabledTrackColor
,
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
))),
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
))),
reason:
'Inactive disabled switch track should use this value'
,
reason:
'Inactive disabled switch track should use this value'
,
);
);
...
@@ -1340,8 +1318,7 @@ void main() {
...
@@ -1340,8 +1318,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
activeDisabledTrackColor
,
color:
activeDisabledTrackColor
,
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
))),
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
))),
reason:
'Active disabled switch should match these colors'
,
reason:
'Active disabled switch should match these colors'
,
);
);
...
@@ -1353,8 +1330,7 @@ void main() {
...
@@ -1353,8 +1330,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
inactiveEnabledTrackColor
,
color:
inactiveEnabledTrackColor
,
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
))),
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
))),
reason:
'Inactive enabled switch should match these colors'
,
reason:
'Inactive enabled switch should match these colors'
,
);
);
...
@@ -1366,8 +1342,7 @@ void main() {
...
@@ -1366,8 +1342,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
inactiveDisabledTrackColor
,
color:
inactiveDisabledTrackColor
,
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
))),
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
))),
reason:
'Inactive disabled switch should match these colors'
,
reason:
'Inactive disabled switch should match these colors'
,
);
);
});
});
...
@@ -1420,8 +1395,7 @@ void main() {
...
@@ -1420,8 +1395,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
focusedTrackColor
,
color:
focusedTrackColor
,
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
))),
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
))),
reason:
'Inactive enabled switch should match these colors'
,
reason:
'Inactive enabled switch should match these colors'
,
);
);
...
@@ -1437,8 +1411,7 @@ void main() {
...
@@ -1437,8 +1411,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
hoveredTrackColor
,
color:
hoveredTrackColor
,
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
))),
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
))),
reason:
'Inactive enabled switch should match these colors'
,
reason:
'Inactive enabled switch should match these colors'
,
);
);
});
});
...
@@ -1488,8 +1461,7 @@ void main() {
...
@@ -1488,8 +1461,7 @@ void main() {
paints
paints
..
rrect
(
..
rrect
(
color:
Colors
.
black12
,
color:
Colors
.
black12
,
rrect:
RRect
.
fromLTRBR
(
rrect:
RRect
.
fromLTRBR
(
13.0
,
17.0
,
46.0
,
31.0
,
const
Radius
.
circular
(
7.0
)))
383.5
,
293.0
,
416.5
,
307.0
,
const
Radius
.
circular
(
7.0
)))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x33000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x24000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
..
circle
(
color:
const
Color
(
0x1f000000
))
...
@@ -1638,4 +1610,27 @@ void main() {
...
@@ -1638,4 +1610,27 @@ void main() {
reason:
'Hovered Switch should use overlay color
$hoverOverlayColor
over
$hoverColor
'
,
reason:
'Hovered Switch should use overlay color
$hoverOverlayColor
over
$hoverColor
'
,
);
);
});
});
testWidgets
(
'Do not crash when widget disappears while pointer is down'
,
(
WidgetTester
tester
)
async
{
Widget
buildSwitch
(
bool
show
)
{
return
MaterialApp
(
home:
Material
(
child:
Center
(
child:
show
?
Switch
(
value:
true
,
onChanged:
(
_
)
{
})
:
Container
(),
),
),
);
}
await
tester
.
pumpWidget
(
buildSwitch
(
true
));
final
Offset
center
=
tester
.
getCenter
(
find
.
byType
(
Switch
));
// Put a pointer down on the screen.
final
TestGesture
gesture
=
await
tester
.
startGesture
(
center
);
await
tester
.
pump
();
// While the pointer is down, the widget disappears.
await
tester
.
pumpWidget
(
buildSwitch
(
false
));
expect
(
find
.
byType
(
Switch
),
findsNothing
);
// Release pointer after widget disappeared.
gesture
.
up
();
});
}
}
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