diff --git a/packages/flutter/lib/src/semantics/semantics.dart b/packages/flutter/lib/src/semantics/semantics.dart index a868cee68b9..72f55976f0b 100644 --- a/packages/flutter/lib/src/semantics/semantics.dart +++ b/packages/flutter/lib/src/semantics/semantics.dart @@ -2001,14 +2001,16 @@ class _SemanticsSortGroup extends Comparable<_SemanticsSortGroup> { List sortedWithinVerticalGroup() { final List<_BoxEdge> edges = <_BoxEdge>[]; for (SemanticsNode child in nodes) { + // Using a small delta to shrink child rects removes overlapping cases. + final Rect childRect = child.rect.deflate(0.1); edges.add(new _BoxEdge( isLeadingEdge: true, - offset: _pointInParentCoordinates(child, child.rect.topLeft).dx, + offset: _pointInParentCoordinates(child, childRect.topLeft).dx, node: child, )); edges.add(new _BoxEdge( isLeadingEdge: false, - offset: _pointInParentCoordinates(child, child.rect.bottomRight).dx, + offset: _pointInParentCoordinates(child, childRect.bottomRight).dx, node: child, )); } @@ -2145,14 +2147,16 @@ Offset _pointInParentCoordinates(SemanticsNode node, Offset point) { List _childrenInDefaultOrder(List children, TextDirection textDirection) { final List<_BoxEdge> edges = <_BoxEdge>[]; for (SemanticsNode child in children) { + // Using a small delta to shrink child rects removes overlapping cases. + final Rect childRect = child.rect.deflate(0.1); edges.add(new _BoxEdge( isLeadingEdge: true, - offset: _pointInParentCoordinates(child, child.rect.topLeft).dy, + offset: _pointInParentCoordinates(child, childRect.topLeft).dy, node: child, )); edges.add(new _BoxEdge( isLeadingEdge: false, - offset: _pointInParentCoordinates(child, child.rect.bottomRight).dy, + offset: _pointInParentCoordinates(child, childRect.bottomRight).dy, node: child, )); } diff --git a/packages/flutter/test/material/search_test.dart b/packages/flutter/test/material/search_test.dart index 36c8e59f230..8e4cf6884f4 100644 --- a/packages/flutter/test/material/search_test.dart +++ b/packages/flutter/test/material/search_test.dart @@ -498,17 +498,6 @@ void main() { label: routeName, textDirection: TextDirection.ltr, children: [ - new TestSemantics( - id: 8, - flags: [ - SemanticsFlag.isButton, - SemanticsFlag.hasEnabledState, - SemanticsFlag.isEnabled, - ], - actions: [SemanticsAction.tap], - label: 'Suggestions', - textDirection: TextDirection.ltr, - ), new TestSemantics( id: 9, children: [ @@ -541,6 +530,17 @@ void main() { ), ], ), + new TestSemantics( + id: 8, + flags: [ + SemanticsFlag.isButton, + SemanticsFlag.hasEnabledState, + SemanticsFlag.isEnabled, + ], + actions: [SemanticsAction.tap], + label: 'Suggestions', + textDirection: TextDirection.ltr, + ), ], ), ], diff --git a/packages/flutter/test/semantics/traversal_order_test.dart b/packages/flutter/test/semantics/traversal_order_test.dart new file mode 100644 index 00000000000..7e2afd78b00 --- /dev/null +++ b/packages/flutter/test/semantics/traversal_order_test.dart @@ -0,0 +1,95 @@ +// Copyright 2018 The Chromium 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/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../widgets/semantics_tester.dart'; + +void main() { + testWidgets('Traversal order handles touching elements', (WidgetTester tester) async { + final SemanticsTester semantics = new SemanticsTester(tester); + await tester.pumpWidget( + new MaterialApp( + home: new Column( + children: new List.generate(3, (int column) { + return new Row(children: List.generate(3, (int row) { + return new Semantics( + child: new SizedBox( + width: 50.0, + height: 50.0, + child: new Text('$column - $row'), + ), + ); + })); + }), + ), + )); + + final TestSemantics expected = new TestSemantics.root( + children: [ + new TestSemantics( + id: 1, + textDirection: TextDirection.ltr, + children: [ + new TestSemantics( + id: 2, + flags: [SemanticsFlag.scopesRoute], + children: [ + new TestSemantics( + id: 3, + label: '0 - 0', + textDirection: TextDirection.ltr, + ), + new TestSemantics( + id: 4, + label: '0 - 1', + textDirection: TextDirection.ltr, + ), + new TestSemantics( + id: 5, + label: '0 - 2', + textDirection: TextDirection.ltr, + ), + new TestSemantics( + id: 6, + label: '1 - 0', + textDirection: TextDirection.ltr, + ), + new TestSemantics( + id: 7, + label: '1 - 1', + textDirection: TextDirection.ltr, + ), + new TestSemantics( + id: 8, + label: '1 - 2', + textDirection: TextDirection.ltr, + ), + new TestSemantics( + id: 9, + label: '2 - 0', + textDirection: TextDirection.ltr, + ), + new TestSemantics( + id: 10, + label: '2 - 1', + textDirection: TextDirection.ltr, + ), + new TestSemantics( + id: 11, + label: '2 - 2', + textDirection: TextDirection.ltr, + ), + ], + ), + ], + ), + ], + ); + expect(semantics, hasSemantics(expected, ignoreRect: true, ignoreTransform: true)); + semantics.dispose(); + }); +}