Unverified Commit 9d38db42 authored by chunhtai's avatar chunhtai Committed by GitHub

fix ExpansionPanelList merge the header semantics when it is not necessart (#33808)

parent 00d95e62
...@@ -7,6 +7,7 @@ import 'package:flutter/widgets.dart'; ...@@ -7,6 +7,7 @@ import 'package:flutter/widgets.dart';
import 'expand_icon.dart'; import 'expand_icon.dart';
import 'ink_well.dart'; import 'ink_well.dart';
import 'material_localizations.dart';
import 'mergeable_material.dart'; import 'mergeable_material.dart';
import 'theme.dart'; import 'theme.dart';
...@@ -446,7 +447,26 @@ class _ExpansionPanelListState extends State<ExpansionPanelList> { ...@@ -446,7 +447,26 @@ class _ExpansionPanelListState extends State<ExpansionPanelList> {
context, context,
_isChildExpanded(index), _isChildExpanded(index),
); );
final Row header = Row(
Widget expandIconContainer = Container(
margin: const EdgeInsetsDirectional.only(end: 8.0),
child: ExpandIcon(
isExpanded: _isChildExpanded(index),
padding: const EdgeInsets.all(16.0),
onPressed: !child.canTapOnHeader
? (bool isExpanded) => _handlePressed(isExpanded, index)
: null,
),
);
if (!child.canTapOnHeader) {
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
expandIconContainer = Semantics(
label: _isChildExpanded(index)? localizations.expandedIconTapHint : localizations.collapsedIconTapHint,
container: true,
child: expandIconContainer
);
}
Widget header = Row(
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: AnimatedContainer( child: AnimatedContainer(
...@@ -459,32 +479,23 @@ class _ExpansionPanelListState extends State<ExpansionPanelList> { ...@@ -459,32 +479,23 @@ class _ExpansionPanelListState extends State<ExpansionPanelList> {
), ),
), ),
), ),
Container( expandIconContainer,
margin: const EdgeInsetsDirectional.only(end: 8.0),
child: ExpandIcon(
isExpanded: _isChildExpanded(index),
padding: const EdgeInsets.all(16.0),
onPressed: !child.canTapOnHeader
? (bool isExpanded) => _handlePressed(isExpanded, index)
: null,
),
),
], ],
); );
if (child.canTapOnHeader) {
header = MergeSemantics(
child: InkWell(
onTap: () => _handlePressed(_isChildExpanded(index), index),
child: header,
)
);
}
items.add( items.add(
MaterialSlice( MaterialSlice(
key: _SaltedKey<BuildContext, int>(context, index * 2), key: _SaltedKey<BuildContext, int>(context, index * 2),
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
MergeSemantics( header,
child: child.canTapOnHeader
? InkWell(
onTap: () => _handlePressed(_isChildExpanded(index), index),
child: header,
)
: header,
),
AnimatedCrossFade( AnimatedCrossFade(
firstChild: Container(height: 0.0), firstChild: Container(height: 0.0),
secondChild: child.body, secondChild: child.body,
......
...@@ -54,6 +54,49 @@ class _SimpleExpansionPanelListTestWidgetState extends State<SimpleExpansionPane ...@@ -54,6 +54,49 @@ class _SimpleExpansionPanelListTestWidgetState extends State<SimpleExpansionPane
} }
} }
class ExpansionPanelListSemanticsTest extends StatefulWidget {
const ExpansionPanelListSemanticsTest({this.headerKey});
final Key headerKey;
@override
ExpansionPanelListSemanticsTestState createState() => ExpansionPanelListSemanticsTestState();
}
class ExpansionPanelListSemanticsTestState extends State<ExpansionPanelListSemanticsTest> {
bool headerTapped = false;
@override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
ExpansionPanelList(
children: <ExpansionPanel>[
ExpansionPanel(
canTapOnHeader: false,
headerBuilder: (BuildContext context, bool isExpanded) {
return MergeSemantics(
key: widget.headerKey,
child: GestureDetector(
onTap: () => headerTapped = true,
child: const Text.rich(
TextSpan(
text:'head1',
),
),
),
);
},
body: Container(
child: const Placeholder(),
),
),
],
),
],
);
}
}
void main() { void main() {
testWidgets('ExpansionPanelList test', (WidgetTester tester) async { testWidgets('ExpansionPanelList test', (WidgetTester tester) async {
int index; int index;
...@@ -91,7 +134,7 @@ void main() { ...@@ -91,7 +134,7 @@ void main() {
box = tester.renderObject(find.byType(ExpansionPanelList)); box = tester.renderObject(find.byType(ExpansionPanelList));
expect(box.size.height, equals(oldHeight)); expect(box.size.height, equals(oldHeight));
// now expand the child panel // Now, expand the child panel.
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: SingleChildScrollView( home: SingleChildScrollView(
...@@ -121,6 +164,52 @@ void main() { ...@@ -121,6 +164,52 @@ void main() {
expect(box.size.height - oldHeight, greaterThanOrEqualTo(100.0)); // 100 + some margin expect(box.size.height - oldHeight, greaterThanOrEqualTo(100.0)); // 100 + some margin
}); });
testWidgets('ExpansionPanelList does not merge header when canTapOnHeader is false', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
final Key headerKey = UniqueKey();
await tester.pumpWidget(
MaterialApp(
home: ExpansionPanelListSemanticsTest(headerKey: headerKey),
),
);
// Make sure custom gesture detector widget is clickable.
await tester.tap(find.text('head1'));
await tester.pump();
final ExpansionPanelListSemanticsTestState state =
tester.state(find.byType(ExpansionPanelListSemanticsTest));
expect(state.headerTapped, true);
// Check the expansion icon semantics does not merged with header widget.
final Finder expansionIcon = find.descendant(
of: find.ancestor(
of: find.byKey(headerKey),
matching: find.byType(Row),
),
matching: find.byType(ExpandIcon),
);
expect(tester.getSemantics(expansionIcon), matchesSemantics(
label: 'Expand',
isButton: true,
hasEnabledState: true,
isEnabled: true,
hasTapAction: true,
));
// Check custom header widget semantics is preserved.
final Finder headerWidget = find.descendant(
of: find.byKey(headerKey),
matching: find.byType(RichText),
);
expect(tester.getSemantics(headerWidget), matchesSemantics(
label: 'head1',
hasTapAction: true,
));
handle.dispose();
});
testWidgets('Multiple Panel List test', (WidgetTester tester) async { testWidgets('Multiple Panel List test', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -798,7 +887,7 @@ void main() { ...@@ -798,7 +887,7 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
}); });
testWidgets('Panel header has semantics', (WidgetTester tester) async { testWidgets('Panel header has semantics, canTapOnHeader = false ', (WidgetTester tester) async {
const Key expandedKey = Key('expanded'); const Key expandedKey = Key('expanded');
const Key collapsedKey = Key('collapsed'); const Key collapsedKey = Key('collapsed');
const DefaultMaterialLocalizations localizations = DefaultMaterialLocalizations(); const DefaultMaterialLocalizations localizations = DefaultMaterialLocalizations();
...@@ -832,8 +921,16 @@ void main() { ...@@ -832,8 +921,16 @@ void main() {
), ),
); );
expect(tester.getSemantics(find.byKey(expandedKey)), matchesSemantics( // Check the semantics of [ExpanIcon] for expanded panel.
label: 'Expanded', final Finder expandedIcon = find.descendant(
of: find.ancestor(
of: find.byKey(expandedKey),
matching: find.byType(Row),
),
matching: find.byType(ExpandIcon),
);
expect(tester.getSemantics(expandedIcon), matchesSemantics(
label: 'Collapse',
isButton: true, isButton: true,
hasEnabledState: true, hasEnabledState: true,
isEnabled: true, isEnabled: true,
...@@ -841,8 +938,22 @@ void main() { ...@@ -841,8 +938,22 @@ void main() {
onTapHint: localizations.expandedIconTapHint, onTapHint: localizations.expandedIconTapHint,
)); ));
expect(tester.getSemantics(find.byKey(collapsedKey)), matchesSemantics( // Check the semantics of the header widget for expanded panel.
label: 'Collapsed', final Finder expandedHeader = find.byKey(expandedKey);
expect(tester.getSemantics(expandedHeader), matchesSemantics(
label: 'Expanded',
));
// Check the semantics of [ExpanIcon] for collapsed panel.
final Finder collapsedIcon = find.descendant(
of: find.ancestor(
of: find.byKey(collapsedKey),
matching: find.byType(Row),
),
matching: find.byType(ExpandIcon),
);
expect(tester.getSemantics(collapsedIcon), matchesSemantics(
label: 'Expand',
isButton: true, isButton: true,
hasEnabledState: true, hasEnabledState: true,
isEnabled: true, isEnabled: true,
...@@ -850,6 +961,64 @@ void main() { ...@@ -850,6 +961,64 @@ void main() {
onTapHint: localizations.collapsedIconTapHint, onTapHint: localizations.collapsedIconTapHint,
)); ));
// Check the semantics of the header widget for expanded panel.
final Finder collapsedHeader = find.byKey(collapsedKey);
expect(tester.getSemantics(collapsedHeader), matchesSemantics(
label: 'Collapsed',
));
handle.dispose();
});
testWidgets('Panel header has semantics, canTapOnHeader = true', (WidgetTester tester) async {
const Key expandedKey = Key('expanded');
const Key collapsedKey = Key('collapsed');
final SemanticsHandle handle = tester.ensureSemantics();
final List<ExpansionPanel> _demoItems = <ExpansionPanel>[
ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return const Text('Expanded', key: expandedKey);
},
canTapOnHeader: true,
body: const SizedBox(height: 100.0),
isExpanded: true,
),
ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return const Text('Collapsed', key: collapsedKey);
},
canTapOnHeader: true,
body: const SizedBox(height: 100.0),
isExpanded: false,
),
];
final ExpansionPanelList _expansionList = ExpansionPanelList(
children: _demoItems,
);
await tester.pumpWidget(
MaterialApp(
home: SingleChildScrollView(
child: _expansionList,
),
),
);
expect(tester.getSemantics(find.byKey(expandedKey)), matchesSemantics(
label: 'Expanded',
isButton: true,
hasEnabledState: true,
hasTapAction: true,
));
expect(tester.getSemantics(find.byKey(collapsedKey)), matchesSemantics(
label: 'Collapsed',
isButton: true,
hasEnabledState: true,
hasTapAction: true,
));
handle.dispose(); handle.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