8000 [CP-stable]Fixes tab semantics gets dropped if the child produce a se… · flutter/flutter@7d3efe4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7d3efe4

Browse files
[CP-stable]Fixes tab semantics gets dropped if the child produce a semantics node (#169362)
This pull request is created by [automatic cherry pick workflow](https://github.com/flutter/flutter/blob/main/docs/releases/Flutter-Cherrypick-Process.md#automatically-creates-a-cherry-pick-request) Please fill in the form below, and a flutter domain expert will evaluate this cherry pick request. ### Issue Link: What is the link to the issue this cherry-pick is addressing? #169175 ### Changelog Description: Explain this cherry pick in one line that is accessible to most Flutter developers. See [best practices](https://github.com/flutter/flutter/blob/main/docs/releases/Hotfix-Documentation-Best-Practices.md) for examples Fixed unexpected crash when using Tab and TabBar widgets. ### Impact Description: What is the impact (ex. visual jank on Samsung phones, app crash, cannot ship an iOS app)? Does it impact development (ex. flutter doctor crashes when Android Studio is installed), or the shipping production app (the app crashes on launch) app crash ### Workaround: Is there a workaround for this issue? Wrap the Tab widget with a MergeSemantics widget will mitigate the issue. ### Risk: What is the risk level of this cherry-pick? - [O] Low ### Test Coverage: Are you confident that your fix is well-tested by automated tests? - [O] Yes ### Validation Steps: What are the steps to validate that this fix works? create a TabBar that has a Tab with image widget ```dart TabBar( tabs: <Widget>[ Tab(icon: Image.network('https://some-url')), Tab(icon: Icon(Icons.beach_access_sharp)), Tab(icon: Icon(Icons.brightness_5_sharp)), ], ), ```
1 parent c56879b commit 7d3efe4

File tree

4 files changed

+40
-2
lines changed

4 files changed

+40
-2
lines changed

packages/flutter/lib/src/material/tabs.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1976,6 +1976,7 @@ class _TabBarState extends State<TabBar> {
19761976
),
19771977
),
19781978
);
1979+
wrappedTabs[index] = MergeSemantics(child: wrappedTabs[index]);
19791980
if (!widget.isScrollable && effectiveTabAlignment == TabAlignment.fill) {
19801981
wrappedTabs[index] = Expanded(child: wrappedTabs[index]);
19811982
}

packages/flutter/lib/src/semantics/semantics.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4300,6 +4300,9 @@ class SemanticsOwner extends ChangeNotifier {
43004300
return null;
43014301
}
43024302
if (node.mergeAllDescendantsIntoThisNode) {
4303+
if (node._canPerformAction(action)) {
4304+
return node._actions[action];
4305+
}
43034306
SemanticsNode? result;
43044307
node._visitDescendants((SemanticsNode child) {
43054308
if (child._canPerformAction(action)) {

packages/flutter/test/material/tabs_test.dart

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'dart:ui';
6-
75
import 'package:flutter/foundation.dart';
86
import 'package:flutter/gestures.dart';
97
import 'package:flutter/material.dart';
@@ -156,6 +154,22 @@ void main() {
156154
expect(tester.renderObject(find.byType(CustomPaint)).debugNeedsPaint, true);
157155
});
158156

157+
testWidgets('tab semantics role test', (WidgetTester tester) async {
158+
// Regressing test for https://github.com/flutter/flutter/issues/169175
159+
// Creates an image semantics node with zero size.
160+
await tester.pumpWidget(
161+
boilerplate(
162+
child: DefaultTabController(
163+
length: 1,
164+
child: TabBar(
165+
tabs: <Widget>[Tab(icon: Semantics(image: true, child: const SizedBox.shrink()))],
166+
),
167+
),
168+
),
169+
);
170+
expect(find.byType(Tab), findsOneWidget);
171+
});
172+
159173
testWidgets('Tab sizing - icon', (WidgetTester tester) async {
160174
await tester.pumpWidget(
161175
const MaterialApp(

packages/flutter/test/semantics/semantics_test.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,26 @@ void main() {
918918
expect(newNode.id, expectId);
919919
});
920920

921+
test('performActionAt can hit test on merged semantics node', () {
922+
bool tapped = false;
923+
final SemanticsOwner owner = SemanticsOwner(onSemanticsUpdate: (SemanticsUpdate update) {});
924+
final SemanticsNode root = SemanticsNode.root(owner: owner)
925+
..rect = const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0);
926+
final SemanticsNode merged = SemanticsNode()..rect = const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0);
927+
final SemanticsConfiguration mergeConfig =
928+
SemanticsConfiguration()
929+
..isSemanticBoundary = true
930+
..isMergingSemanticsOfDescendants = true
931+
..onTap = () => tapped = true;
932+
final SemanticsConfiguration rootConfig = SemanticsConfiguration()..isSemanticBoundary = true;
933+
934+
merged.updateWith(config: mergeConfig, childrenInInversePaintOrder: <SemanticsNode>[]);
935+
root.updateWith(config: rootConfig, childrenInInversePaintOrder: <SemanticsNode>[merged]);
936+
937+
owner.performActionAt(const Offset(5, 5), SemanticsAction.tap);
938+
expect(tapped, isTrue);
939+
});
940+
921941
test('Tags show up in debug properties', () {
922942
final SemanticsNode actionNode =
923943
SemanticsNode()..tags = <SemanticsTag>{RenderViewport.useTwoPaneSemantics};

0 commit comments

Comments
 (0)
0