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
2aa9bb2b
Unverified
Commit
2aa9bb2b
authored
Feb 13, 2018
by
Hans Muller
Committed by
GitHub
Feb 13, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Tri-state Checkbox (#14611)
parent
8507b72a
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
275 additions
and
71 deletions
+275
-71
checkbox.dart
packages/flutter/lib/src/material/checkbox.dart
+165
-50
radio.dart
packages/flutter/lib/src/material/radio.dart
+1
-0
switch.dart
packages/flutter/lib/src/material/switch.dart
+1
-0
toggleable.dart
packages/flutter/lib/src/material/toggleable.dart
+64
-21
checkbox_test.dart
packages/flutter/test/material/checkbox_test.dart
+44
-0
No files found.
packages/flutter/lib/src/material/checkbox.dart
View file @
2aa9bb2b
...
...
@@ -21,6 +21,10 @@ import 'toggleable.dart';
/// rebuild the checkbox with a new [value] to update the visual appearance of
/// the checkbox.
///
/// The checkbox can optionally display three values - true, false, and null -
/// if [tristate] is true. When [value] is null a dash is displayed. By default
/// [tristate] is false and the checkbox's [value] must be true or false.
///
/// Requires one of its ancestors to be a [Material] widget.
///
/// See also:
...
...
@@ -43,16 +47,20 @@ class Checkbox extends StatefulWidget {
///
/// The following arguments are required:
///
/// * [value], which determines whether the checkbox is checked
, and must not
///
be null
.
/// * [value], which determines whether the checkbox is checked
. The [value]
///
can only be be null if [tristate] is true
.
/// * [onChanged], which is called when the value of the checkbox should
/// change. It can be set to null to disable the checkbox.
///
/// The value of [tristate] must not be null.
const
Checkbox
({
Key
key
,
@required
this
.
value
,
this
.
tristate
:
false
,
@required
this
.
onChanged
,
this
.
activeColor
,
})
:
assert
(
value
!=
null
),
})
:
assert
(
tristate
!=
null
),
assert
(
tristate
||
value
!=
null
),
super
(
key:
key
);
/// Whether this checkbox is checked.
...
...
@@ -66,7 +74,12 @@ class Checkbox extends StatefulWidget {
/// change state until the parent widget rebuilds the checkbox with the new
/// value.
///
/// If null, the checkbox will be displayed as disabled.
/// If this callback is null, the checkbox will be displayed as disabled
/// and will not respond to input gestures.
///
/// When the checkbox is tapped, if [tristate] is false (the default) then
/// the [onChanged] callback will be applied to `!value`. If [tristate] is
/// true this callback cycle from false to true to null.
///
/// The callback provided to [onChanged] should update the state of the parent
/// [StatefulWidget] using the [State.setState] method, so that the parent
...
...
@@ -89,6 +102,18 @@ class Checkbox extends StatefulWidget {
/// Defaults to accent color of the current [Theme].
final
Color
activeColor
;
/// If true the checkbox's [value] can be true, false, or null.
///
/// Checkbox displays a dash when its value is null.
///
/// When a tri-state checkbox is tapped its [onChanged] callback will be
/// applied to true if the current value is null or false, false otherwise.
/// Typically tri-state checkboxes are disabled (the onChanged callback is
/// null) so they don't respond to taps.
///
/// If tristate is false (the default), [value] must not be null.
final
bool
tristate
;
/// The width of a checkbox widget.
static
const
double
width
=
18.0
;
...
...
@@ -103,6 +128,7 @@ class _CheckboxState extends State<Checkbox> with TickerProviderStateMixin {
final
ThemeData
themeData
=
Theme
.
of
(
context
);
return
new
_CheckboxRenderObjectWidget
(
value:
widget
.
value
,
tristate:
widget
.
tristate
,
activeColor:
widget
.
activeColor
??
themeData
.
accentColor
,
inactiveColor:
widget
.
onChanged
!=
null
?
themeData
.
unselectedWidgetColor
:
themeData
.
disabledColor
,
onChanged:
widget
.
onChanged
,
...
...
@@ -115,17 +141,20 @@ class _CheckboxRenderObjectWidget extends LeafRenderObjectWidget {
const
_CheckboxRenderObjectWidget
({
Key
key
,
@required
this
.
value
,
@required
this
.
tristate
,
@required
this
.
activeColor
,
@required
this
.
inactiveColor
,
@required
this
.
onChanged
,
@required
this
.
vsync
,
})
:
assert
(
value
!=
null
),
})
:
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
Color
activeColor
;
final
Color
inactiveColor
;
final
ValueChanged
<
bool
>
onChanged
;
...
...
@@ -134,6 +163,7 @@ class _CheckboxRenderObjectWidget extends LeafRenderObjectWidget {
@override
_RenderCheckbox
createRenderObject
(
BuildContext
context
)
=>
new
_RenderCheckbox
(
value:
value
,
tristate:
tristate
,
activeColor:
activeColor
,
inactiveColor:
inactiveColor
,
onChanged:
onChanged
,
...
...
@@ -144,6 +174,7 @@ class _CheckboxRenderObjectWidget extends LeafRenderObjectWidget {
void
updateRenderObject
(
BuildContext
context
,
_RenderCheckbox
renderObject
)
{
renderObject
..
value
=
value
..
tristate
=
tristate
..
activeColor
=
activeColor
..
inactiveColor
=
inactiveColor
..
onChanged
=
onChanged
...
...
@@ -158,67 +189,151 @@ const double _kStrokeWidth = 2.0;
class
_RenderCheckbox
extends
RenderToggleable
{
_RenderCheckbox
({
bool
value
,
bool
tristate
,
Color
activeColor
,
Color
inactiveColor
,
ValueChanged
<
bool
>
onChanged
,
@required
TickerProvider
vsync
,
}):
super
(
value:
value
,
activeColor:
activeColor
,
inactiveColor:
inactiveColor
,
onChanged:
onChanged
,
size:
const
Size
(
2
*
kRadialReactionRadius
,
2
*
kRadialReactionRadius
),
vsync:
vsync
,
);
}):
_oldValue
=
value
,
super
(
value:
value
,
tristate:
tristate
,
activeColor:
activeColor
,
inactiveColor:
inactiveColor
,
onChanged:
onChanged
,
size:
const
Size
(
2
*
kRadialReactionRadius
,
2
*
kRadialReactionRadius
),
vsync:
vsync
,
);
bool
_oldValue
;
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
set
value
(
bool
newValue
)
{
if
(
newValue
==
value
)
return
;
_oldValue
=
value
;
super
.
value
=
newValue
;
}
final
Canvas
canvas
=
context
.
canvas
;
// The square outer bounds of the checkbox at t, with the specified origin.
// At t == 0.0, the outer rect's size is _kEdgeSize (Checkbox.width)
// At t == 0.5, .. is _kEdgeSize - _kStrokeWidth
// At t == 1.0, .. is _kEdgeSize
RRect
_outerRectAt
(
Offset
origin
,
double
t
)
{
final
double
inset
=
1.0
-
(
t
-
0.5
).
abs
()
*
2.0
;
final
double
size
=
_kEdgeSize
-
inset
*
_kStrokeWidth
;
final
Rect
rect
=
new
Rect
.
fromLTWH
(
origin
.
dx
+
inset
,
origin
.
dy
+
inset
,
size
,
size
);
return
new
RRect
.
fromRectAndRadius
(
rect
,
_kEdgeRadius
);
}
final
double
offsetX
=
offset
.
dx
+
(
size
.
width
-
_kEdgeSize
)
/
2.0
;
final
double
offsetY
=
offset
.
dy
+
(
size
.
height
-
_kEdgeSize
)
/
2.0
;
// The checkbox's border color if value == false, or its fill color when
// value == true or null.
Color
_colorAt
(
double
t
)
{
// As t goes from 0.0 to 0.25, animate from the inactiveColor to activeColor.
return
onChanged
==
null
?
inactiveColor
:
(
t
>=
0.25
?
activeColor
:
Color
.
lerp
(
inactiveColor
,
activeColor
,
t
*
4.0
));
}
// White stroke used to paint the check and dash.
void
_initStrokePaint
(
Paint
paint
)
{
paint
..
color
=
const
Color
(
0xFFFFFFFF
)
..
style
=
PaintingStyle
.
stroke
..
strokeWidth
=
_kStrokeWidth
;
}
void
_drawBorder
(
Canvas
canvas
,
RRect
outer
,
double
t
,
Paint
paint
)
{
assert
(
t
>=
0.0
&&
t
<=
0.5
);
final
double
size
=
outer
.
width
;
// As t goes from 0.0 to 1.0, gradually fill the outer RRect.
final
RRect
inner
=
outer
.
deflate
(
math
.
min
(
size
/
2.0
,
_kStrokeWidth
+
size
*
t
));
canvas
.
drawDRRect
(
outer
,
inner
,
paint
);
}
void
_drawCheck
(
Canvas
canvas
,
Offset
origin
,
double
t
,
Paint
paint
)
{
assert
(
t
>=
0.0
&&
t
<=
1.0
);
// As t goes from 0.0 to 1.0, animate the two checkmark strokes from the
// mid point outwards.
final
Path
path
=
new
Path
();
const
Offset
start
=
const
Offset
(
_kEdgeSize
*
0.15
,
_kEdgeSize
*
0.45
);
const
Offset
mid
=
const
Offset
(
_kEdgeSize
*
0.4
,
_kEdgeSize
*
0.7
);
const
Offset
end
=
const
Offset
(
_kEdgeSize
*
0.85
,
_kEdgeSize
*
0.25
);
final
Offset
drawStart
=
Offset
.
lerp
(
start
,
mid
,
1.0
-
t
);
final
Offset
drawEnd
=
Offset
.
lerp
(
mid
,
end
,
t
);
path
.
moveTo
(
origin
.
dx
+
drawStart
.
dx
,
origin
.
dy
+
drawStart
.
dy
);
path
.
lineTo
(
origin
.
dx
+
mid
.
dx
,
origin
.
dy
+
mid
.
dy
);
path
.
lineTo
(
origin
.
dx
+
drawEnd
.
dx
,
origin
.
dy
+
drawEnd
.
dy
);
canvas
.
drawPath
(
path
,
paint
);
}
void
_drawDash
(
Canvas
canvas
,
Offset
origin
,
double
t
,
Paint
paint
)
{
assert
(
t
>=
0.0
&&
t
<=
1.0
);
// As t goes from 0.0 to 1.0, animate the horizontal line from the
// mid point outwards.
const
Offset
start
=
const
Offset
(
_kEdgeSize
*
0.2
,
_kEdgeSize
*
0.5
);
const
Offset
mid
=
const
Offset
(
_kEdgeSize
*
0.5
,
_kEdgeSize
*
0.5
);
const
Offset
end
=
const
Offset
(
_kEdgeSize
*
0.8
,
_kEdgeSize
*
0.5
);
final
Offset
drawStart
=
Offset
.
lerp
(
start
,
mid
,
1.0
-
t
);
final
Offset
drawEnd
=
Offset
.
lerp
(
mid
,
end
,
t
);
canvas
.
drawLine
(
origin
+
drawStart
,
origin
+
drawEnd
,
paint
);
}
@override
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
final
Canvas
canvas
=
context
.
canvas
;
paintRadialReaction
(
canvas
,
offset
,
size
.
center
(
Offset
.
zero
));
final
double
t
=
position
.
value
;
final
Offset
origin
=
offset
+
(
size
/
2.0
-
const
Size
.
square
(
_kEdgeSize
)
/
2.0
);
final
AnimationStatus
status
=
position
.
status
;
final
double
tNormalized
=
status
==
AnimationStatus
.
forward
||
status
==
AnimationStatus
.
completed
?
position
.
value
:
1.0
-
position
.
value
;
Color
borderColor
=
inactiveColor
;
if
(
onChanged
!=
null
)
borderColor
=
t
>=
0.25
?
activeColor
:
Color
.
lerp
(
inactiveColor
,
activeColor
,
t
*
4.0
);
// Four cases: false to null, false to true, null to false, true to false
if
(
_oldValue
==
false
||
value
==
false
)
{
final
double
t
=
value
==
false
?
1.0
-
tNormalized
:
tNormalized
;
final
RRect
outer
=
_outerRectAt
(
origin
,
t
);
final
Paint
paint
=
new
Paint
()..
color
=
_colorAt
(
t
);
final
Paint
paint
=
new
Paint
()
..
color
=
borderColor
;
if
(
t
<=
0.5
)
{
_drawBorder
(
canvas
,
outer
,
t
,
paint
);
}
else
{
canvas
.
drawRRect
(
outer
,
paint
);
final
double
inset
=
1.0
-
(
t
-
0.5
).
abs
()
*
2.0
;
final
double
rectSize
=
_kEdgeSize
-
inset
*
_kStrokeWidth
;
final
Rect
rect
=
new
Rect
.
fromLTWH
(
offsetX
+
inset
,
offsetY
+
inset
,
rectSize
,
rectSize
);
final
RRect
outer
=
new
RRect
.
fromRectAndRadius
(
rect
,
_kEdgeRadius
);
if
(
t
<=
0.5
)
{
// Outline
final
RRect
inner
=
outer
.
deflate
(
math
.
min
(
rectSize
/
2.0
,
_kStrokeWidth
+
rectSize
*
t
));
canvas
.
drawDRRect
(
outer
,
inner
,
paint
);
}
else
{
// Background
_initStrokePaint
(
paint
);
final
double
tShrink
=
(
t
-
0.5
)
*
2.0
;
if
(
_oldValue
==
null
)
_drawDash
(
canvas
,
origin
,
tShrink
,
paint
);
else
_drawCheck
(
canvas
,
origin
,
tShrink
,
paint
);
}
}
else
{
// Two cases: null to true, true to null
final
RRect
outer
=
_outerRectAt
(
origin
,
1.0
);
final
Paint
paint
=
new
Paint
()
..
color
=
_colorAt
(
1.0
);
canvas
.
drawRRect
(
outer
,
paint
);
// White inner check
final
double
value
=
(
t
-
0.5
)
*
2.0
;
paint
..
color
=
const
Color
(
0xFFFFFFFF
)
..
style
=
PaintingStyle
.
stroke
..
strokeWidth
=
_kStrokeWidth
;
final
Path
path
=
new
Path
();
const
Offset
start
=
const
Offset
(
_kEdgeSize
*
0.15
,
_kEdgeSize
*
0.45
);
const
Offset
mid
=
const
Offset
(
_kEdgeSize
*
0.4
,
_kEdgeSize
*
0.7
);
const
Offset
end
=
const
Offset
(
_kEdgeSize
*
0.85
,
_kEdgeSize
*
0.25
);
final
Offset
drawStart
=
Offset
.
lerp
(
start
,
mid
,
1.0
-
value
);
final
Offset
drawEnd
=
Offset
.
lerp
(
mid
,
end
,
value
);
path
.
moveTo
(
offsetX
+
drawStart
.
dx
,
offsetY
+
drawStart
.
dy
);
path
.
lineTo
(
offsetX
+
mid
.
dx
,
offsetY
+
mid
.
dy
);
path
.
lineTo
(
offsetX
+
drawEnd
.
dx
,
offsetY
+
drawEnd
.
dy
);
canvas
.
drawPath
(
path
,
paint
);
_initStrokePaint
(
paint
);
if
(
tNormalized
<=
0.5
)
{
final
double
tShrink
=
1.0
-
tNormalized
*
2.0
;
if
(
_oldValue
==
true
)
_drawCheck
(
canvas
,
origin
,
tShrink
,
paint
);
else
_drawDash
(
canvas
,
origin
,
tShrink
,
paint
);
}
else
{
final
double
tExpand
=
(
tNormalized
-
0.5
)
*
2.0
;
if
(
value
==
true
)
_drawCheck
(
canvas
,
origin
,
tExpand
,
paint
);
else
_drawDash
(
canvas
,
origin
,
tExpand
,
paint
);
}
}
}
// TODO(hmuller): smooth segues for cases where the value changes
// in the middle of position's animation cycle.
// https://github.com/flutter/flutter/issues/14674
// TODO(hmuller): accessibility support for tristate checkboxes.
// https://github.com/flutter/flutter/issues/14677
}
packages/flutter/lib/src/material/radio.dart
View file @
2aa9bb2b
...
...
@@ -176,6 +176,7 @@ class _RenderRadio extends RenderToggleable {
@required
TickerProvider
vsync
,
}):
super
(
value:
value
,
tristate:
false
,
activeColor:
activeColor
,
inactiveColor:
inactiveColor
,
onChanged:
onChanged
,
...
...
packages/flutter/lib/src/material/switch.dart
View file @
2aa9bb2b
...
...
@@ -247,6 +247,7 @@ class _RenderSwitch extends RenderToggleable {
_textDirection
=
textDirection
,
super
(
value:
value
,
tristate:
false
,
activeColor:
activeColor
,
inactiveColor:
inactiveColor
,
onChanged:
onChanged
,
...
...
packages/flutter/lib/src/material/toggleable.dart
View file @
2aa9bb2b
...
...
@@ -21,20 +21,23 @@ final Tween<double> _kRadialReactionRadiusTween = new Tween<double>(begin: 0.0,
abstract
class
RenderToggleable
extends
RenderConstrainedBox
{
/// Creates a toggleable render object.
///
/// The [
value], [
activeColor], and [inactiveColor] arguments must not be
/// null.
/// The [activeColor], and [inactiveColor] arguments must not be
/// null.
The [value] can only be null if tristate is true.
RenderToggleable
({
@required
bool
value
,
bool
tristate:
false
,
Size
size
,
@required
Color
activeColor
,
@required
Color
inactiveColor
,
ValueChanged
<
bool
>
onChanged
,
@required
TickerProvider
vsync
,
})
:
assert
(
value
!=
null
),
})
:
assert
(
tristate
!=
null
),
assert
(
tristate
||
value
!=
null
),
assert
(
activeColor
!=
null
),
assert
(
inactiveColor
!=
null
),
assert
(
vsync
!=
null
),
_value
=
value
,
_tristate
=
tristate
,
_activeColor
=
activeColor
,
_inactiveColor
=
inactiveColor
,
_onChanged
=
onChanged
,
...
...
@@ -47,7 +50,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
..
onTapCancel
=
_handleTapCancel
;
_positionController
=
new
AnimationController
(
duration:
_kToggleDuration
,
value:
value
?
1.0
:
0
.0
,
value:
value
==
false
?
0.0
:
1
.0
,
vsync:
vsync
,
);
_position
=
new
CurvedAnimation
(
...
...
@@ -79,8 +82,9 @@ abstract class RenderToggleable extends RenderConstrainedBox {
/// The visual value of the control.
///
/// When the control is inactive, the [value] is false and this animation has
/// the value 0.0. When the control is active, the value is true and this
/// animation has the value 1.0. When the control is changing from inactive
/// the value 0.0. When the control is active, the value either true or tristate
/// is true and the value is null. When the control is active the 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
/// gradually updates from 0.0 to 1.0 (or vice versa).
CurvedAnimation
get
position
=>
_position
;
...
...
@@ -110,7 +114,11 @@ abstract class RenderToggleable extends RenderConstrainedBox {
reactionController
.
resync
(
vsync
);
}
/// Whether this control is current "active" (checked, on, selected) or "inactive" (unchecked, off, not selected).
/// False if this control is "inactive" (not checked, off, or unselected).
///
/// If value is true then the control "active" (checked, on, or selected). If
/// tristate is true and value is null, then the control is considered to be
/// in its third or "indeterminate" state.
///
/// When the value changes, this object starts the [positionController] and
/// [position] animations to animate the visual appearance of the control to
...
...
@@ -118,7 +126,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
bool
get
value
=>
_value
;
bool
_value
;
set
value
(
bool
value
)
{
assert
(
value
!=
null
);
assert
(
tristate
||
value
!=
null
);
if
(
value
==
_value
)
return
;
_value
=
value
;
...
...
@@ -126,10 +134,29 @@ abstract class RenderToggleable extends RenderConstrainedBox {
_position
..
curve
=
Curves
.
easeIn
..
reverseCurve
=
Curves
.
easeOut
;
if
(
value
)
_positionController
.
forward
();
else
_positionController
.
reverse
();
switch
(
_positionController
.
status
)
{
case
AnimationStatus
.
forward
:
case
AnimationStatus
.
completed
:
_positionController
.
reverse
();
break
;
default
:
_positionController
.
forward
();
}
}
/// If true, [value] can be true, false, or null, otherwise [value] must
/// be true or false.
///
/// When [tristate] is true and [value] is null, then the control is
/// considered to be in its third or "indeterminate" state.
bool
get
tristate
=>
_tristate
;
bool
_tristate
;
set
tristate
(
bool
value
)
{
assert
(
tristate
!=
null
);
if
(
value
==
_tristate
)
return
;
_tristate
=
value
;
markNeedsSemanticsUpdate
();
}
/// The color that should be used in the active state (i.e., when [value] is true).
...
...
@@ -196,10 +223,10 @@ abstract class RenderToggleable extends RenderConstrainedBox {
@override
void
attach
(
PipelineOwner
owner
)
{
super
.
attach
(
owner
);
if
(
value
)
_positionController
.
forward
();
else
if
(
value
==
false
)
_positionController
.
reverse
();
else
_positionController
.
forward
();
if
(
isInteractive
)
{
switch
(
_reactionController
.
status
)
{
case
AnimationStatus
.
forward
:
...
...
@@ -223,12 +250,17 @@ abstract class RenderToggleable extends RenderConstrainedBox {
super
.
detach
();
}
// Handle the case where the _positionController's value changes because
// the user dragged the toggleable: we may reach 0.0 or 1.0 without
// seeing a tap. The Switch does this.
void
_handlePositionStateChanged
(
AnimationStatus
status
)
{
if
(
isInteractive
)
{
if
(
status
==
AnimationStatus
.
completed
&&
!
_value
)
if
(
isInteractive
&&
!
tristate
)
{
if
(
status
==
AnimationStatus
.
completed
&&
_value
==
false
)
{
onChanged
(
true
);
else
if
(
status
==
AnimationStatus
.
dismissed
&&
_value
)
}
else
if
(
status
==
AnimationStatus
.
dismissed
&&
_value
!=
false
)
{
onChanged
(
false
);
}
}
}
...
...
@@ -240,8 +272,19 @@ abstract class RenderToggleable extends RenderConstrainedBox {
}
void
_handleTap
()
{
if
(
isInteractive
)
onChanged
(!
_value
);
if
(!
isInteractive
)
return
;
switch
(
value
)
{
case
false
:
onChanged
(
true
);
break
;
case
true
:
onChanged
(
tristate
?
null
:
false
);
break
;
default
:
// case null:
onChanged
(
false
);
break
;
}
}
void
_handleTapUp
(
TapUpDetails
details
)
{
...
...
@@ -290,7 +333,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
config
.
isEnabled
=
isInteractive
;
if
(
isInteractive
)
config
.
onTap
=
_handleTap
;
config
.
isChecked
=
_value
;
config
.
isChecked
=
_value
!=
false
;
}
@override
...
...
packages/flutter/test/material/checkbox_test.dart
View file @
2aa9bb2b
...
...
@@ -102,4 +102,48 @@ void main() {
semantics
.
dispose
();
});
testWidgets
(
'CheckBox tristate: true'
,
(
WidgetTester
tester
)
async
{
bool
checkBoxValue
;
await
tester
.
pumpWidget
(
new
Material
(
child:
new
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
new
Checkbox
(
tristate:
true
,
value:
checkBoxValue
,
onChanged:
(
bool
value
)
{
setState
(()
{
checkBoxValue
=
value
;
});
},
);
},
),
),
);
expect
(
tester
.
widget
<
Checkbox
>(
find
.
byType
(
Checkbox
)).
value
,
null
);
await
tester
.
tap
(
find
.
byType
(
Checkbox
));
await
tester
.
pumpAndSettle
();
expect
(
checkBoxValue
,
false
);
await
tester
.
tap
(
find
.
byType
(
Checkbox
));
await
tester
.
pumpAndSettle
();
expect
(
checkBoxValue
,
true
);
await
tester
.
tap
(
find
.
byType
(
Checkbox
));
await
tester
.
pumpAndSettle
();
expect
(
checkBoxValue
,
null
);
checkBoxValue
=
true
;
await
tester
.
pumpAndSettle
();
expect
(
checkBoxValue
,
true
);
checkBoxValue
=
null
;
await
tester
.
pumpAndSettle
();
expect
(
checkBoxValue
,
null
);
});
}
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