Unverified Commit 4373a319 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Reapply "Revert "Propagate textfield character limits to semantics (#40468)" (#40767)

parent f19f040a
......@@ -64,7 +64,11 @@ void main() {
});
test('TextField has correct Android semantics', () async {
final SerializableFinder normalTextField = find.byValueKey(normalTextFieldKeyValue);
final SerializableFinder normalTextField = find.descendant(
of: find.byValueKey(normalTextFieldKeyValue),
matching: find.byType('Semantics'),
firstMatchOnly: true,
);
expect(await getSemantics(normalTextField), hasAndroidSemantics(
className: AndroidClassName.editText,
isEditable: true,
......@@ -112,7 +116,11 @@ void main() {
});
test('password TextField has correct Android semantics', () async {
final SerializableFinder passwordTextField = find.byValueKey(passwordTextFieldKeyValue);
final SerializableFinder passwordTextField = find.descendant(
of: find.byValueKey(passwordTextFieldKeyValue),
matching: find.byType('Semantics'),
firstMatchOnly: true,
);
expect(await getSemantics(passwordTextField), hasAndroidSemantics(
className: AndroidClassName.editText,
isEditable: true,
......
......@@ -753,6 +753,8 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
bool get _isEnabled => widget.enabled ?? widget.decoration?.enabled ?? true;
int get _currentLength => _effectiveController.value.text.runes.length;
InputDecoration _getEffectiveDecoration() {
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
final ThemeData themeData = Theme.of(context);
......@@ -769,7 +771,7 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
// If buildCounter was provided, use it to generate a counter widget.
Widget counter;
final int currentLength = _effectiveController.value.text.runes.length;
final int currentLength = _currentLength;
if (effectiveDecoration.counter == null
&& effectiveDecoration.counterText == null
&& widget.buildCounter != null) {
......@@ -1099,18 +1101,27 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
child: child,
);
}
return Semantics(
onTap: () {
if (!_effectiveController.selection.isValid)
_effectiveController.selection = TextSelection.collapsed(offset: _effectiveController.text.length);
_requestKeyboard();
},
return IgnorePointer(
ignoring: !_isEnabled,
child: MouseRegion(
onEnter: _handleMouseEnter,
onExit: _handleMouseExit,
child: IgnorePointer(
ignoring: !_isEnabled,
child: AnimatedBuilder(
animation: controller, // changes the _currentLength
builder: (BuildContext context, Widget child) {
return Semantics(
maxValueLength: widget.maxLengthEnforced && widget.maxLength != null && widget.maxLength > 0
? widget.maxLength
: null,
currentValueLength: _currentLength,
onTap: () {
if (!_effectiveController.selection.isValid)
_effectiveController.selection = TextSelection.collapsed(offset: _effectiveController.text.length);
_requestKeyboard();
},
child: child,
);
},
child: _selectionGestureDetectorBuilder.buildGestureDetector(
behavior: HitTestBehavior.translucent,
child: child,
......
......@@ -864,6 +864,12 @@ class RenderCustomPaint extends RenderProxyBox {
if (properties.liveRegion != null) {
config.liveRegion = properties.liveRegion;
}
if (properties.maxValueLength != null) {
config.maxValueLength = properties.maxValueLength;
}
if (properties.currentValueLength != null) {
config.currentValueLength = properties.currentValueLength;
}
if (properties.toggled != null) {
config.isToggled = properties.toggled;
}
......
......@@ -3497,6 +3497,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
bool hidden,
bool image,
bool liveRegion,
int maxValueLength,
int currentValueLength,
String label,
String value,
String increasedValue,
......@@ -3544,6 +3546,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
_scopesRoute = scopesRoute,
_namesRoute = namesRoute,
_liveRegion = liveRegion,
_maxValueLength = maxValueLength,
_currentValueLength = currentValueLength,
_hidden = hidden,
_image = image,
_onDismiss = onDismiss,
......@@ -3799,6 +3803,28 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
markNeedsSemanticsUpdate();
}
/// If non-null, sets the [SemanticsNode.maxValueLength] semantic to the given
/// value.
int get maxValueLength => _maxValueLength;
int _maxValueLength;
set maxValueLength(int value) {
if (_maxValueLength == value)
return;
_maxValueLength = value;
markNeedsSemanticsUpdate();
}
/// If non-null, sets the [SemanticsNode.currentValueLength] semantic to the
/// given value.
int get currentValueLength => _currentValueLength;
int _currentValueLength;
set currentValueLength(int value) {
if (_currentValueLength == value)
return;
_currentValueLength = value;
markNeedsSemanticsUpdate();
}
/// If non-null, sets the [SemanticsNode.isToggled] semantic to the given
/// value.
bool get toggled => _toggled;
......@@ -4370,6 +4396,12 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
config.namesRoute = namesRoute;
if (liveRegion != null)
config.liveRegion = liveRegion;
if (maxValueLength != null) {
config.maxValueLength = maxValueLength;
}
if (currentValueLength != null) {
config.currentValueLength = currentValueLength;
}
if (textDirection != null)
config.textDirection = textDirection;
if (sortKey != null)
......
......@@ -6196,6 +6196,8 @@ class Semantics extends SingleChildRenderObjectWidget {
bool hidden,
bool image,
bool liveRegion,
int maxValueLength,
int currentValueLength,
String label,
String value,
String increasedValue,
......@@ -6247,6 +6249,8 @@ class Semantics extends SingleChildRenderObjectWidget {
hidden: hidden,
image: image,
liveRegion: liveRegion,
maxValueLength: maxValueLength,
currentValueLength: currentValueLength,
label: label,
value: value,
increasedValue: increasedValue,
......@@ -6350,6 +6354,8 @@ class Semantics extends SingleChildRenderObjectWidget {
readOnly: properties.readOnly,
focused: properties.focused,
liveRegion: properties.liveRegion,
maxValueLength: properties.maxValueLength,
currentValueLength: properties.currentValueLength,
inMutuallyExclusiveGroup: properties.inMutuallyExclusiveGroup,
obscured: properties.obscured,
multiline: properties.multiline,
......@@ -6422,6 +6428,8 @@ class Semantics extends SingleChildRenderObjectWidget {
..hidden = properties.hidden
..image = properties.image
..liveRegion = properties.liveRegion
..maxValueLength = properties.maxValueLength
..currentValueLength = properties.currentValueLength
..label = properties.label
..value = properties.value
..increasedValue = properties.increasedValue
......
......@@ -3367,6 +3367,68 @@ void main() {
semantics.dispose();
});
testWidgets('Disabled text field does not have tap action', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(
const MaterialApp(
home: Material(
child: Center(
child: TextField(
maxLength: 10,
enabled: false,
),
),
),
),
);
expect(semantics, isNot(includesNodeWith(actions: <SemanticsAction>[SemanticsAction.tap])));
semantics.dispose();
});
testWidgets('currentValueLength/maxValueLength are in the tree', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
final TextEditingController controller = TextEditingController();
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Center(
child: TextField(
controller: controller,
maxLength: 10,
),
),
),
),
);
expect(semantics, includesNodeWith(
flags: <SemanticsFlag>[SemanticsFlag.isTextField],
maxValueLength: 10,
currentValueLength: 0,
));
await tester.showKeyboard(find.byType(TextField));
const String testValue = '123';
tester.testTextInput.updateEditingValue(const TextEditingValue(
text: testValue,
selection: TextSelection.collapsed(offset: 3),
composing: TextRange(start: 0, end: testValue.length),
));
await tester.pump();
expect(semantics, includesNodeWith(
flags: <SemanticsFlag>[SemanticsFlag.isTextField, SemanticsFlag.isFocused],
maxValueLength: 10,
currentValueLength: 3,
));
semantics.dispose();
});
testWidgets('Read only TextField identifies as read only text field in semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
......
......@@ -447,6 +447,8 @@ void main() {
' textDirection: null\n'
' sortKey: null\n'
' platformViewId: null\n'
' maxValueLength: null\n'
' currentValueLength: null\n'
' scrollChildren: null\n'
' scrollIndex: null\n'
' scrollExtentMin: null\n'
......@@ -543,6 +545,8 @@ void main() {
' textDirection: null\n'
' sortKey: null\n'
' platformViewId: null\n'
' maxValueLength: null\n'
' currentValueLength: null\n'
' scrollChildren: null\n'
' scrollIndex: null\n'
' scrollExtentMin: null\n'
......
......@@ -452,8 +452,11 @@ void main() {
),
);
final dynamic semanticsDebuggerPainter = _getSemanticsDebuggerPainter(debuggerKey: debugger, tester: tester);
final RenderObject renderTextfield = tester.renderObject(find.descendant(of: find.byKey(textField), matching: find.byType(Semantics)).first);
expect(
_getMessageShownInSemanticsDebugger(widgetKey: textField, debuggerKey: debugger, tester: tester),
semanticsDebuggerPainter.getMessage(renderTextfield.debugSemantics),
'textfield',
);
});
......@@ -463,6 +466,14 @@ String _getMessageShownInSemanticsDebugger({
@required Key widgetKey,
@required Key debuggerKey,
@required WidgetTester tester,
}) {
final dynamic semanticsDebuggerPainter = _getSemanticsDebuggerPainter(debuggerKey: debuggerKey, tester: tester);
return semanticsDebuggerPainter.getMessage(tester.renderObject(find.byKey(widgetKey)).debugSemantics);
}
dynamic _getSemanticsDebuggerPainter({
@required Key debuggerKey,
@required WidgetTester tester,
}) {
final CustomPaint customPaint = tester.widgetList(find.descendant(
of: find.byKey(debuggerKey),
......@@ -470,5 +481,5 @@ String _getMessageShownInSemanticsDebugger({
)).first;
final dynamic semanticsDebuggerPainter = customPaint.foregroundPainter;
expect(semanticsDebuggerPainter.runtimeType.toString(), '_SemanticsDebuggerPainter');
return semanticsDebuggerPainter.getMessage(tester.renderObject(find.byKey(widgetKey)).debugSemantics);
return semanticsDebuggerPainter;
}
......@@ -442,6 +442,8 @@ class SemanticsTester {
double scrollPosition,
double scrollExtentMax,
double scrollExtentMin,
int currentValueLength,
int maxValueLength,
SemanticsNode ancestor,
}) {
bool checkNode(SemanticsNode node) {
......@@ -471,6 +473,12 @@ class SemanticsTester {
return false;
if (scrollExtentMin != null && !nearEqual(node.scrollExtentMin, scrollExtentMin, 0.1))
return false;
if (currentValueLength != null && node.currentValueLength != currentValueLength) {
return false;
}
if (maxValueLength != null && node.maxValueLength != maxValueLength) {
return false;
}
return true;
}
......@@ -713,7 +721,19 @@ class _IncludesNodeWith extends Matcher {
this.scrollPosition,
this.scrollExtentMax,
this.scrollExtentMin,
}) : assert(label != null || value != null || actions != null || flags != null || scrollPosition != null || scrollExtentMax != null || scrollExtentMin != null);
this.maxValueLength,
this.currentValueLength,
}) : assert(
label != null ||
value != null ||
actions != null ||
flags != null ||
scrollPosition != null ||
scrollExtentMax != null ||
scrollExtentMin != null ||
maxValueLength != null ||
currentValueLength != null
);
final String label;
final String value;
......@@ -724,6 +744,8 @@ class _IncludesNodeWith extends Matcher {
final double scrollPosition;
final double scrollExtentMax;
final double scrollExtentMin;
final int currentValueLength;
final int maxValueLength;
@override
bool matches(covariant SemanticsTester item, Map<dynamic, dynamic> matchState) {
......@@ -737,6 +759,8 @@ class _IncludesNodeWith extends Matcher {
scrollPosition: scrollPosition,
scrollExtentMax: scrollExtentMax,
scrollExtentMin: scrollExtentMin,
currentValueLength: currentValueLength,
maxValueLength: maxValueLength,
).isNotEmpty;
}
......@@ -761,6 +785,8 @@ class _IncludesNodeWith extends Matcher {
if (scrollPosition != null) 'scrollPosition "$scrollPosition"',
if (scrollExtentMax != null) 'scrollExtentMax "$scrollExtentMax"',
if (scrollExtentMin != null) 'scrollExtentMin "$scrollExtentMin"',
if (currentValueLength != null) 'currentValueLength "$currentValueLength"',
if (maxValueLength != null) 'maxValueLength "$maxValueLength"',
];
return strings.join(', ');
}
......@@ -780,6 +806,8 @@ Matcher includesNodeWith({
double scrollPosition,
double scrollExtentMax,
double scrollExtentMin,
int maxValueLength,
int currentValueLength,
}) {
return _IncludesNodeWith(
label: label,
......@@ -791,5 +819,7 @@ Matcher includesNodeWith({
scrollPosition: scrollPosition,
scrollExtentMax: scrollExtentMax,
scrollExtentMin: scrollExtentMin,
maxValueLength: maxValueLength,
currentValueLength: currentValueLength,
);
}
......@@ -317,6 +317,7 @@ class Descendant extends SerializableFinder {
@required this.of,
@required this.matching,
this.matchRoot = false,
this.firstMatchOnly = false,
});
/// The finder specifying the widget of which the descendant is to be found.
......@@ -328,6 +329,9 @@ class Descendant extends SerializableFinder {
/// Whether the widget matching [of] will be considered for a match.
final bool matchRoot;
/// If true then only the first descendant matching `matching` will be returned.
final bool firstMatchOnly;
@override
String get finderType => 'Descendant';
......@@ -338,6 +342,7 @@ class Descendant extends SerializableFinder {
..addAll(matching.serialize().map((String key, String value) => MapEntry<String, String>('matching_$key', value)))
..addAll(<String, String>{
'matchRoot': matchRoot ? 'true' : 'false',
'firstMatchOnly': firstMatchOnly ? 'true' : 'false',
});
}
......@@ -359,6 +364,7 @@ class Descendant extends SerializableFinder {
of: SerializableFinder.deserialize(of),
matching: SerializableFinder.deserialize(matching),
matchRoot: other['matchRoot'] == 'true',
firstMatchOnly: other['firstMatchOnly'] == 'true',
);
}
}
......@@ -374,6 +380,7 @@ class Ancestor extends SerializableFinder {
@required this.of,
@required this.matching,
this.matchRoot = false,
this.firstMatchOnly = false,
});
/// The finder specifying the widget of which the ancestor is to be found.
......@@ -385,6 +392,9 @@ class Ancestor extends SerializableFinder {
/// Whether the widget matching [of] will be considered for a match.
final bool matchRoot;
/// If true then only the first ancestor matching `matching` will be returned.
final bool firstMatchOnly;
@override
String get finderType => 'Ancestor';
......@@ -395,6 +405,7 @@ class Ancestor extends SerializableFinder {
..addAll(matching.serialize().map((String key, String value) => MapEntry<String, String>('matching_$key', value)))
..addAll(<String, String>{
'matchRoot': matchRoot ? 'true' : 'false',
'firstMatchOnly': firstMatchOnly ? 'true' : 'false',
});
}
......@@ -416,6 +427,7 @@ class Ancestor extends SerializableFinder {
of: SerializableFinder.deserialize(of),
matching: SerializableFinder.deserialize(matching),
matchRoot: other['matchRoot'] == 'true',
firstMatchOnly: other['firstMatchOnly'] == 'true',
);
}
}
......
......@@ -1199,22 +1199,30 @@ class CommonFinders {
///
/// If the `matchRoot` argument is true then the widget specified by `of` will
/// be considered for a match. The argument defaults to false.
///
/// If `firstMatchOnly` is true then only the first ancestor matching
/// `matching` will be returned. Defaults to false.
SerializableFinder ancestor({
@required SerializableFinder of,
@required SerializableFinder matching,
bool matchRoot = false,
}) => Ancestor(of: of, matching: matching, matchRoot: matchRoot);
bool firstMatchOnly = false,
}) => Ancestor(of: of, matching: matching, matchRoot: matchRoot, firstMatchOnly: firstMatchOnly);
/// Finds the widget that is an descendant of the `of` parameter and that
/// matches the `matching` parameter.
///
/// If the `matchRoot` argument is true then the widget specified by `of` will
/// be considered for a match. The argument defaults to false.
///
/// If `firstMatchOnly` is true then only the first descendant matching
/// `matching` will be returned. Defaults to false.
SerializableFinder descendant({
@required SerializableFinder of,
@required SerializableFinder matching,
bool matchRoot = false,
}) => Descendant(of: of, matching: matching, matchRoot: matchRoot);
bool firstMatchOnly = false,
}) => Descendant(of: of, matching: matching, matchRoot: matchRoot, firstMatchOnly: firstMatchOnly);
}
/// An immutable 2D floating-point offset used by Flutter Driver.
......
......@@ -335,19 +335,21 @@ class FlutterDriverExtension {
}
Finder _createAncestorFinder(Ancestor arguments) {
return find.ancestor(
final Finder finder = find.ancestor(
of: _createFinder(arguments.of),
matching: _createFinder(arguments.matching),
matchRoot: arguments.matchRoot,
);
return arguments.firstMatchOnly ? finder.first : finder;
}
Finder _createDescendantFinder(Descendant arguments) {
return find.descendant(
final Finder finder = find.descendant(
of: _createFinder(arguments.of),
matching: _createFinder(arguments.matching),
matchRoot: arguments.matchRoot,
);
return arguments.firstMatchOnly ? finder.first : finder;
}
Finder _createFinder(SerializableFinder finder) {
......
......@@ -573,6 +573,39 @@ void main() {
expect(await result, null);
});
testWidgets('descendant finder firstMatchOnly', (WidgetTester tester) async {
flutterDriverLog.listen((LogRecord _) {}); // Silence logging.
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true);
Future<String> getDescendantText() async {
final Map<String, Object> arguments = GetText(Descendant(
of: ByValueKey('column'),
matching: const ByType('Text'),
firstMatchOnly: true,
), timeout: const Duration(seconds: 1)).serialize();
final Map<String, dynamic> result = await extension.call(arguments);
if (result['isError']) {
return null;
}
return GetTextResult.fromJson(result['response']).text;
}
await tester.pumpWidget(
MaterialApp(
home: Column(
key: const ValueKey<String>('column'),
children: const <Widget>[
Text('Hello1', key: ValueKey<String>('text1')),
Text('Hello2', key: ValueKey<String>('text2')),
Text('Hello3', key: ValueKey<String>('text3')),
],
),
),
);
expect(await getDescendantText(), 'Hello1');
});
testWidgets('ancestor finder', (WidgetTester tester) async {
flutterDriverLog.listen((LogRecord _) {}); // Silence logging.
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true);
......@@ -642,6 +675,54 @@ void main() {
expect(await result, null);
});
testWidgets('ancestor finder firstMatchOnly', (WidgetTester tester) async {
flutterDriverLog.listen((LogRecord _) {}); // Silence logging.
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true);
Future<Offset> getAncestorTopLeft() async {
final Map<String, Object> arguments = GetOffset(Ancestor(
of: ByValueKey('leaf'),
matching: const ByType('Container'),
firstMatchOnly: true,
), OffsetType.topLeft, timeout: const Duration(seconds: 1)).serialize();
final Map<String, dynamic> response = await extension.call(arguments);
if (response['isError']) {
return null;
}
final GetOffsetResult result = GetOffsetResult.fromJson(response['response']);
return Offset(result.dx, result.dy);
}
await tester.pumpWidget(
MaterialApp(
home: Center(
child: Container(
height: 200,
width: 200,
child: Center(
child: Container(
height: 100,
width: 100,
child: Center(
child: Container(
key: const ValueKey<String>('leaf'),
height: 50,
width: 50,
),
),
),
),
),
),
),
);
expect(
await getAncestorTopLeft(),
const Offset((800 - 100) / 2, (600 - 100) / 2),
);
});
testWidgets('GetDiagnosticsTree', (WidgetTester tester) async {
final FlutterDriverExtension extension = FlutterDriverExtension((String arg) async => '', true);
......
......@@ -15,6 +15,7 @@ void main() {
of: of,
matching: matching,
matchRoot: true,
firstMatchOnly: true,
);
expect(a.serialize(), <String, String>{
'finderType': 'Ancestor',
......@@ -23,7 +24,8 @@ void main() {
'matching_finderType': 'ByValueKey',
'matching_keyValueString': 'hello',
'matching_keyValueType': 'String',
'matchRoot': 'true'
'matchRoot': 'true',
'firstMatchOnly': 'true',
});
});
......@@ -35,13 +37,15 @@ void main() {
'matching_finderType': 'ByValueKey',
'matching_keyValueString': 'hello',
'matching_keyValueType': 'String',
'matchRoot': 'true'
'matchRoot': 'true',
'firstMatchOnly': 'true',
};
final Ancestor a = Ancestor.deserialize(serialized);
expect(a.of, isA<ByType>());
expect(a.matching, isA<ByValueKey>());
expect(a.matchRoot, isTrue);
expect(a.firstMatchOnly, isTrue);
});
test('Descendant finder serialize', () {
......@@ -52,6 +56,7 @@ void main() {
of: of,
matching: matching,
matchRoot: true,
firstMatchOnly: true,
);
expect(a.serialize(), <String, String>{
'finderType': 'Descendant',
......@@ -60,7 +65,8 @@ void main() {
'matching_finderType': 'ByValueKey',
'matching_keyValueString': 'hello',
'matching_keyValueType': 'String',
'matchRoot': 'true'
'matchRoot': 'true',
'firstMatchOnly': 'true',
});
});
......@@ -72,12 +78,14 @@ void main() {
'matching_finderType': 'ByValueKey',
'matching_keyValueString': 'hello',
'matching_keyValueType': 'String',
'matchRoot': 'true'
'matchRoot': 'true',
'firstMatchOnly': 'true',
};
final Descendant a = Descendant.deserialize(serialized);
expect(a.of, isA<ByType>());
expect(a.matching, isA<ByValueKey>());
expect(a.matchRoot, isTrue);
expect(a.firstMatchOnly, isTrue);
});
}
......@@ -433,6 +433,8 @@ Matcher matchesSemantics({
double elevation,
double thickness,
int platformViewId,
int maxValueLength,
int currentValueLength,
// Flags //
bool hasCheckedState = false,
bool isChecked = false,
......@@ -552,6 +554,8 @@ Matcher matchesSemantics({
platformViewId: platformViewId,
customActions: customActions,
hintOverrides: hintOverrides,
currentValueLength: currentValueLength,
maxValueLength: maxValueLength,
children: children,
);
}
......@@ -1745,6 +1749,8 @@ class _MatchesSemanticsData extends Matcher {
this.elevation,
this.thickness,
this.platformViewId,
this.maxValueLength,
this.currentValueLength,
this.customActions,
this.hintOverrides,
this.children,
......@@ -1765,6 +1771,8 @@ class _MatchesSemanticsData extends Matcher {
final double elevation;
final double thickness;
final int platformViewId;
final int maxValueLength;
final int currentValueLength;
final List<Matcher> children;
@override
......@@ -1796,6 +1804,10 @@ class _MatchesSemanticsData extends Matcher {
description.add(' with thickness: $thickness');
if (platformViewId != null)
description.add(' with platformViewId: $platformViewId');
if (maxValueLength != null)
description.add(' with maxValueLength: $maxValueLength');
if (currentValueLength != null)
description.add(' with currentValueLength: $currentValueLength');
if (customActions != null)
description.add(' with custom actions: $customActions');
if (hintOverrides != null)
......@@ -1838,6 +1850,10 @@ class _MatchesSemanticsData extends Matcher {
return failWithDescription(matchState, 'thickness was: ${data.thickness}');
if (platformViewId != null && platformViewId != data.platformViewId)
return failWithDescription(matchState, 'platformViewId was: ${data.platformViewId}');
if (currentValueLength != null && currentValueLength != data.currentValueLength)
return failWithDescription(matchState, 'currentValueLength was: ${data.currentValueLength}');
if (maxValueLength != null && maxValueLength != data.maxValueLength)
return failWithDescription(matchState, 'maxValueLength was: ${data.maxValueLength}');
if (actions != null) {
int actionBits = 0;
for (SemanticsAction action in actions)
......
......@@ -525,6 +525,8 @@ void main() {
scrollExtentMin: null,
platformViewId: 105,
customSemanticsActionIds: <int>[CustomSemanticsAction.getIdentifier(action)],
currentValueLength: 10,
maxValueLength: 15,
);
final _FakeSemanticsNode node = _FakeSemanticsNode();
node.data = data;
......@@ -535,6 +537,8 @@ void main() {
elevation: 3.0,
thickness: 4.0,
platformViewId: 105,
currentValueLength: 10,
maxValueLength: 15,
/* Flags */
hasCheckedState: true,
isChecked: true,
......
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