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
67edb63d
Unverified
Commit
67edb63d
authored
Mar 04, 2021
by
Michael Goderbauer
Committed by
GitHub
Mar 04, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Re-land "Toggable Refactor (#76745)" (#77263)
parent
90f353f9
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
1052 additions
and
1331 deletions
+1052
-1331
main_test.dart
...ests/android_semantics_testing/test_driver/main_test.dart
+4
-24
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.
dev/integration_tests/android_semantics_testing/test_driver/main_test.dart
View file @
67edb63d
...
@@ -238,12 +238,7 @@ void main() {
...
@@ -238,12 +238,7 @@ void main() {
test
(
'Checkbox has correct Android semantics'
,
()
async
{
test
(
'Checkbox has correct Android semantics'
,
()
async
{
Future
<
AndroidSemanticsNode
>
getCheckboxSemantics
(
String
key
)
async
{
Future
<
AndroidSemanticsNode
>
getCheckboxSemantics
(
String
key
)
async
{
return
getSemantics
(
return
getSemantics
(
find
.
byValueKey
(
key
));
find
.
descendant
(
of:
find
.
byValueKey
(
key
),
matching:
find
.
byType
(
'_CheckboxRenderObjectWidget'
),
),
);
}
}
expect
(
expect
(
await
getCheckboxSemantics
(
checkboxKeyValue
),
await
getCheckboxSemantics
(
checkboxKeyValue
),
...
@@ -290,12 +285,7 @@ void main() {
...
@@ -290,12 +285,7 @@ void main() {
});
});
test
(
'Radio has correct Android semantics'
,
()
async
{
test
(
'Radio has correct Android semantics'
,
()
async
{
Future
<
AndroidSemanticsNode
>
getRadioSemantics
(
String
key
)
async
{
Future
<
AndroidSemanticsNode
>
getRadioSemantics
(
String
key
)
async
{
return
getSemantics
(
return
getSemantics
(
find
.
byValueKey
(
key
));
find
.
descendant
(
of:
find
.
byValueKey
(
key
),
matching:
find
.
byType
(
'_RadioRenderObjectWidget'
),
),
);
}
}
expect
(
expect
(
await
getRadioSemantics
(
radio2KeyValue
),
await
getRadioSemantics
(
radio2KeyValue
),
...
@@ -331,12 +321,7 @@ void main() {
...
@@ -331,12 +321,7 @@ void main() {
});
});
test
(
'Switch has correct Android semantics'
,
()
async
{
test
(
'Switch has correct Android semantics'
,
()
async
{
Future
<
AndroidSemanticsNode
>
getSwitchSemantics
(
String
key
)
async
{
Future
<
AndroidSemanticsNode
>
getSwitchSemantics
(
String
key
)
async
{
return
getSemantics
(
return
getSemantics
(
find
.
byValueKey
(
key
));
find
.
descendant
(
of:
find
.
byValueKey
(
key
),
matching:
find
.
byType
(
'_SwitchRenderObjectWidget'
),
),
);
}
}
expect
(
expect
(
await
getSwitchSemantics
(
switchKeyValue
),
await
getSwitchSemantics
(
switchKeyValue
),
...
@@ -374,12 +359,7 @@ void main() {
...
@@ -374,12 +359,7 @@ void main() {
// Regression test for https://github.com/flutter/flutter/issues/20820.
// Regression test for https://github.com/flutter/flutter/issues/20820.
test
(
'Switch can be labeled'
,
()
async
{
test
(
'Switch can be labeled'
,
()
async
{
Future
<
AndroidSemanticsNode
>
getSwitchSemantics
(
String
key
)
async
{
Future
<
AndroidSemanticsNode
>
getSwitchSemantics
(
String
key
)
async
{
return
getSemantics
(
return
getSemantics
(
find
.
byValueKey
(
key
));
find
.
descendant
(
of:
find
.
byValueKey
(
key
),
matching:
find
.
byType
(
'_SwitchRenderObjectWidget'
),
),
);
}
}
expect
(
expect
(
await
getSwitchSemantics
(
labeledSwitchKeyValue
),
await
getSwitchSemantics
(
labeledSwitchKeyValue
),
...
...
packages/flutter/lib/src/material/checkbox.dart
View file @
67edb63d
...
@@ -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
;
});
}
}
Set
<
MaterialState
>
get
_states
=>
<
MaterialState
>{
@override
if
(!
enabled
)
MaterialState
.
disabled
,
bool
get
tristate
=>
widget
.
tristate
;
if
(
_hovering
)
MaterialState
.
hovered
,
if
(
_focused
)
MaterialState
.
focused
,
@override
if
(
widget
.
value
==
null
||
widget
.
value
!)
MaterialState
.
selected
,
bool
?
get
value
=>
widget
.
value
;
};
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
)
...
@@ -426,194 +412,95 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
...
@@ -426,194 +412,95 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
??
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
,
child:
buildToggleable
(
mouseCursor:
effectiveMouseCursor
,
focusNode:
widget
.
focusNode
,
focusNode:
widget
.
focusNode
,
autofocus:
widget
.
autofocus
,
autofocus:
widget
.
autofocus
,
enabled:
enabled
,
size:
size
,
onShowFocusHighlight:
_handleFocusHighlightChanged
,
painter:
_painter
onShowHoverHighlight:
_handleHoverChanged
,
..
position
=
position
mouseCursor:
effectiveMouseCursor
,
..
reaction
=
reaction
child:
Builder
(
..
reactionFocusFade
=
reactionFocusFade
builder:
(
BuildContext
context
)
{
..
reactionHoverFade
=
reactionHoverFade
return
_CheckboxRenderObjectWidget
(
..
inactiveReactionColor
=
effectiveInactivePressedOverlayColor
value:
widget
.
value
,
..
reactionColor
=
effectiveActivePressedOverlayColor
tristate:
widget
.
tristate
,
..
hoverColor
=
effectiveHoverOverlayColor
activeColor:
effectiveActiveColor
,
..
focusColor
=
effectiveFocusOverlayColor
checkColor:
effectiveCheckColor
,
..
splashRadius
=
widget
.
splashRadius
??
themeData
.
checkboxTheme
.
splashRadius
??
kRadialReactionRadius
inactiveColor:
effectiveInactiveColor
,
..
downPosition
=
downPosition
focusColor:
effectiveFocusOverlayColor
,
..
isFocused
=
states
.
contains
(
MaterialState
.
focused
)
hoverColor:
effectiveHoverOverlayColor
,
..
isHovered
=
states
.
contains
(
MaterialState
.
hovered
)
reactionColor:
effectiveActivePressedOverlayColor
,
..
activeColor
=
effectiveActiveColor
inactiveReactionColor:
effectiveInactivePressedOverlayColor
,
..
inactiveColor
=
effectiveInactiveColor
splashRadius:
widget
.
splashRadius
??
themeData
.
checkboxTheme
.
splashRadius
??
kRadialReactionRadius
,
..
checkColor
=
effectiveCheckColor
onChanged:
widget
.
onChanged
,
..
value
=
value
additionalConstraints:
additionalConstraints
,
..
previousValue
=
_previousValue
vsync:
this
,
..
shape
=
widget
.
shape
??
themeData
.
checkboxTheme
.
shape
??
const
RoundedRectangleBorder
(
hasFocus:
_focused
,
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
1.0
))
hovering:
_hovering
,
)
side:
widget
.
side
??
themeData
.
checkboxTheme
.
side
,
..
side
=
widget
.
side
??
themeData
.
checkboxTheme
.
side
,
shape:
widget
.
shape
??
themeData
.
checkboxTheme
.
shape
??
const
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
1.0
)),
),
);
},
),
),
);
);
}
}
}
}
class
_CheckboxRenderObjectWidget
extends
LeafRenderObjectWidget
{
const
_CheckboxRenderObjectWidget
({
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
void
updateRenderObject
(
BuildContext
context
,
_RenderCheckbox
renderObject
)
{
renderObject
// The `tristate` must be changed before `value` due to the assertion at
// the beginning of `set value`.
..
tristate
=
tristate
..
value
=
value
..
activeColor
=
activeColor
..
checkColor
=
checkColor
..
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
;
const
double
_kEdgeSize
=
Checkbox
.
width
;
const
double
_kStrokeWidth
=
2.0
;
const
double
_kStrokeWidth
=
2.0
;
class
_RenderCheckbox
extends
RenderToggleable
{
class
_CheckboxPainter
extends
ToggleablePainter
{
_RenderCheckbox
({
Color
get
checkColor
=>
_checkColor
!;
bool
?
value
,
Color
?
_checkColor
;
required
bool
tristate
,
set
checkColor
(
Color
value
)
{
required
Color
activeColor
,
if
(
_checkColor
==
value
)
{
required
this
.
checkColor
,
return
;
required
Color
inactiveColor
,
}
Color
?
focusColor
,
_checkColor
=
value
;
Color
?
hoverColor
,
notifyListeners
();
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
;
bool
?
get
value
=>
_value
;
Color
checkColor
;
bool
?
_value
;
OutlinedBorder
shape
;
set
value
(
bool
?
value
)
{
BorderSide
?
side
;
if
(
_value
==
value
)
{
return
;
}
_value
=
value
;
notifyListeners
();
}
@override
bool
?
get
previousValue
=>
_previousValue
;
set
value
(
bool
?
newValue
)
{
bool
?
_previousValue
;
if
(
newValue
==
value
)
set
previousValue
(
bool
?
value
)
{
if
(
_previousValue
==
value
)
{
return
;
}
_previousValue
=
value
;
notifyListeners
();
}
OutlinedBorder
get
shape
=>
_shape
!;
OutlinedBorder
?
_shape
;
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 @
67edb63d
...
@@ -353,64 +353,47 @@ class Radio<T> extends StatefulWidget {
...
@@ -353,64 +353,47 @@ 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
_handleChanged
(
bool
?
selected
)
{
void
initState
()
{
if
(
selected
==
null
)
{
super
.
initState
();
widget
.
onChanged
!(
null
);
_actionMap
=
<
Type
,
Action
<
Intent
>>{
return
;
ActivateIntent:
CallbackAction
<
ActivateIntent
>(
onInvoke:
_actionHandler
,
),
};
}
}
if
(
selected
)
{
void
_actionHandler
(
ActivateIntent
intent
)
{
if
(
widget
.
onChanged
!=
null
)
{
widget
.
onChanged
!(
widget
.
value
);
widget
.
onChanged
!(
widget
.
value
);
}
}
final
RenderObject
renderObject
=
context
.
findRenderObject
()!;
renderObject
.
sendSemanticsEvent
(
const
TapSemanticEvent
());
}
}
bool
_focused
=
false
;
@override
void
_handleHighlightChanged
(
bool
focused
)
{
void
didUpdateWidget
(
Radio
<
T
>
oldWidget
)
{
if
(
_focused
!=
focused
)
{
super
.
didUpdateWidget
(
oldWidget
);
setState
(()
{
_focused
=
focused
;
});
if
(
widget
.
_selected
!=
oldWidget
.
_selected
)
{
animateToValue
();
}
}
}
}
bool
_hovering
=
false
;
@override
void
_handleHoverChanged
(
bool
hovering
)
{
void
dispose
()
{
if
(
_hovering
!=
hovering
)
{
_painter
.
dispose
();
setState
(()
{
_hovering
=
hovering
;
});
super
.
dispose
();
}
}
}
void
_handleChanged
(
bool
?
selected
)
{
@override
if
(
selected
==
null
)
{
ValueChanged
<
bool
?>?
get
onChanged
=>
widget
.
onChanged
!=
null
?
_handleChanged
:
null
;
widget
.
onChanged
!(
null
);
return
;
}
if
(
selected
)
{
widget
.
onChanged
!(
widget
.
value
);
}
}
bool
get
_selected
=>
widget
.
value
==
widget
.
groupValue
;
@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
,
child:
buildToggleable
(
focusNode:
widget
.
focusNode
,
focusNode:
widget
.
focusNode
,
autofocus:
widget
.
autofocus
,
autofocus:
widget
.
autofocus
,
mouseCursor:
effectiveMouseCursor
,
mouseCursor:
effectiveMouseCursor
,
enabled:
enabled
,
size:
size
,
onShowFocusHighlight:
_handleHighlightChanged
,
painter:
_painter
onShowHoverHighlight:
_handleHoverChanged
,
..
position
=
position
child:
Builder
(
..
reaction
=
reaction
builder:
(
BuildContext
context
)
{
..
reactionFocusFade
=
reactionFocusFade
return
_RadioRenderObjectWidget
(
..
reactionHoverFade
=
reactionHoverFade
selected:
_selected
,
..
inactiveReactionColor
=
effectiveInactivePressedOverlayColor
activeColor:
effectiveActiveColor
,
..
reactionColor
=
effectiveActivePressedOverlayColor
inactiveColor:
effectiveInactiveColor
,
..
hoverColor
=
effectiveHoverOverlayColor
focusColor:
effectiveFocusOverlayColor
,
..
focusColor
=
effectiveFocusOverlayColor
hoverColor:
effectiveHoverOverlayColor
,
..
splashRadius
=
widget
.
splashRadius
??
themeData
.
radioTheme
.
splashRadius
??
kRadialReactionRadius
reactionColor:
effectiveActivePressedOverlayColor
,
..
downPosition
=
downPosition
inactiveReactionColor:
effectiveInactivePressedOverlayColor
,
..
isFocused
=
states
.
contains
(
MaterialState
.
focused
)
splashRadius:
widget
.
splashRadius
??
themeData
.
radioTheme
.
splashRadius
??
kRadialReactionRadius
,
..
isHovered
=
states
.
contains
(
MaterialState
.
hovered
)
onChanged:
enabled
?
_handleChanged
:
null
,
..
activeColor
=
effectiveActiveColor
toggleable:
widget
.
toggleable
,
..
inactiveColor
=
effectiveInactiveColor
additionalConstraints:
additionalConstraints
,
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 @
67edb63d
...
@@ -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
);
final
bool
value
;
final
ValueChanged
<
bool
>?
onChanged
;
final
Color
?
activeColor
;
final
Color
?
activeTrackColor
;
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
;
@override
@override
void
initState
()
{
State
<
StatefulWidget
>
createState
()
=>
_MaterialSwitchState
();
super
.
initState
();
}
_actionMap
=
<
Type
,
Action
<
Intent
>>{
ActivateIntent:
CallbackAction
<
ActivateIntent
>(
onInvoke:
_actionHandler
),
};
}
void
_actionHandler
(
ActivateIntent
intent
)
{
class
_MaterialSwitchState
extends
State
<
_MaterialSwitch
>
with
TickerProviderStateMixin
,
ToggleableStateMixin
{
if
(
widget
.
onChanged
!=
null
)
{
final
_SwitchPainter
_painter
=
_SwitchPainter
();
widget
.
onChanged
!(!
widget
.
value
);
}
final
RenderObject
renderObject
=
context
.
findRenderObject
()!;
renderObject
.
sendSemanticsEvent
(
const
TapSemanticEvent
());
}
bool
_focused
=
false
;
@override
void
_handleFocusHighlightChanged
(
bool
focused
)
{
void
didUpdateWidget
(
_MaterialSwitch
oldWidget
)
{
if
(
focused
!=
_focused
)
{
super
.
didUpdateWidget
(
oldWidget
);
setState
(()
{
_focused
=
focused
;
});
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
();
bool
_hovering
=
false
;
void
_handleHoverChanged
(
bool
hovering
)
{
if
(
hovering
!=
_hovering
)
{
setState
(()
{
_hovering
=
hovering
;
});
}
}
}
}
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
(
return
Semantics
(
actions:
_actionMap
,
toggled:
widget
.
value
,
focusNode:
widget
.
focusNode
,
child:
GestureDetector
(
autofocus:
widget
.
autofocus
,
excludeFromSemantics:
true
,
enabled:
enabled
,
onHorizontalDragStart:
_handleDragStart
,
onShowFocusHighlight:
_handleFocusHighlightChanged
,
onHorizontalDragUpdate:
_handleDragUpdate
,
onShowHoverHighlight:
_handleHoverChanged
,
onHorizontalDragEnd:
_handleDragEnd
,
mouseCursor:
effectiveMouseCursor
,
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
_SwitchRenderObjectWidget
(
dragStartBehavior:
widget
.
dragStartBehavior
,
dragStartBehavior:
widget
.
dragStartBehavior
,
value:
widget
.
value
,
child:
buildToggleable
(
activeColor:
effectiveActiveThumbColor
,
mouseCursor:
effectiveMouseCursor
,
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
)
{
final
Size
size
=
getSwitchSize
(
Theme
.
of
(
context
));
return
Focus
(
focusNode:
widget
.
focusNode
,
focusNode:
widget
.
focusNode
,
autofocus:
widget
.
autofocus
,
autofocus:
widget
.
autofocus
,
child:
Container
(
size:
widget
.
size
,
width:
size
.
width
,
// Same size as the Material switch.
painter:
_painter
height:
size
.
height
,
..
position
=
position
alignment:
Alignment
.
center
,
..
reaction
=
reaction
child:
CupertinoSwitch
(
..
reactionFocusFade
=
reactionFocusFade
dragStartBehavior:
widget
.
dragStartBehavior
,
..
reactionHoverFade
=
reactionHoverFade
value:
widget
.
value
,
..
inactiveReactionColor
=
effectiveInactivePressedOverlayColor
onChanged:
widget
.
onChanged
,
..
reactionColor
=
effectiveActivePressedOverlayColor
activeColor:
widget
.
activeColor
,
..
hoverColor
=
effectiveHoverOverlayColor
trackColor:
widget
.
inactiveTrackColor
..
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
{
class
_SwitchPainter
extends
ToggleablePainter
{
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
{
_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
;
set
dragStartBehavior
(
DragStartBehavior
value
)
{
assert
(
value
!=
null
);
if
(
_drag
.
dragStartBehavior
==
value
)
return
;
_drag
.
dragStartBehavior
=
value
;
}
}
Color
get
surfaceColor
=>
_surfaceColor
;
Color
get
surfaceColor
=>
_surfaceColor
!
;
Color
_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
();
}
}
@override
void
detach
()
{
_cachedThumbPainter
?.
dispose
();
_cachedThumbPainter
=
null
;
super
.
detach
();
}
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
;
}
}
}
_isInteractive
=
value
;
notifyListeners
();
}
}
void
_handleDragEnd
(
DragEndDetails
details
)
{
double
get
trackInnerLength
=>
_trackInnerLength
!;
_needsPositionAnimation
=
true
;
double
?
_trackInnerLength
;
set
trackInnerLength
(
double
value
)
{
if
(
position
.
value
>=
0.5
!=
value
)
if
(
value
==
_trackInnerLength
)
{
onChanged
!(!
value
!);
return
;
reactionController
.
reverse
();
state
.
_didFinishDragging
();
}
}
_trackInnerLength
=
value
;
@override
notifyListeners
();
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 @
67edb63d
...
@@ -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
;
@override
notifyListeners
();
void
detach
()
{
_positionController
.
stop
();
_reactionController
.
stop
();
_reactionHoverFadeController
.
stop
();
_reactionFocusFadeController
.
stop
();
super
.
detach
();
}
}
void
_handleTapDown
(
TapDownDetails
details
)
{
/// The splash radius for the radial reaction.
if
(
isInteractive
)
{
double
get
splashRadius
=>
_splashRadius
!;
_downPosition
=
globalToLocal
(
details
.
globalPosition
);
double
?
_splashRadius
;
_reactionController
.
forward
();
set
splashRadius
(
double
value
)
{
if
(
value
==
_splashRadius
)
{
return
;
}
}
_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
)
{
/// 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
;
void
_handleTapCancel
()
{
notifyListeners
();
_downPosition
=
null
;
if
(
isInteractive
)
_reactionController
.
reverse
();
}
}
@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 @
67edb63d
...
@@ -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
));
...
@@ -454,10 +450,9 @@ void main() {
...
@@ -454,10 +450,9 @@ void main() {
..
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.
...
@@ -470,10 +465,9 @@ void main() {
...
@@ -470,10 +465,9 @@ void main() {
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 @
67edb63d
...
@@ -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 @
67edb63d
...
@@ -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 @
67edb63d
...
@@ -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