Unverified Commit ee396272 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Fix dropdown button semantics (#19932)

parent a7595e53
...@@ -139,6 +139,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> { ...@@ -139,6 +139,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
// //
// When the menu is dismissed we just fade the entire thing out // When the menu is dismissed we just fade the entire thing out
// in the first 0.25s. // in the first 0.25s.
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
final _DropdownRoute<T> route = widget.route; final _DropdownRoute<T> route = widget.route;
final double unit = 0.5 / (route.items.length + 1.5); final double unit = 0.5 / (route.items.length + 1.5);
final List<Widget> children = <Widget>[]; final List<Widget> children = <Widget>[];
...@@ -175,24 +176,30 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> { ...@@ -175,24 +176,30 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
selectedIndex: route.selectedIndex, selectedIndex: route.selectedIndex,
resize: _resize, resize: _resize,
), ),
child: new Material( child: new Semantics(
type: MaterialType.transparency, scopesRoute: true,
textStyle: route.style, namesRoute: true,
child: new ScrollConfiguration( explicitChildNodes: true,
behavior: const _DropdownScrollBehavior(), label: localizations.popupMenuLabel,
child: new Scrollbar( child: new Material(
child: new ListView( type: MaterialType.transparency,
controller: widget.route.scrollController, textStyle: route.style,
padding: kMaterialListPadding, child: new ScrollConfiguration(
itemExtent: _kMenuItemHeight, behavior: const _DropdownScrollBehavior(),
shrinkWrap: true, child: new Scrollbar(
children: children, child: new ListView(
controller: widget.route.scrollController,
padding: kMaterialListPadding,
itemExtent: _kMenuItemHeight,
shrinkWrap: true,
children: children,
),
),
), ),
), ),
), ),
), ),
), );
);
} }
} }
...@@ -627,6 +634,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi ...@@ -627,6 +634,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
style: _textStyle.copyWith(color: Theme.of(context).hintColor), style: _textStyle.copyWith(color: Theme.of(context).hintColor),
child: new IgnorePointer( child: new IgnorePointer(
child: widget.hint, child: widget.hint,
ignoringSemantics: false,
), ),
)); ));
} }
...@@ -681,10 +689,13 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi ...@@ -681,10 +689,13 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
); );
} }
return new GestureDetector( return new Semantics(
onTap: _handleTap, button: true,
behavior: HitTestBehavior.opaque, child: new GestureDetector(
child: result onTap: _handleTap,
behavior: HitTestBehavior.opaque,
child: result
),
); );
} }
} }
...@@ -365,7 +365,7 @@ class SemanticsData extends Diagnosticable { ...@@ -365,7 +365,7 @@ class SemanticsData extends Diagnosticable {
scrollExtentMax, scrollExtentMax,
scrollExtentMin, scrollExtentMin,
transform, transform,
customSemanticsActionIds, ui.hashList(customSemanticsActionIds),
); );
} }
......
...@@ -580,4 +580,102 @@ void main() { ...@@ -580,4 +580,102 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('Dropdown button includes semantics', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
const Key key = const Key('test');
await tester.pumpWidget(buildFrame(
buttonKey: key,
value: null,
items: menuItems,
onChanged: (String _) {},
hint: const Text('test'),
));
// By default the hint contributes the label.
expect(tester.getSemanticsData(find.byKey(key)), matchesSemanticsData(
isButton: true,
label: 'test',
hasTapAction: true,
));
await tester.pumpWidget(buildFrame(
buttonKey: key,
value: 'three',
items: menuItems,
onChanged: null,
hint: const Text('test'),
));
// Displays label of select item and is no longer tappable.
expect(tester.getSemanticsData(find.byKey(key)), matchesSemanticsData(
isButton: true,
label: 'three',
hasTapAction: true,
));
handle.dispose();
});
testWidgets('Dropdown menu includes semantics', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
const Key key = const Key('test');
await tester.pumpWidget(buildFrame(
buttonKey: key,
value: null,
items: menuItems,
));
await tester.tap(find.byKey(key));
await tester.pumpAndSettle();
expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
children: <TestSemantics>[
new TestSemantics(
flags: <SemanticsFlag>[
SemanticsFlag.scopesRoute,
SemanticsFlag.namesRoute,
],
label: 'Popup menu',
children: <TestSemantics>[
new TestSemantics(
children: <TestSemantics>[
new TestSemantics(
children: <TestSemantics>[
new TestSemantics(
label: 'one',
textDirection: TextDirection.ltr,
tags: <SemanticsTag>[const SemanticsTag('RenderViewport.twoPane')],
actions: <SemanticsAction>[SemanticsAction.tap],
),
new TestSemantics(
label: 'two',
textDirection: TextDirection.ltr,
tags: <SemanticsTag>[const SemanticsTag('RenderViewport.twoPane')],
actions: <SemanticsAction>[SemanticsAction.tap],
),
new TestSemantics(
label: 'three',
textDirection: TextDirection.ltr,
tags: <SemanticsTag>[const SemanticsTag('RenderViewport.twoPane')],
actions: <SemanticsAction>[SemanticsAction.tap],
),
new TestSemantics(
label: 'four',
textDirection: TextDirection.ltr,
tags: <SemanticsTag>[const SemanticsTag('RenderViewport.twoPane')],
actions: <SemanticsAction>[SemanticsAction.tap],
),
],
),
],
),
],
),
],
),
],
), ignoreId: true, ignoreRect: true, ignoreTransform: true));
semantics.dispose();
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment