From 278c69c4d33ab1fe4ecf0b65db29a06075546da0 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Fri, 15 Jun 2018 20:13:15 -0700 Subject: [PATCH] Add semantics label property to Text widget (#18518) --- packages/flutter/lib/src/widgets/text.dart | 31 +++++++++++++++++++- packages/flutter/test/widgets/text_test.dart | 27 +++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/packages/flutter/lib/src/widgets/text.dart b/packages/flutter/lib/src/widgets/text.dart index c88e8be956..94fa551e0d 100644 --- a/packages/flutter/lib/src/widgets/text.dart +++ b/packages/flutter/lib/src/widgets/text.dart @@ -211,6 +211,7 @@ class Text extends StatelessWidget { this.overflow, this.textScaleFactor, this.maxLines, + this.semanticsLabel, }) : assert(data != null), textSpan = null, super(key: key); @@ -226,6 +227,7 @@ class Text extends StatelessWidget { this.overflow, this.textScaleFactor, this.maxLines, + this.semanticsLabel, }): assert(textSpan != null), data = null, super(key: key); @@ -305,13 +307,27 @@ class Text extends StatelessWidget { /// widget directly to entirely override the [DefaultTextStyle]. final int maxLines; + /// An alternative semantics label for this text. + /// + /// If present, the semantics of this widget will contain this value instead + /// of the actual text. + /// + /// This is useful for replacing abbreviations or shorthands will the full + /// text value: + /// + /// ```dart + /// new Text(r'$$', semanticsLabel: 'Double dollars') + /// + /// ``` + final String semanticsLabel; + @override Widget build(BuildContext context) { final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context); TextStyle effectiveTextStyle = style; if (style == null || style.inherit) effectiveTextStyle = defaultTextStyle.style.merge(style); - return new RichText( + Widget result = new RichText( textAlign: textAlign ?? defaultTextStyle.textAlign ?? TextAlign.start, textDirection: textDirection, // RichText uses Directionality.of to obtain a default if this is null. locale: locale, // RichText uses Localizations.localeOf to obtain a default if this is null @@ -325,6 +341,16 @@ class Text extends StatelessWidget { children: textSpan != null ? [textSpan] : null, ), ); + if (semanticsLabel != null) { + result = new Semantics( + textDirection: textDirection, + label: semanticsLabel, + child: new ExcludeSemantics( + child: result, + ) + ); + } + return result; } @override @@ -342,5 +368,8 @@ class Text extends StatelessWidget { properties.add(new EnumProperty('overflow', overflow, defaultValue: null)); properties.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null)); properties.add(new IntProperty('maxLines', maxLines, defaultValue: null)); + if (semanticsLabel != null) { + properties.add(new StringProperty('semanticsLabel', semanticsLabel)); + } } } diff --git a/packages/flutter/test/widgets/text_test.dart b/packages/flutter/test/widgets/text_test.dart index 0d4c97fe22..8eb74b58a8 100644 --- a/packages/flutter/test/widgets/text_test.dart +++ b/packages/flutter/test/widgets/text_test.dart @@ -5,6 +5,8 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/widgets.dart'; +import 'semantics_tester.dart'; + void main() { testWidgets('Text respects media query', (WidgetTester tester) async { await tester.pumpWidget(const MediaQuery( @@ -109,4 +111,29 @@ void main() { expect(text, isNotNull); expect(text.text.style.fontSize, 20.0); }); + + testWidgets('semanticsLabel can override text label', (WidgetTester tester) async { + final SemanticsTester semantics = new SemanticsTester(tester); + await tester.pumpWidget( + const Text('\$\$', semanticsLabel: 'Double dollars', textDirection: TextDirection.ltr) + ); + final TestSemantics expectedSemantics = new TestSemantics.root( + children: [ + new TestSemantics.rootChild( + label: 'Double dollars', + textDirection: TextDirection.ltr, + ), + ], + ); + expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true, ignoreRect: true)); + + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: const Text('\$\$', semanticsLabel: 'Double dollars')), + ); + + expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true, ignoreId: true, ignoreRect: true)); + semantics.dispose(); + }); }