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
6b32c069
Unverified
Commit
6b32c069
authored
Oct 07, 2022
by
Greg Spencer
Committed by
GitHub
Oct 07, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add checkbox and radio menu buttons (#112821)
parent
91b5079f
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
995 additions
and
48 deletions
+995
-48
menu_anchor.dart
dev/manual_tests/lib/menu_anchor.dart
+143
-41
checkbox_menu_button.0.dart
.../api/lib/material/menu_anchor/checkbox_menu_button.0.dart
+108
-0
radio_menu_button.0.dart
...les/api/lib/material/menu_anchor/radio_menu_button.0.dart
+114
-0
checkbox_menu_button.0_test.dart
...est/material/menu_anchor/checkbox_menu_button.0_test.dart
+27
-0
radio_menu_button.0_test.dart
...i/test/material/menu_anchor/radio_menu_button.0_test.dart
+77
-0
checkbox.dart
packages/flutter/lib/src/material/checkbox.dart
+1
-1
menu_anchor.dart
packages/flutter/lib/src/material/menu_anchor.dart
+396
-0
checkbox_list_tile_test.dart
packages/flutter/test/material/checkbox_list_tile_test.dart
+1
-1
checkbox_test.dart
packages/flutter/test/material/checkbox_test.dart
+5
-5
menu_anchor_test.dart
packages/flutter/test/material/menu_anchor_test.dart
+123
-0
No files found.
dev/manual_tests/lib/menu_anchor.dart
View file @
6b32c069
...
@@ -211,10 +211,7 @@ class _ControlsState extends State<_Controls> {
...
@@ -211,10 +211,7 @@ class _ControlsState extends State<_Controls> {
alignmentOffset:
const
Offset
(
100
,
-
8
),
alignmentOffset:
const
Offset
(
100
,
-
8
),
menuChildren:
<
Widget
>[
menuChildren:
<
Widget
>[
MenuItemButton
(
MenuItemButton
(
shortcut:
const
SingleActivator
(
shortcut:
TestMenu
.
standaloneMenu1
.
shortcut
,
LogicalKeyboardKey
.
keyB
,
control:
true
,
),
onPressed:
()
{
onPressed:
()
{
_itemSelected
(
TestMenu
.
standaloneMenu1
);
_itemSelected
(
TestMenu
.
standaloneMenu1
);
},
},
...
@@ -426,6 +423,9 @@ class _TestMenus extends StatefulWidget {
...
@@ -426,6 +423,9 @@ class _TestMenus extends StatefulWidget {
class
_TestMenusState
extends
State
<
_TestMenus
>
{
class
_TestMenusState
extends
State
<
_TestMenus
>
{
final
TextEditingController
textController
=
TextEditingController
();
final
TextEditingController
textController
=
TextEditingController
();
bool
?
checkboxState
=
false
;
TestMenu
?
radioValue
;
ShortcutRegistryEntry
?
_shortcutsEntry
;
void
_itemSelected
(
TestMenu
item
)
{
void
_itemSelected
(
TestMenu
item
)
{
debugPrint
(
'App: Selected item
${item.label}
'
);
debugPrint
(
'App: Selected item
${item.label}
'
);
...
@@ -439,6 +439,79 @@ class _TestMenusState extends State<_TestMenus> {
...
@@ -439,6 +439,79 @@ class _TestMenusState extends State<_TestMenus> {
debugPrint
(
'App: Closed item
${item.label}
'
);
debugPrint
(
'App: Closed item
${item.label}
'
);
}
}
void
_setRadio
(
TestMenu
item
)
{
debugPrint
(
'App: Set Radio item
${item.label}
'
);
setState
(()
{
radioValue
=
item
;
});
}
void
_setCheck
(
TestMenu
item
)
{
debugPrint
(
'App: Set Checkbox item
${item.label}
'
);
setState
(()
{
switch
(
checkboxState
)
{
case
false
:
checkboxState
=
true
;
break
;
case
true
:
checkboxState
=
null
;
break
;
case
null
:
checkboxState
=
false
;
break
;
}
});
}
@override
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
_shortcutsEntry
?.
dispose
();
final
Map
<
ShortcutActivator
,
Intent
>
shortcuts
=
<
ShortcutActivator
,
Intent
>{};
for
(
final
TestMenu
item
in
TestMenu
.
values
)
{
if
(
item
.
shortcut
==
null
)
{
continue
;
}
switch
(
item
)
{
case
TestMenu
.
radioMenu1
:
case
TestMenu
.
radioMenu2
:
case
TestMenu
.
radioMenu3
:
shortcuts
[
item
.
shortcut
!]
=
VoidCallbackIntent
(()
=>
_setRadio
(
item
));
break
;
case
TestMenu
.
subMenu1
:
shortcuts
[
item
.
shortcut
!]
=
VoidCallbackIntent
(()
=>
_setCheck
(
item
));
break
;
case
TestMenu
.
mainMenu1
:
case
TestMenu
.
mainMenu2
:
case
TestMenu
.
mainMenu3
:
case
TestMenu
.
mainMenu4
:
case
TestMenu
.
subMenu2
:
case
TestMenu
.
subMenu3
:
case
TestMenu
.
subMenu4
:
case
TestMenu
.
subMenu5
:
case
TestMenu
.
subMenu6
:
case
TestMenu
.
subMenu7
:
case
TestMenu
.
subMenu8
:
case
TestMenu
.
subSubMenu1
:
case
TestMenu
.
subSubMenu2
:
case
TestMenu
.
subSubMenu3
:
case
TestMenu
.
subSubSubMenu1
:
case
TestMenu
.
testButton
:
case
TestMenu
.
standaloneMenu1
:
case
TestMenu
.
standaloneMenu2
:
shortcuts
[
item
.
shortcut
!]
=
VoidCallbackIntent
(()
=>
_itemSelected
(
item
));
break
;
}
}
_shortcutsEntry
=
ShortcutRegistry
.
of
(
context
).
addAll
(
shortcuts
);
}
@override
void
dispose
()
{
_shortcutsEntry
?.
dispose
();
super
.
dispose
();
}
@override
@override
Widget
build
(
BuildContext
context
)
{
Widget
build
(
BuildContext
context
)
{
return
Row
(
return
Row
(
...
@@ -455,19 +528,61 @@ class _TestMenusState extends State<_TestMenus> {
...
@@ -455,19 +528,61 @@ class _TestMenusState extends State<_TestMenus> {
_closeItem
(
TestMenu
.
mainMenu1
);
_closeItem
(
TestMenu
.
mainMenu1
);
},
},
menuChildren:
<
Widget
>[
menuChildren:
<
Widget
>[
MenuItemButton
(
CheckboxMenuButton
(
shortcut:
const
SingleActivator
(
value:
checkboxState
,
LogicalKeyboardKey
.
keyB
,
tristate:
true
,
control:
true
,
shortcut:
TestMenu
.
subMenu1
.
shortcut
,
),
leadingIcon:
widget
.
addItem
?
const
Icon
(
Icons
.
check_box
)
:
const
Icon
(
Icons
.
check_box_outline_blank
),
trailingIcon:
const
Icon
(
Icons
.
assessment
),
trailingIcon:
const
Icon
(
Icons
.
assessment
),
onPressed:
()
{
onChanged:
(
bool
?
value
)
{
setState
(()
{
checkboxState
=
value
;
});
_itemSelected
(
TestMenu
.
subMenu1
);
_itemSelected
(
TestMenu
.
subMenu1
);
},
},
child:
Text
(
TestMenu
.
subMenu1
.
label
),
child:
Text
(
TestMenu
.
subMenu1
.
label
),
),
),
RadioMenuButton
<
TestMenu
>(
value:
TestMenu
.
radioMenu1
,
groupValue:
radioValue
,
toggleable:
true
,
shortcut:
TestMenu
.
radioMenu1
.
shortcut
,
trailingIcon:
const
Icon
(
Icons
.
assessment
),
onChanged:
(
TestMenu
?
value
)
{
setState
(()
{
radioValue
=
value
;
});
_itemSelected
(
TestMenu
.
radioMenu1
);
},
child:
Text
(
TestMenu
.
radioMenu1
.
label
),
),
RadioMenuButton
<
TestMenu
>(
value:
TestMenu
.
radioMenu2
,
groupValue:
radioValue
,
toggleable:
true
,
shortcut:
TestMenu
.
radioMenu2
.
shortcut
,
trailingIcon:
const
Icon
(
Icons
.
assessment
),
onChanged:
(
TestMenu
?
value
)
{
setState
(()
{
radioValue
=
value
;
});
_itemSelected
(
TestMenu
.
radioMenu2
);
},
child:
Text
(
TestMenu
.
radioMenu2
.
label
),
),
RadioMenuButton
<
TestMenu
>(
value:
TestMenu
.
radioMenu3
,
groupValue:
radioValue
,
toggleable:
true
,
shortcut:
TestMenu
.
radioMenu3
.
shortcut
,
trailingIcon:
const
Icon
(
Icons
.
assessment
),
onChanged:
(
TestMenu
?
value
)
{
setState
(()
{
radioValue
=
value
;
});
_itemSelected
(
TestMenu
.
radioMenu3
);
},
child:
Text
(
TestMenu
.
radioMenu3
.
label
),
),
MenuItemButton
(
MenuItemButton
(
leadingIcon:
const
Icon
(
Icons
.
send
),
leadingIcon:
const
Icon
(
Icons
.
send
),
trailingIcon:
const
Icon
(
Icons
.
mail
),
trailingIcon:
const
Icon
(
Icons
.
mail
),
...
@@ -495,10 +610,7 @@ class _TestMenusState extends State<_TestMenus> {
...
@@ -495,10 +610,7 @@ class _TestMenusState extends State<_TestMenus> {
},
},
),
),
MenuItemButton
(
MenuItemButton
(
shortcut:
const
SingleActivator
(
shortcut:
TestMenu
.
subMenu3
.
shortcut
,
LogicalKeyboardKey
.
enter
,
control:
true
,
),
onPressed:
()
{
onPressed:
()
{
_itemSelected
(
TestMenu
.
subMenu3
);
_itemSelected
(
TestMenu
.
subMenu3
);
},
},
...
@@ -542,10 +654,6 @@ class _TestMenusState extends State<_TestMenus> {
...
@@ -542,10 +654,6 @@ class _TestMenusState extends State<_TestMenus> {
)
)
},
},
child:
MenuItemButton
(
child:
MenuItemButton
(
shortcut:
const
SingleActivator
(
LogicalKeyboardKey
.
keyA
,
control:
true
,
),
onPressed:
()
{
onPressed:
()
{
debugPrint
(
'Activated text input item with
${textController.text}
as a value.'
);
debugPrint
(
'Activated text input item with
${textController.text}
as a value.'
);
},
},
...
@@ -569,15 +677,7 @@ class _TestMenusState extends State<_TestMenus> {
...
@@ -569,15 +677,7 @@ class _TestMenusState extends State<_TestMenus> {
},
},
menuChildren:
<
Widget
>[
menuChildren:
<
Widget
>[
MenuItemButton
(
MenuItemButton
(
shortcut:
widget
.
addItem
shortcut:
TestMenu
.
subSubMenu1
.
shortcut
,
?
const
SingleActivator
(
LogicalKeyboardKey
.
f11
,
control:
true
,
)
:
const
SingleActivator
(
LogicalKeyboardKey
.
f10
,
control:
true
,
),
onPressed:
()
{
onPressed:
()
{
_itemSelected
(
TestMenu
.
subSubMenu1
);
_itemSelected
(
TestMenu
.
subSubMenu1
);
},
},
...
@@ -593,10 +693,11 @@ class _TestMenusState extends State<_TestMenus> {
...
@@ -593,10 +693,11 @@ class _TestMenusState extends State<_TestMenus> {
SubmenuButton
(
SubmenuButton
(
menuChildren:
<
Widget
>[
menuChildren:
<
Widget
>[
MenuItemButton
(
MenuItemButton
(
child:
Text
(
TestMenu
.
subSubSubMenu1
.
label
)
,
shortcut:
TestMenu
.
subSubSubMenu1
.
shortcut
,
onPressed:
()
{
onPressed:
()
{
_itemSelected
(
TestMenu
.
subSubSubMenu1
);
_itemSelected
(
TestMenu
.
subSubSubMenu1
);
},
},
child:
Text
(
TestMenu
.
subSubSubMenu1
.
label
),
),
),
],
],
child:
Text
(
TestMenu
.
subSubMenu3
.
label
),
child:
Text
(
TestMenu
.
subSubMenu3
.
label
),
...
@@ -606,10 +707,7 @@ class _TestMenusState extends State<_TestMenus> {
...
@@ -606,10 +707,7 @@ class _TestMenusState extends State<_TestMenus> {
),
),
MenuItemButton
(
MenuItemButton
(
// Disabled button
// Disabled button
shortcut:
const
SingleActivator
(
shortcut:
TestMenu
.
subMenu6
.
shortcut
,
LogicalKeyboardKey
.
tab
,
control:
true
,
),
child:
Text
(
TestMenu
.
subMenu6
.
label
),
child:
Text
(
TestMenu
.
subMenu6
.
label
),
),
),
MenuItemButton
(
MenuItemButton
(
...
@@ -646,22 +744,26 @@ enum TestMenu {
...
@@ -646,22 +744,26 @@ enum TestMenu {
mainMenu2
(
'Menu 2'
),
mainMenu2
(
'Menu 2'
),
mainMenu3
(
'Menu 3'
),
mainMenu3
(
'Menu 3'
),
mainMenu4
(
'Menu 4'
),
mainMenu4
(
'Menu 4'
),
subMenu1
(
'Sub Menu 1'
),
radioMenu1
(
'Radio Menu One'
,
SingleActivator
(
LogicalKeyboardKey
.
digit1
,
control:
true
)),
radioMenu2
(
'Radio Menu Two'
,
SingleActivator
(
LogicalKeyboardKey
.
digit2
,
control:
true
)),
radioMenu3
(
'Radio Menu Three'
,
SingleActivator
(
LogicalKeyboardKey
.
digit3
,
control:
true
)),
subMenu1
(
'Sub Menu 1'
,
SingleActivator
(
LogicalKeyboardKey
.
keyB
,
control:
true
)),
subMenu2
(
'Sub Menu 2'
),
subMenu2
(
'Sub Menu 2'
),
subMenu3
(
'Sub Menu 3'
),
subMenu3
(
'Sub Menu 3'
,
SingleActivator
(
LogicalKeyboardKey
.
enter
,
control:
true
)
),
subMenu4
(
'Sub Menu 4'
),
subMenu4
(
'Sub Menu 4'
),
subMenu5
(
'Sub Menu 5'
),
subMenu5
(
'Sub Menu 5'
),
subMenu6
(
'Sub Menu 6'
),
subMenu6
(
'Sub Menu 6'
,
SingleActivator
(
LogicalKeyboardKey
.
tab
,
control:
true
)
),
subMenu7
(
'Sub Menu 7'
),
subMenu7
(
'Sub Menu 7'
),
subMenu8
(
'Sub Menu 8'
),
subMenu8
(
'Sub Menu 8'
),
subSubMenu1
(
'Sub Sub Menu 1'
),
subSubMenu1
(
'Sub Sub Menu 1'
,
SingleActivator
(
LogicalKeyboardKey
.
f10
,
control:
true
)
),
subSubMenu2
(
'Sub Sub Menu 2'
),
subSubMenu2
(
'Sub Sub Menu 2'
),
subSubMenu3
(
'Sub Sub Menu 3'
),
subSubMenu3
(
'Sub Sub Menu 3'
),
subSubSubMenu1
(
'Sub Sub Sub Menu 1'
),
subSubSubMenu1
(
'Sub Sub Sub Menu 1'
,
SingleActivator
(
LogicalKeyboardKey
.
f11
,
control:
true
)
),
testButton
(
'TEST button'
),
testButton
(
'TEST button'
),
standaloneMenu1
(
'Standalone Menu 1'
),
standaloneMenu1
(
'Standalone Menu 1'
,
SingleActivator
(
LogicalKeyboardKey
.
keyC
,
control:
true
)
),
standaloneMenu2
(
'Standalone Menu 2'
);
standaloneMenu2
(
'Standalone Menu 2'
);
const
TestMenu
(
this
.
label
);
const
TestMenu
(
this
.
label
,
[
this
.
shortcut
]
);
final
String
label
;
final
String
label
;
final
MenuSerializableShortcut
?
shortcut
;
}
}
examples/api/lib/material/menu_anchor/checkbox_menu_button.0.dart
0 → 100644
View file @
6b32c069
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// Flutter code sample for [CheckboxMenuButton].
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
void
main
(
)
=>
runApp
(
const
MenuApp
());
class
MyCheckboxMenu
extends
StatefulWidget
{
const
MyCheckboxMenu
({
super
.
key
,
required
this
.
message
});
final
String
message
;
@override
State
<
MyCheckboxMenu
>
createState
()
=>
_MyCheckboxMenuState
();
}
class
_MyCheckboxMenuState
extends
State
<
MyCheckboxMenu
>
{
final
FocusNode
_buttonFocusNode
=
FocusNode
(
debugLabel:
'Menu Button'
);
static
const
SingleActivator
_showShortcut
=
SingleActivator
(
LogicalKeyboardKey
.
keyS
,
control:
true
);
bool
_showingMessage
=
false
;
@override
void
dispose
()
{
_buttonFocusNode
.
dispose
();
super
.
dispose
();
}
void
_setMessageVisibility
(
bool
visible
)
{
setState
(()
{
_showingMessage
=
visible
;
});
}
@override
Widget
build
(
BuildContext
context
)
{
return
CallbackShortcuts
(
bindings:
<
ShortcutActivator
,
VoidCallback
>{
_showShortcut:
()
{
_setMessageVisibility
(!
_showingMessage
);
},
},
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
<
Widget
>[
MenuAnchor
(
childFocusNode:
_buttonFocusNode
,
menuChildren:
<
Widget
>[
CheckboxMenuButton
(
value:
_showingMessage
,
onChanged:
(
bool
?
value
)
{
_setMessageVisibility
(
value
!);
},
child:
const
Text
(
'Show Message'
),
),
],
builder:
(
BuildContext
context
,
MenuController
controller
,
Widget
?
child
)
{
return
TextButton
(
focusNode:
_buttonFocusNode
,
onPressed:
()
{
if
(
controller
.
isOpen
)
{
controller
.
close
();
}
else
{
controller
.
open
();
}
},
child:
const
Text
(
'OPEN MENU'
),
);
},
),
Expanded
(
child:
Container
(
alignment:
Alignment
.
center
,
child:
Column
(
mainAxisAlignment:
MainAxisAlignment
.
center
,
children:
<
Widget
>[
Padding
(
padding:
const
EdgeInsets
.
all
(
12.0
),
child:
Text
(
_showingMessage
?
widget
.
message
:
''
,
style:
Theme
.
of
(
context
).
textTheme
.
headlineSmall
,
),
),
],
),
),
),
],
),
);
}
}
class
MenuApp
extends
StatelessWidget
{
const
MenuApp
({
super
.
key
});
static
const
String
kMessage
=
'"Talk less. Smile more." - A. Burr'
;
@override
Widget
build
(
BuildContext
context
)
{
return
const
MaterialApp
(
home:
Scaffold
(
body:
MyCheckboxMenu
(
message:
kMessage
)),
);
}
}
examples/api/lib/material/menu_anchor/radio_menu_button.0.dart
0 → 100644
View file @
6b32c069
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// Flutter code sample for [RadioMenuButton].
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
void
main
(
)
=>
runApp
(
const
MenuApp
());
class
MyRadioMenu
extends
StatefulWidget
{
const
MyRadioMenu
({
super
.
key
});
@override
State
<
MyRadioMenu
>
createState
()
=>
_MyRadioMenuState
();
}
class
_MyRadioMenuState
extends
State
<
MyRadioMenu
>
{
final
FocusNode
_buttonFocusNode
=
FocusNode
(
debugLabel:
'Menu Button'
);
Color
_backgroundColor
=
Colors
.
red
;
late
ShortcutRegistryEntry
_entry
;
static
const
SingleActivator
_redShortcut
=
SingleActivator
(
LogicalKeyboardKey
.
keyR
,
control:
true
);
static
const
SingleActivator
_greenShortcut
=
SingleActivator
(
LogicalKeyboardKey
.
keyG
,
control:
true
);
static
const
SingleActivator
_blueShortcut
=
SingleActivator
(
LogicalKeyboardKey
.
keyB
,
control:
true
);
@override
void
didChangeDependencies
()
{
super
.
didChangeDependencies
();
_entry
=
ShortcutRegistry
.
of
(
context
).
addAll
(<
ShortcutActivator
,
VoidCallbackIntent
>{
_redShortcut:
VoidCallbackIntent
(()
=>
_setBackgroundColor
(
Colors
.
red
)),
_greenShortcut:
VoidCallbackIntent
(()
=>
_setBackgroundColor
(
Colors
.
green
)),
_blueShortcut:
VoidCallbackIntent
(()
=>
_setBackgroundColor
(
Colors
.
blue
)),
});
}
@override
void
dispose
()
{
_buttonFocusNode
.
dispose
();
_entry
.
dispose
();
super
.
dispose
();
}
void
_setBackgroundColor
(
Color
?
color
)
{
setState
(()
{
_backgroundColor
=
color
!;
});
}
@override
Widget
build
(
BuildContext
context
)
{
return
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
<
Widget
>[
MenuAnchor
(
childFocusNode:
_buttonFocusNode
,
menuChildren:
<
Widget
>[
RadioMenuButton
<
Color
>(
value:
Colors
.
red
,
shortcut:
_redShortcut
,
groupValue:
_backgroundColor
,
onChanged:
_setBackgroundColor
,
child:
const
Text
(
'Red Background'
),
),
RadioMenuButton
<
Color
>(
value:
Colors
.
green
,
shortcut:
_greenShortcut
,
groupValue:
_backgroundColor
,
onChanged:
_setBackgroundColor
,
child:
const
Text
(
'Green Background'
),
),
RadioMenuButton
<
Color
>(
value:
Colors
.
blue
,
shortcut:
_blueShortcut
,
groupValue:
_backgroundColor
,
onChanged:
_setBackgroundColor
,
child:
const
Text
(
'Blue Background'
),
),
],
builder:
(
BuildContext
context
,
MenuController
controller
,
Widget
?
child
)
{
return
TextButton
(
focusNode:
_buttonFocusNode
,
onPressed:
()
{
if
(
controller
.
isOpen
)
{
controller
.
close
();
}
else
{
controller
.
open
();
}
},
child:
const
Text
(
'OPEN MENU'
),
);
},
),
Expanded
(
child:
Container
(
color:
_backgroundColor
,
),
),
],
);
}
}
class
MenuApp
extends
StatelessWidget
{
const
MenuApp
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
const
MaterialApp
(
home:
Scaffold
(
body:
MyRadioMenu
()),
);
}
}
examples/api/test/material/menu_anchor/checkbox_menu_button.0_test.dart
0 → 100644
View file @
6b32c069
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/material.dart'
;
import
'package:flutter_api_samples/material/menu_anchor/checkbox_menu_button.0.dart'
as
example
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'Can open menu and show message'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
example
.
MenuApp
(),
);
await
tester
.
tap
(
find
.
byType
(
TextButton
));
await
tester
.
pump
();
expect
(
find
.
text
(
'Show Message'
),
findsOneWidget
);
expect
(
find
.
text
(
example
.
MenuApp
.
kMessage
),
findsNothing
);
await
tester
.
tap
(
find
.
text
(
'Show Message'
));
await
tester
.
pump
();
expect
(
find
.
text
(
'Show Message'
),
findsNothing
);
expect
(
find
.
text
(
example
.
MenuApp
.
kMessage
),
findsOneWidget
);
});
}
examples/api/test/material/menu_anchor/radio_menu_button.0_test.dart
0 → 100644
View file @
6b32c069
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter_api_samples/material/menu_anchor/radio_menu_button.0.dart'
as
example
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'Can open menu'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
example
.
MenuApp
(),
);
await
tester
.
tap
(
find
.
byType
(
TextButton
));
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
find
.
text
(
'Red Background'
),
findsOneWidget
);
expect
(
find
.
text
(
'Green Background'
),
findsOneWidget
);
expect
(
find
.
text
(
'Blue Background'
),
findsOneWidget
);
expect
(
find
.
byType
(
Radio
<
Color
>),
findsNWidgets
(
3
));
expect
(
tester
.
widget
<
Container
>(
find
.
byType
(
Container
)).
color
,
equals
(
Colors
.
red
));
await
tester
.
tap
(
find
.
text
(
'Green Background'
));
await
tester
.
pump
();
expect
(
tester
.
widget
<
Container
>(
find
.
byType
(
Container
)).
color
,
equals
(
Colors
.
green
));
});
testWidgets
(
'Shortcuts work'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
example
.
MenuApp
(),
);
// Open the menu so we can watch state changes resulting from the shortcuts
// firing.
await
tester
.
tap
(
find
.
byType
(
TextButton
));
await
tester
.
pump
();
expect
(
find
.
text
(
'Red Background'
),
findsOneWidget
);
expect
(
find
.
text
(
'Green Background'
),
findsOneWidget
);
expect
(
find
.
text
(
'Blue Background'
),
findsOneWidget
);
expect
(
find
.
byType
(
Radio
<
Color
>),
findsNWidgets
(
3
));
expect
(
tester
.
widget
<
Container
>(
find
.
byType
(
Container
)).
color
,
equals
(
Colors
.
red
));
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
controlLeft
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
keyG
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
controlLeft
);
await
tester
.
pump
();
// Need to pump twice because of the one frame delay in the notification to
// update the overlay entry.
await
tester
.
pump
();
expect
(
tester
.
widget
<
Radio
<
Color
>>(
find
.
descendant
(
of:
find
.
byType
(
RadioMenuButton
<
Color
>).
at
(
0
),
matching:
find
.
byType
(
Radio
<
Color
>))).
groupValue
,
equals
(
Colors
.
green
));
expect
(
tester
.
widget
<
Container
>(
find
.
byType
(
Container
)).
color
,
equals
(
Colors
.
green
));
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
controlLeft
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
keyR
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
controlLeft
);
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
tester
.
widget
<
Radio
<
Color
>>(
find
.
descendant
(
of:
find
.
byType
(
RadioMenuButton
<
Color
>).
at
(
1
),
matching:
find
.
byType
(
Radio
<
Color
>))).
groupValue
,
equals
(
Colors
.
red
));
expect
(
tester
.
widget
<
Container
>(
find
.
byType
(
Container
)).
color
,
equals
(
Colors
.
red
));
await
tester
.
sendKeyDownEvent
(
LogicalKeyboardKey
.
controlLeft
);
await
tester
.
sendKeyEvent
(
LogicalKeyboardKey
.
keyB
);
await
tester
.
sendKeyUpEvent
(
LogicalKeyboardKey
.
controlLeft
);
await
tester
.
pump
();
await
tester
.
pump
();
expect
(
tester
.
widget
<
Radio
<
Color
>>(
find
.
descendant
(
of:
find
.
byType
(
RadioMenuButton
<
Color
>).
at
(
2
),
matching:
find
.
byType
(
Radio
<
Color
>))).
groupValue
,
equals
(
Colors
.
blue
));
expect
(
tester
.
widget
<
Container
>(
find
.
byType
(
Container
)).
color
,
equals
(
Colors
.
blue
));
});
}
packages/flutter/lib/src/material/checkbox.dart
View file @
6b32c069
...
@@ -208,7 +208,7 @@ class Checkbox extends StatefulWidget {
...
@@ -208,7 +208,7 @@ class Checkbox extends StatefulWidget {
/// If true the checkbox's [value] can be true, false, or null.
/// If true the checkbox's [value] can be true, false, or null.
///
///
///
Checkbox
displays a dash when its value is null.
///
[Checkbox]
displays a dash when its value is null.
///
///
/// When a tri-state checkbox ([tristate] is true) is tapped, its [onChanged]
/// When a tri-state checkbox ([tristate] is true) is tapped, its [onChanged]
/// callback will be applied to true if the current value is false, to null if
/// callback will be applied to true if the current value is false, to null if
...
...
packages/flutter/lib/src/material/menu_anchor.dart
View file @
6b32c069
...
@@ -12,6 +12,7 @@ import 'package:flutter/widgets.dart';
...
@@ -12,6 +12,7 @@ import 'package:flutter/widgets.dart';
import
'button_style.dart'
;
import
'button_style.dart'
;
import
'button_style_button.dart'
;
import
'button_style_button.dart'
;
import
'checkbox.dart'
;
import
'color_scheme.dart'
;
import
'color_scheme.dart'
;
import
'colors.dart'
;
import
'colors.dart'
;
import
'constants.dart'
;
import
'constants.dart'
;
...
@@ -24,10 +25,18 @@ import 'menu_bar_theme.dart';
...
@@ -24,10 +25,18 @@ import 'menu_bar_theme.dart';
import
'menu_button_theme.dart'
;
import
'menu_button_theme.dart'
;
import
'menu_style.dart'
;
import
'menu_style.dart'
;
import
'menu_theme.dart'
;
import
'menu_theme.dart'
;
import
'radio.dart'
;
import
'text_button.dart'
;
import
'text_button.dart'
;
import
'theme.dart'
;
import
'theme.dart'
;
import
'theme_data.dart'
;
import
'theme_data.dart'
;
// Examples can assume:
// bool _throwShotAway = false;
// late BuildContext context;
// enum SingingCharacter { lafayette }
// late SingingCharacter? _character;
// late StateSetter setState;
// Enable if you want verbose logging about menu changes.
// Enable if you want verbose logging about menu changes.
const
bool
_kDebugMenus
=
false
;
const
bool
_kDebugMenus
=
false
;
...
@@ -1069,6 +1078,393 @@ class _MenuItemButtonState extends State<MenuItemButton> {
...
@@ -1069,6 +1078,393 @@ class _MenuItemButtonState extends State<MenuItemButton> {
}
}
}
}
/// A menu item that combines a [Checkbox] widget with a [MenuItemButton].
///
/// To style the checkbox separately from the button, add a [CheckboxTheme]
/// ancestor.
///
/// {@tool dartpad}
/// This example shows a menu with a checkbox that shows a message in the body
/// of the app if checked.
///
/// ** See code in examples/api/lib/material/menu_anchor/checkbox_menu_button.0.dart **
/// {@end-tool}
///
/// See also:
///
/// - [MenuBar], a widget that creates a menu bar of cascading menu items.
/// - [MenuAnchor], a widget that defines a region which can host a cascading
/// menu.
class
CheckboxMenuButton
extends
StatelessWidget
{
/// Creates a const [CheckboxMenuButton].
///
/// The [child], [value], and [onChanged] attributes are required.
const
CheckboxMenuButton
({
super
.
key
,
required
this
.
value
,
this
.
tristate
=
false
,
this
.
isError
=
false
,
required
this
.
onChanged
,
this
.
onHover
,
this
.
onFocusChange
,
this
.
focusNode
,
this
.
shortcut
,
this
.
style
,
this
.
statesController
,
this
.
clipBehavior
=
Clip
.
none
,
this
.
trailingIcon
,
required
this
.
child
,
});
/// Whether this checkbox is checked.
///
/// When [tristate] is true, a value of null corresponds to the mixed state.
/// When [tristate] is false, this value must not be null.
final
bool
?
value
;
/// If true, then the checkbox's [value] can be true, false, or null.
///
/// [CheckboxMenuButton] displays a dash when its value is null.
///
/// When a tri-state checkbox ([tristate] is true) is tapped, its [onChanged]
/// callback will be applied to true if the current value is false, to null if
/// value is true, and to false if value is null (i.e. it cycles through false
/// => true => null => false when tapped).
///
/// If tristate is false (the default), [value] must not be null.
final
bool
tristate
;
/// True if this checkbox wants to show an error state.
///
/// The checkbox will have different default container color and check color when
/// this is true. This is only used when [ThemeData.useMaterial3] is set to true.
///
/// Must not be null. Defaults to false.
final
bool
isError
;
/// Called when the value of the checkbox should change.
///
/// The checkbox passes the new value to the callback but does not actually
/// change state until the parent widget rebuilds the checkbox with the new
/// value.
///
/// If this callback is null, the menu item 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 and then back to false
/// again.
///
/// The callback provided to [onChanged] should update the state of the parent
/// [StatefulWidget] using the [State.setState] method, so that the parent
/// gets rebuilt; for example:
///
/// ```dart
/// CheckboxMenuButton(
/// value: _throwShotAway,
/// child: const Text('THROW'),
/// onChanged: (bool? newValue) {
/// setState(() {
/// _throwShotAway = newValue!;
/// });
/// },
/// )
/// ```
final
ValueChanged
<
bool
?>?
onChanged
;
/// Called when a pointer enters or exits the button response area.
///
/// The value passed to the callback is true if a pointer has entered button
/// area and false if a pointer has exited.
final
ValueChanged
<
bool
>?
onHover
;
/// Handler called when the focus changes.
///
/// Called with true if this widget's node gains focus, and false if it loses
/// focus.
final
ValueChanged
<
bool
>?
onFocusChange
;
/// {@macro flutter.widgets.Focus.focusNode}
final
FocusNode
?
focusNode
;
/// The optional shortcut that selects this [MenuItemButton].
///
/// {@macro flutter.material.menu_bar.shortcuts_note}
final
MenuSerializableShortcut
?
shortcut
;
/// Customizes this button's appearance.
///
/// Non-null properties of this style override the corresponding properties in
/// [MenuItemButton.themeStyleOf] and [MenuItemButton.defaultStyleOf].
/// [MaterialStateProperty]s that resolve to non-null values will similarly
/// override the corresponding [MaterialStateProperty]s in
/// [MenuItemButton.themeStyleOf] and [MenuItemButton.defaultStyleOf].
///
/// Null by default.
final
ButtonStyle
?
style
;
/// {@macro flutter.material.inkwell.statesController}
final
MaterialStatesController
?
statesController
;
/// {@macro flutter.material.Material.clipBehavior}
///
/// Defaults to [Clip.none].
final
Clip
clipBehavior
;
/// An optional icon to display after the [child] label.
final
Widget
?
trailingIcon
;
/// The widget displayed in the center of this button.
///
/// Typically this is the button's label, using a [Text] widget.
///
/// {@macro flutter.widgets.ProxyWidget.child}
final
Widget
?
child
;
/// Whether the button is enabled or disabled.
///
/// To enable a button, set its [onChanged] property to a non-null value.
bool
get
enabled
=>
onChanged
!=
null
;
@override
Widget
build
(
BuildContext
context
)
{
return
MenuItemButton
(
key:
key
,
onPressed:
onChanged
==
null
?
null
:
()
{
switch
(
value
)
{
case
false
:
onChanged
!.
call
(
true
);
break
;
case
true
:
onChanged
!.
call
(
tristate
?
null
:
false
);
break
;
case
null
:
onChanged
!.
call
(
false
);
break
;
}
},
onHover:
onHover
,
onFocusChange:
onFocusChange
,
focusNode:
focusNode
,
style:
style
,
shortcut:
shortcut
,
statesController:
statesController
,
leadingIcon:
ExcludeFocus
(
child:
IgnorePointer
(
child:
ConstrainedBox
(
constraints:
const
BoxConstraints
(
maxHeight:
Checkbox
.
width
,
maxWidth:
Checkbox
.
width
,
),
child:
Checkbox
(
tristate:
tristate
,
value:
value
,
onChanged:
onChanged
,
isError:
isError
,
),
),
),
),
clipBehavior:
clipBehavior
,
trailingIcon:
trailingIcon
,
child:
child
,
);
}
}
/// A menu item that combines a [Radio] widget with a [MenuItemButton].
///
/// To style the radio button separately from the overall button, add a
/// [RadioTheme] ancestor.
///
/// {@tool dartpad}
/// This example shows a menu with three radio buttons with shortcuts that
/// changes the background color of the body when the buttons are selected.
///
/// ** See code in examples/api/lib/material/menu_anchor/radio_menu_button.0.dart **
/// {@end-tool}
///
/// See also:
///
/// - [MenuBar], a widget that creates a menu bar of cascading menu items.
/// - [MenuAnchor], a widget that defines a region which can host a cascading
/// menu.
class
RadioMenuButton
<
T
>
extends
StatelessWidget
{
/// Creates a const [RadioMenuButton].
///
/// The [child] attribute is required.
const
RadioMenuButton
({
super
.
key
,
required
this
.
value
,
required
this
.
groupValue
,
required
this
.
onChanged
,
this
.
toggleable
=
false
,
this
.
onHover
,
this
.
onFocusChange
,
this
.
focusNode
,
this
.
shortcut
,
this
.
style
,
this
.
statesController
,
this
.
clipBehavior
=
Clip
.
none
,
this
.
trailingIcon
,
required
this
.
child
,
});
/// The value represented by this radio button.
///
/// This radio button is considered selected if its [value] matches the
/// [groupValue].
final
T
value
;
/// The currently selected value for a group of radio buttons.
///
/// This radio button is considered selected if its [value] matches the
/// [groupValue].
final
T
?
groupValue
;
/// Set to true if this radio button is allowed to be returned to an
/// indeterminate state by selecting it again when selected.
///
/// To indicate returning to an indeterminate state, [onChanged] will be
/// called with null.
///
/// If true, [onChanged] can be called with [value] when selected while
/// [groupValue] != [value], or with null when selected again while
/// [groupValue] == [value].
///
/// If false, [onChanged] will be called with [value] when it is selected
/// while [groupValue] != [value], and only by selecting another radio button
/// in the group (i.e. changing the value of [groupValue]) can this radio
/// button be unselected.
///
/// The default is false.
final
bool
toggleable
;
/// Called when the user selects this radio button.
///
/// The radio button passes [value] as a parameter to this callback. The radio
/// button does not actually change state until the parent widget rebuilds the
/// radio button with the new [groupValue].
///
/// If null, the radio button will be displayed as disabled.
///
/// The provided callback will not be invoked if this radio button is already
/// selected.
///
/// The callback provided to [onChanged] should update the state of the parent
/// [StatefulWidget] using the [State.setState] method, so that the parent
/// gets rebuilt; for example:
///
/// ```dart
/// RadioMenuButton<SingingCharacter>(
/// value: SingingCharacter.lafayette,
/// groupValue: _character,
/// onChanged: (SingingCharacter? newValue) {
/// setState(() {
/// _character = newValue;
/// });
/// },
/// child: const Text('Lafayette'),
/// )
/// ```
final
ValueChanged
<
T
?>?
onChanged
;
/// Called when a pointer enters or exits the button response area.
///
/// The value passed to the callback is true if a pointer has entered button
/// area and false if a pointer has exited.
final
ValueChanged
<
bool
>?
onHover
;
/// Handler called when the focus changes.
///
/// Called with true if this widget's node gains focus, and false if it loses
/// focus.
final
ValueChanged
<
bool
>?
onFocusChange
;
/// {@macro flutter.widgets.Focus.focusNode}
final
FocusNode
?
focusNode
;
/// The optional shortcut that selects this [MenuItemButton].
///
/// {@macro flutter.material.menu_bar.shortcuts_note}
final
MenuSerializableShortcut
?
shortcut
;
/// Customizes this button's appearance.
///
/// Non-null properties of this style override the corresponding properties in
/// [MenuItemButton.themeStyleOf] and [MenuItemButton.defaultStyleOf].
/// [MaterialStateProperty]s that resolve to non-null values will similarly
/// override the corresponding [MaterialStateProperty]s in
/// [MenuItemButton.themeStyleOf] and [MenuItemButton.defaultStyleOf].
///
/// Null by default.
final
ButtonStyle
?
style
;
/// {@macro flutter.material.inkwell.statesController}
final
MaterialStatesController
?
statesController
;
/// {@macro flutter.material.Material.clipBehavior}
///
/// Defaults to [Clip.none].
final
Clip
clipBehavior
;
/// An optional icon to display after the [child] label.
final
Widget
?
trailingIcon
;
/// The widget displayed in the center of this button.
///
/// Typically this is the button's label, using a [Text] widget.
///
/// {@macro flutter.widgets.ProxyWidget.child}
final
Widget
?
child
;
/// Whether the button is enabled or disabled.
///
/// To enable a button, set its [onChanged] property to a non-null value.
bool
get
enabled
=>
onChanged
!=
null
;
@override
Widget
build
(
BuildContext
context
)
{
return
MenuItemButton
(
key:
key
,
onPressed:
onChanged
==
null
?
null
:
()
{
if
(
toggleable
&&
groupValue
==
value
)
{
onChanged
!.
call
(
null
);
return
;
}
onChanged
!.
call
(
value
);
},
onHover:
onHover
,
onFocusChange:
onFocusChange
,
focusNode:
focusNode
,
style:
style
,
shortcut:
shortcut
,
statesController:
statesController
,
leadingIcon:
ExcludeFocus
(
child:
IgnorePointer
(
child:
ConstrainedBox
(
constraints:
const
BoxConstraints
(
maxHeight:
Checkbox
.
width
,
maxWidth:
Checkbox
.
width
,
),
child:
Radio
<
T
>(
value:
value
,
groupValue:
groupValue
,
onChanged:
onChanged
,
toggleable:
toggleable
,
),
),
),
),
clipBehavior:
clipBehavior
,
trailingIcon:
trailingIcon
,
child:
child
,
);
}
}
/// A menu button that displays a cascading menu.
/// A menu button that displays a cascading menu.
///
///
/// It can be used as part of a [MenuBar], or as a standalone widget.
/// It can be used as part of a [MenuBar], or as a standalone widget.
...
...
packages/flutter/test/material/checkbox_list_tile_test.dart
View file @
6b32c069
...
@@ -148,7 +148,7 @@ void main() {
...
@@ -148,7 +148,7 @@ void main() {
final
Rect
tallerWidget
=
checkboxRect
.
height
>
titleRect
.
height
?
checkboxRect
:
titleRect
;
final
Rect
tallerWidget
=
checkboxRect
.
height
>
titleRect
.
height
?
checkboxRect
:
titleRect
;
// Check the offsets of Check
B
ox and title after padding is applied.
// Check the offsets of Check
b
ox and title after padding is applied.
expect
(
paddingRect
.
right
,
checkboxRect
.
right
+
4
);
expect
(
paddingRect
.
right
,
checkboxRect
.
right
+
4
);
expect
(
paddingRect
.
left
,
titleRect
.
left
-
10
);
expect
(
paddingRect
.
left
,
titleRect
.
left
-
10
);
...
...
packages/flutter/test/material/checkbox_test.dart
View file @
6b32c069
...
@@ -60,7 +60,7 @@ void main() {
...
@@ -60,7 +60,7 @@ void main() {
expect
(
tester
.
getSize
(
find
.
byType
(
Checkbox
)),
const
Size
(
40.0
,
40.0
));
expect
(
tester
.
getSize
(
find
.
byType
(
Checkbox
)),
const
Size
(
40.0
,
40.0
));
});
});
testWidgets
(
'Check
B
ox semantics'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Check
b
ox semantics'
,
(
WidgetTester
tester
)
async
{
final
SemanticsHandle
handle
=
tester
.
ensureSemantics
();
final
SemanticsHandle
handle
=
tester
.
ensureSemantics
();
await
tester
.
pumpWidget
(
Theme
(
await
tester
.
pumpWidget
(
Theme
(
...
@@ -193,7 +193,7 @@ void main() {
...
@@ -193,7 +193,7 @@ void main() {
handle
.
dispose
();
handle
.
dispose
();
});
});
testWidgets
(
'Can wrap Check
B
ox with Semantics'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Can wrap Check
b
ox with Semantics'
,
(
WidgetTester
tester
)
async
{
final
SemanticsHandle
handle
=
tester
.
ensureSemantics
();
final
SemanticsHandle
handle
=
tester
.
ensureSemantics
();
await
tester
.
pumpWidget
(
Theme
(
await
tester
.
pumpWidget
(
Theme
(
...
@@ -222,7 +222,7 @@ void main() {
...
@@ -222,7 +222,7 @@ void main() {
handle
.
dispose
();
handle
.
dispose
();
});
});
testWidgets
(
'Check
B
ox tristate: true'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Check
b
ox tristate: true'
,
(
WidgetTester
tester
)
async
{
bool
?
checkBoxValue
;
bool
?
checkBoxValue
;
await
tester
.
pumpWidget
(
await
tester
.
pumpWidget
(
...
@@ -388,7 +388,7 @@ void main() {
...
@@ -388,7 +388,7 @@ void main() {
semanticsTester
.
dispose
();
semanticsTester
.
dispose
();
});
});
testWidgets
(
'Check
B
ox tristate rendering, programmatic transitions'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Check
b
ox tristate rendering, programmatic transitions'
,
(
WidgetTester
tester
)
async
{
Widget
buildFrame
(
bool
?
checkboxValue
)
{
Widget
buildFrame
(
bool
?
checkboxValue
)
{
return
Theme
(
return
Theme
(
data:
theme
,
data:
theme
,
...
@@ -439,7 +439,7 @@ void main() {
...
@@ -439,7 +439,7 @@ void main() {
expect
(
getCheckboxRenderer
(),
paints
..
line
());
// null is rendered as a line (a "dash")
expect
(
getCheckboxRenderer
(),
paints
..
line
());
// null is rendered as a line (a "dash")
});
});
testWidgets
(
'Check
B
ox color rendering'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'Check
b
ox color rendering'
,
(
WidgetTester
tester
)
async
{
const
Color
borderColor
=
Color
(
0xff2196f3
);
const
Color
borderColor
=
Color
(
0xff2196f3
);
Color
checkColor
=
const
Color
(
0xffFFFFFF
);
Color
checkColor
=
const
Color
(
0xffFFFFFF
);
Color
activeColor
;
Color
activeColor
;
...
...
packages/flutter/test/material/menu_anchor_test.dart
View file @
6b32c069
...
@@ -1642,6 +1642,129 @@ void main() {
...
@@ -1642,6 +1642,129 @@ void main() {
expect
(
find
.
text
(
charExpected
),
findsOneWidget
);
expect
(
find
.
text
(
charExpected
),
findsOneWidget
);
},
variant:
TargetPlatformVariant
.
all
());
},
variant:
TargetPlatformVariant
.
all
());
});
});
group
(
'CheckboxMenuButton'
,
()
{
testWidgets
(
'tapping toggles checkbox'
,
(
WidgetTester
tester
)
async
{
bool
?
checkBoxValue
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
MenuBar
(
children:
<
Widget
>[
SubmenuButton
(
menuChildren:
<
Widget
>[
CheckboxMenuButton
(
value:
checkBoxValue
,
onChanged:
(
bool
?
value
)
{
setState
(()
{
checkBoxValue
=
value
;
});
},
tristate:
true
,
child:
const
Text
(
'checkbox'
),
)
],
child:
const
Text
(
'submenu'
),
),
],
);
},
),
),
);
await
tester
.
tap
(
find
.
byType
(
SubmenuButton
));
await
tester
.
pump
();
expect
(
tester
.
widget
<
CheckboxMenuButton
>(
find
.
byType
(
CheckboxMenuButton
)).
value
,
null
);
await
tester
.
tap
(
find
.
byType
(
CheckboxMenuButton
));
await
tester
.
pumpAndSettle
();
expect
(
checkBoxValue
,
false
);
await
tester
.
tap
(
find
.
byType
(
SubmenuButton
));
await
tester
.
pump
();
await
tester
.
tap
(
find
.
byType
(
CheckboxMenuButton
));
await
tester
.
pumpAndSettle
();
expect
(
checkBoxValue
,
true
);
await
tester
.
tap
(
find
.
byType
(
SubmenuButton
));
await
tester
.
pump
();
await
tester
.
tap
(
find
.
byType
(
CheckboxMenuButton
));
await
tester
.
pumpAndSettle
();
expect
(
checkBoxValue
,
null
);
});
});
group
(
'RadioMenuButton'
,
()
{
testWidgets
(
'tapping toggles radio button'
,
(
WidgetTester
tester
)
async
{
int
?
radioValue
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
MenuBar
(
children:
<
Widget
>[
SubmenuButton
(
menuChildren:
<
Widget
>[
RadioMenuButton
<
int
>(
value:
0
,
groupValue:
radioValue
,
onChanged:
(
int
?
value
)
{
setState
(()
{
radioValue
=
value
;
});
},
toggleable:
true
,
child:
const
Text
(
'radio 0'
),
),
RadioMenuButton
<
int
>(
value:
1
,
groupValue:
radioValue
,
onChanged:
(
int
?
value
)
{
setState
(()
{
radioValue
=
value
;
});
},
toggleable:
true
,
child:
const
Text
(
'radio 1'
),
)
],
child:
const
Text
(
'submenu'
),
),
],
);
},
),
),
);
await
tester
.
tap
(
find
.
byType
(
SubmenuButton
));
await
tester
.
pump
();
expect
(
tester
.
widget
<
RadioMenuButton
<
int
>>(
find
.
byType
(
RadioMenuButton
<
int
>).
first
).
groupValue
,
null
,
);
await
tester
.
tap
(
find
.
byType
(
RadioMenuButton
<
int
>).
first
);
await
tester
.
pumpAndSettle
();
expect
(
radioValue
,
0
);
await
tester
.
tap
(
find
.
byType
(
SubmenuButton
));
await
tester
.
pump
();
await
tester
.
tap
(
find
.
byType
(
RadioMenuButton
<
int
>).
first
);
await
tester
.
pumpAndSettle
();
expect
(
radioValue
,
null
);
await
tester
.
tap
(
find
.
byType
(
SubmenuButton
));
await
tester
.
pump
();
await
tester
.
tap
(
find
.
byType
(
RadioMenuButton
<
int
>).
last
);
await
tester
.
pumpAndSettle
();
expect
(
radioValue
,
1
);
});
});
}
}
List
<
Widget
>
createTestMenus
({
List
<
Widget
>
createTestMenus
({
...
...
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