fix the issue that can't set app bar traversal order for flexible space (#162910)
fix https://github.com/flutter/flutter/issues/98570 ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
parent
5fa05ab6df
commit
539c5b93af
@ -218,6 +218,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
|
||||
this.titleTextStyle,
|
||||
this.systemOverlayStyle,
|
||||
this.forceMaterialTransparency = false,
|
||||
this.useDefaultSemanticsOrder = true,
|
||||
this.clipBehavior,
|
||||
this.actionsPadding,
|
||||
}) : assert(elevation == null || elevation >= 0.0),
|
||||
@ -744,6 +745,24 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
|
||||
/// {@endtemplate}
|
||||
final bool forceMaterialTransparency;
|
||||
|
||||
/// {@template flutter.material.appbar.useDefaultSemanticsOrder}
|
||||
/// Whether to use the default semantic ordering for the app bar's children for
|
||||
/// accessibility traversal order.
|
||||
///
|
||||
/// If this is set to true, the app bar will use the default semantic ordering,
|
||||
/// which places the flexible space after the main content in the semantics tree.
|
||||
/// This affects how screen readers and other assistive technologies navigate the app bar's content.
|
||||
///
|
||||
/// Set this to false if you want to customize semantics traversal order in the app bar.
|
||||
/// You can then assign [SemanticsSortKey]s to app bar's children to control the order.
|
||||
///
|
||||
/// Defaults to true.
|
||||
///
|
||||
/// See also:
|
||||
/// * [SemanticsSortKey], which are keys used to define the accessibility traversal order.
|
||||
/// {@endtemplate}
|
||||
final bool useDefaultSemanticsOrder;
|
||||
|
||||
/// {@macro flutter.material.Material.clipBehavior}
|
||||
final Clip? clipBehavior;
|
||||
|
||||
@ -1150,12 +1169,12 @@ class _AppBarState extends State<AppBar> {
|
||||
fit: StackFit.passthrough,
|
||||
children: <Widget>[
|
||||
Semantics(
|
||||
sortKey: const OrdinalSortKey(1.0),
|
||||
sortKey: widget.useDefaultSemanticsOrder ? const OrdinalSortKey(1.0) : null,
|
||||
explicitChildNodes: true,
|
||||
child: widget.flexibleSpace,
|
||||
),
|
||||
Semantics(
|
||||
sortKey: const OrdinalSortKey(0.0),
|
||||
sortKey: widget.useDefaultSemanticsOrder ? const OrdinalSortKey(0.0) : null,
|
||||
explicitChildNodes: true,
|
||||
// Creates a material widget to prevent the flexibleSpace from
|
||||
// obscuring the ink splashes produced by appBar children.
|
||||
@ -1238,6 +1257,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|
||||
required this.titleTextStyle,
|
||||
required this.systemOverlayStyle,
|
||||
required this.forceMaterialTransparency,
|
||||
required this.useDefaultSemanticsOrder,
|
||||
required this.clipBehavior,
|
||||
required this.variant,
|
||||
required this.accessibleNavigation,
|
||||
@ -1277,6 +1297,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|
||||
final SystemUiOverlayStyle? systemOverlayStyle;
|
||||
final double _bottomHeight;
|
||||
final bool forceMaterialTransparency;
|
||||
final bool useDefaultSemanticsOrder;
|
||||
final Clip? clipBehavior;
|
||||
final _SliverAppVariant variant;
|
||||
final bool accessibleNavigation;
|
||||
@ -1369,6 +1390,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|
||||
titleTextStyle: titleTextStyle,
|
||||
systemOverlayStyle: systemOverlayStyle,
|
||||
forceMaterialTransparency: forceMaterialTransparency,
|
||||
useDefaultSemanticsOrder: useDefaultSemanticsOrder,
|
||||
actionsPadding: actionsPadding,
|
||||
),
|
||||
);
|
||||
@ -1408,6 +1430,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
|
||||
titleTextStyle != oldDelegate.titleTextStyle ||
|
||||
systemOverlayStyle != oldDelegate.systemOverlayStyle ||
|
||||
forceMaterialTransparency != oldDelegate.forceMaterialTransparency ||
|
||||
useDefaultSemanticsOrder != oldDelegate.useDefaultSemanticsOrder ||
|
||||
accessibleNavigation != oldDelegate.accessibleNavigation ||
|
||||
actionsPadding != oldDelegate.actionsPadding;
|
||||
}
|
||||
@ -1548,6 +1571,7 @@ class SliverAppBar extends StatefulWidget {
|
||||
this.titleTextStyle,
|
||||
this.systemOverlayStyle,
|
||||
this.forceMaterialTransparency = false,
|
||||
this.useDefaultSemanticsOrder = true,
|
||||
this.clipBehavior,
|
||||
this.actionsPadding,
|
||||
}) : assert(floating || !snap, 'The "snap" argument only makes sense for floating app bars.'),
|
||||
@ -1617,6 +1641,7 @@ class SliverAppBar extends StatefulWidget {
|
||||
this.titleTextStyle,
|
||||
this.systemOverlayStyle,
|
||||
this.forceMaterialTransparency = false,
|
||||
this.useDefaultSemanticsOrder = true,
|
||||
this.clipBehavior,
|
||||
this.actionsPadding,
|
||||
}) : assert(floating || !snap, 'The "snap" argument only makes sense for floating app bars.'),
|
||||
@ -1686,6 +1711,7 @@ class SliverAppBar extends StatefulWidget {
|
||||
this.titleTextStyle,
|
||||
this.systemOverlayStyle,
|
||||
this.forceMaterialTransparency = false,
|
||||
this.useDefaultSemanticsOrder = true,
|
||||
this.clipBehavior,
|
||||
this.actionsPadding,
|
||||
}) : assert(floating || !snap, 'The "snap" argument only makes sense for floating app bars.'),
|
||||
@ -1948,6 +1974,11 @@ class SliverAppBar extends StatefulWidget {
|
||||
/// This property is used to configure an [AppBar].
|
||||
final bool forceMaterialTransparency;
|
||||
|
||||
/// {@macro flutter.material.appbar.useDefaultSemanticsOrder}
|
||||
///
|
||||
/// This property is used to configure an [AppBar].
|
||||
final bool useDefaultSemanticsOrder;
|
||||
|
||||
/// {@macro flutter.material.Material.clipBehavior}
|
||||
final Clip? clipBehavior;
|
||||
|
||||
@ -2107,6 +2138,7 @@ class _SliverAppBarState extends State<SliverAppBar> with TickerProviderStateMix
|
||||
titleTextStyle: widget.titleTextStyle,
|
||||
systemOverlayStyle: widget.systemOverlayStyle,
|
||||
forceMaterialTransparency: widget.forceMaterialTransparency,
|
||||
useDefaultSemanticsOrder: widget.useDefaultSemanticsOrder,
|
||||
clipBehavior: widget.clipBehavior,
|
||||
variant: widget._variant,
|
||||
accessibleNavigation: MediaQuery.of(context).accessibleNavigation,
|
||||
|
@ -1267,6 +1267,163 @@ void main() {
|
||||
|
||||
semantics.dispose();
|
||||
});
|
||||
testWidgets('AppBar has default semantics order', (WidgetTester tester) async {
|
||||
final SemanticsTester semantics = SemanticsTester(tester);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Center(
|
||||
child: AppBar(
|
||||
leading: Semantics(sortKey: const OrdinalSortKey(0), child: const Text('Leading')),
|
||||
title: Semantics(sortKey: const OrdinalSortKey(2), child: const Text('Title')),
|
||||
flexibleSpace: Semantics(
|
||||
sortKey: const OrdinalSortKey(1),
|
||||
child: const Text('Flexible Space'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
semantics,
|
||||
hasSemantics(
|
||||
TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
TestSemantics(
|
||||
id: 1,
|
||||
textDirection: TextDirection.ltr,
|
||||
children: <TestSemantics>[
|
||||
TestSemantics(
|
||||
id: 2,
|
||||
children: <TestSemantics>[
|
||||
TestSemantics(
|
||||
id: 3,
|
||||
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
|
||||
children: <TestSemantics>[
|
||||
TestSemantics(
|
||||
id: 4,
|
||||
children: <TestSemantics>[
|
||||
TestSemantics(
|
||||
id: 7,
|
||||
children: <TestSemantics>[
|
||||
TestSemantics(
|
||||
id: 8,
|
||||
label: 'Leading',
|
||||
textDirection: TextDirection.ltr,
|
||||
),
|
||||
TestSemantics(
|
||||
id: 9,
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.isHeader,
|
||||
SemanticsFlag.namesRoute,
|
||||
],
|
||||
label: 'Title',
|
||||
textDirection: TextDirection.ltr,
|
||||
),
|
||||
],
|
||||
),
|
||||
TestSemantics(
|
||||
id: 5,
|
||||
children: <TestSemantics>[
|
||||
TestSemantics(
|
||||
id: 6,
|
||||
label: 'Flexible Space',
|
||||
textDirection: TextDirection.ltr,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
ignoreRect: true,
|
||||
ignoreTransform: true,
|
||||
),
|
||||
);
|
||||
|
||||
semantics.dispose();
|
||||
});
|
||||
testWidgets('AppBar can customize sort keys for flexible space', (WidgetTester tester) async {
|
||||
final SemanticsTester semantics = SemanticsTester(tester);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Center(
|
||||
child: AppBar(
|
||||
leading: Semantics(sortKey: const OrdinalSortKey(0), child: const Text('Leading')),
|
||||
title: Semantics(sortKey: const OrdinalSortKey(2), child: const Text('Title')),
|
||||
flexibleSpace: Semantics(
|
||||
sortKey: const OrdinalSortKey(1),
|
||||
child: const Text('Flexible Space'),
|
||||
),
|
||||
useDefaultSemanticsOrder: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
semantics,
|
||||
hasSemantics(
|
||||
TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
TestSemantics(
|
||||
id: 1,
|
||||
textDirection: TextDirection.ltr,
|
||||
children: <TestSemantics>[
|
||||
TestSemantics(
|
||||
id: 2,
|
||||
children: <TestSemantics>[
|
||||
TestSemantics(
|
||||
id: 3,
|
||||
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
|
||||
children: <TestSemantics>[
|
||||
TestSemantics(
|
||||
id: 4,
|
||||
children: <TestSemantics>[
|
||||
TestSemantics(
|
||||
id: 6,
|
||||
label: 'Leading',
|
||||
textDirection: TextDirection.ltr,
|
||||
),
|
||||
TestSemantics(
|
||||
id: 5,
|
||||
label: 'Flexible Space',
|
||||
textDirection: TextDirection.ltr,
|
||||
),
|
||||
TestSemantics(
|
||||
id: 7,
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.isHeader,
|
||||
SemanticsFlag.namesRoute,
|
||||
],
|
||||
label: 'Title',
|
||||
textDirection: TextDirection.ltr,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
ignoreRect: true,
|
||||
ignoreTransform: true,
|
||||
),
|
||||
);
|
||||
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
testWidgets('Material3 - AppBar draws a light system bar for a dark background', (
|
||||
WidgetTester tester,
|
||||
|
Loading…
x
Reference in New Issue
Block a user