mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[A11y] TextField prefix icon and suffix icon create a sibling node' (#173312)
prefix/suffix icon should have their own merge group. Noted a textfield can have suffix and suffix icon the same time so they should be different merge group. Co-authored-by: chunhtai <47866232+chunhtai@users.noreply.github.com>
This commit is contained in:
parent
052b903e8e
commit
cb44b5a386
@ -13,6 +13,7 @@ library;
|
||||
import 'dart:math' as math;
|
||||
import 'dart:ui' show lerpDouble;
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
@ -1685,25 +1686,28 @@ class _RenderDecoration extends RenderBox
|
||||
) {
|
||||
final ChildSemanticsConfigurationsResultBuilder builder =
|
||||
ChildSemanticsConfigurationsResultBuilder();
|
||||
List<SemanticsConfiguration>? prefixMergeGroup;
|
||||
List<SemanticsConfiguration>? suffixMergeGroup;
|
||||
|
||||
final Map<SemanticsTag, List<SemanticsConfiguration>> mergeGroups =
|
||||
<SemanticsTag, List<SemanticsConfiguration>>{};
|
||||
final Set<SemanticsTag> tags = <SemanticsTag>{
|
||||
_InputDecoratorState._kPrefixSemanticsTag,
|
||||
_InputDecoratorState._kPrefixIconSemanticsTag,
|
||||
_InputDecoratorState._kSuffixSemanticsTag,
|
||||
_InputDecoratorState._kSuffixIconSemanticsTag,
|
||||
};
|
||||
|
||||
for (final SemanticsConfiguration childConfig in childConfigs) {
|
||||
if (childConfig.tagsChildrenWith(_InputDecoratorState._kPrefixSemanticsTag)) {
|
||||
prefixMergeGroup ??= <SemanticsConfiguration>[];
|
||||
prefixMergeGroup.add(childConfig);
|
||||
} else if (childConfig.tagsChildrenWith(_InputDecoratorState._kSuffixSemanticsTag)) {
|
||||
suffixMergeGroup ??= <SemanticsConfiguration>[];
|
||||
suffixMergeGroup.add(childConfig);
|
||||
final SemanticsTag? tag = tags.firstWhereOrNull(
|
||||
(SemanticsTag tag) => childConfig.tagsChildrenWith(tag),
|
||||
);
|
||||
if (tag != null) {
|
||||
mergeGroups.putIfAbsent(tag, () => <SemanticsConfiguration>[]).add(childConfig);
|
||||
} else {
|
||||
builder.markAsMergeUp(childConfig);
|
||||
}
|
||||
}
|
||||
if (prefixMergeGroup != null) {
|
||||
builder.markAsSiblingMergeGroup(prefixMergeGroup);
|
||||
}
|
||||
if (suffixMergeGroup != null) {
|
||||
builder.markAsSiblingMergeGroup(suffixMergeGroup);
|
||||
}
|
||||
|
||||
mergeGroups.values.forEach(builder.markAsSiblingMergeGroup);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@ -1985,7 +1989,13 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
name: hashCode.toString(),
|
||||
);
|
||||
static const SemanticsTag _kPrefixSemanticsTag = SemanticsTag('_InputDecoratorState.prefix');
|
||||
static const SemanticsTag _kPrefixIconSemanticsTag = SemanticsTag(
|
||||
'_InputDecoratorState.prefixIcon',
|
||||
);
|
||||
static const SemanticsTag _kSuffixSemanticsTag = SemanticsTag('_InputDecoratorState.suffix');
|
||||
static const SemanticsTag _kSuffixIconSemanticsTag = SemanticsTag(
|
||||
'_InputDecoratorState.suffixIcon',
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -2466,7 +2476,10 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
iconSize: WidgetStatePropertyAll<double>(iconSize),
|
||||
).merge(iconButtonTheme.style),
|
||||
),
|
||||
child: Semantics(child: decoration.prefixIcon),
|
||||
child: Semantics(
|
||||
tagForChildren: _kPrefixIconSemanticsTag,
|
||||
child: decoration.prefixIcon,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -2503,7 +2516,10 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
iconSize: WidgetStatePropertyAll<double>(iconSize),
|
||||
).merge(iconButtonTheme.style),
|
||||
),
|
||||
child: Semantics(child: decoration.suffixIcon),
|
||||
child: Semantics(
|
||||
tagForChildren: _kSuffixIconSemanticsTag,
|
||||
child: decoration.suffixIcon,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -5675,6 +5675,48 @@ void main() {
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
testWidgets('TextField prefix icon and suffix icon create a sibling node', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final SemanticsTester semantics = SemanticsTester(tester);
|
||||
await tester.pumpWidget(
|
||||
overlay(
|
||||
child: TextField(
|
||||
controller: _textEditingController(text: 'some text'),
|
||||
decoration: const InputDecoration(prefixIcon: Text('Prefix'), suffixIcon: Text('Suffix')),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
semantics,
|
||||
hasSemantics(
|
||||
TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
TestSemantics.rootChild(
|
||||
id: 1,
|
||||
textDirection: TextDirection.ltr,
|
||||
value: 'some text',
|
||||
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
|
||||
inputType: ui.SemanticsInputType.text,
|
||||
currentValueLength: 9,
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.isTextField,
|
||||
SemanticsFlag.hasEnabledState,
|
||||
SemanticsFlag.isEnabled,
|
||||
],
|
||||
),
|
||||
TestSemantics.rootChild(id: 2, textDirection: TextDirection.ltr, label: 'Prefix'),
|
||||
TestSemantics.rootChild(id: 3, textDirection: TextDirection.ltr, label: 'Suffix'),
|
||||
],
|
||||
),
|
||||
ignoreTransform: true,
|
||||
ignoreRect: true,
|
||||
),
|
||||
);
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
testWidgets('TextField with specified suffixStyle', (WidgetTester tester) async {
|
||||
final TextStyle suffixStyle = TextStyle(color: Colors.pink[500], fontSize: 10.0);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user