From db16d7252ca9e2a163bdcdf4b1026d58aa59f7ee Mon Sep 17 00:00:00 2001 From: melo936 <50238852+melo936@users.noreply.github.com> Date: Tue, 8 Sep 2026 20:34:13 +0400 Subject: [PATCH 1/3] fix(title_bar): skip hidden children when collecting semantics --- .../navigation/navigation_view/title_bar.dart | 19 +++++ test/title_bar_test.dart | 82 +++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/lib/src/controls/navigation/navigation_view/title_bar.dart b/lib/src/controls/navigation/navigation_view/title_bar.dart index 45689613d..6a0277186 100644 --- a/lib/src/controls/navigation/navigation_view/title_bar.dart +++ b/lib/src/controls/navigation/navigation_view/title_bar.dart @@ -561,6 +561,25 @@ class _RenderTitleSubtitleOverflow extends RenderBox child = childAfter(child); } } + + /// Skips hidden children when collecting semantics. + /// + /// Hidden children are never laid out (see [performLayout]). If a semantics + /// walk visits a hidden child before it is laid out, Flutter asserts on + /// `_needsLayout`. Minimizing a maximized window with a screen reader + /// running triggers the assert. + @override + void visitChildrenForSemantics(RenderObjectVisitor visitor) { + var child = firstChild; + while (child != null) { + final childParentData = + child.parentData! as _TitleSubtitleOverflowParentData; + if (!childParentData.isHidden) { + visitor(child); + } + child = childAfter(child); + } + } } /// The preferred position for the pane toggle button. diff --git a/test/title_bar_test.dart b/test/title_bar_test.dart index 9d8a80cc9..09e148c80 100644 --- a/test/title_bar_test.dart +++ b/test/title_bar_test.dart @@ -91,4 +91,86 @@ void main() { // Null titleBar should return 0 expect(TitleBar.calculateHeight(ctx2, null), 0); }); + + testWidgets('an initially hidden title has no semantics until it fits', ( + tester, + ) async { + final handle = tester.ensureSemantics(); + tester.view.devicePixelRatio = 1; + tester.view.physicalSize = const Size(200, 600); + addTearDown(tester.view.reset); + const searchLabel = 'Search settings'; + + try { + await tester.pumpWidget( + FluentApp( + home: NavigationView( + titleBar: TitleBar( + isBackButtonVisible: false, + title: Semantics( + label: searchLabel, + button: true, + child: const SizedBox( + width: searchLabel.length * 16, + height: 32, + ), + ), + ), + content: const SizedBox.shrink(), + ), + ), + ); + expect(tester.takeException(), isNull); + expect(find.bySemanticsLabel(searchLabel), findsNothing); + + tester.view.physicalSize = const Size(800, 600); + await tester.pump(); + expect(tester.takeException(), isNull); + expect(find.bySemanticsLabel(searchLabel), findsOneWidget); + } finally { + handle.dispose(); + } + }); + + testWidgets( + 'a dirty title can be hidden and restored with semantics enabled', + (tester) async { + final handle = tester.ensureSemantics(); + tester.view.devicePixelRatio = 1; + tester.view.physicalSize = const Size(800, 600); + addTearDown(tester.view.reset); + + FluentApp buildApp(String title) => FluentApp( + home: NavigationView( + titleBar: TitleBar( + isBackButtonVisible: false, + title: Semantics( + label: title, + button: true, + child: SizedBox(width: title.length * 16, height: 32), + ), + ), + content: const SizedBox.shrink(), + ), + ); + + try { + await tester.pumpWidget(buildApp('Search')); + expect(tester.takeException(), isNull); + expect(find.bySemanticsLabel('Search'), findsOneWidget); + + tester.view.physicalSize = const Size(200, 600); + await tester.pumpWidget(buildApp('Search settings')); + expect(tester.takeException(), isNull); + expect(find.bySemanticsLabel('Search settings'), findsNothing); + + tester.view.physicalSize = const Size(800, 600); + await tester.pump(); + expect(tester.takeException(), isNull); + expect(find.bySemanticsLabel('Search settings'), findsOneWidget); + } finally { + handle.dispose(); + } + }, + ); } From 2edde186b71d95a998da98650d99eca0ca3dbd0c Mon Sep 17 00:00:00 2001 From: melo936 <50238852+melo936@users.noreply.github.com> Date: Tue, 8 Sep 2026 20:50:29 +0400 Subject: [PATCH 2/3] add changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35bb315fd..4eb6c25ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## [next] +- fix(title_bar): skip hidden children when collecting semantics ([#1354](https://github.com/bdlukaa/fluent_ui/pull/1354)) + ## 4.16.1 - fix: `ComboBox` no longer throws when opening a dropdown with a single item ([#1347](https://github.com/bdlukaa/fluent_ui/pull/1347)) From 7aac1f3116c5cae37ba19cfb19fb6334a3c98d34 Mon Sep 17 00:00:00 2001 From: melo936 <50238852+melo936@users.noreply.github.com> Date: Tue, 8 Sep 2026 20:51:29 +0400 Subject: [PATCH 3/3] chore: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4eb6c25ee..5bfafb291 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ## [next] + - fix(title_bar): skip hidden children when collecting semantics ([#1354](https://github.com/bdlukaa/fluent_ui/pull/1354)) ## 4.16.1