Skip to content

Commit 190d776

Browse files
authored
Add Tooltip textAlign property (#103475)
1 parent aa39fa5 commit 190d776

4 files changed

Lines changed: 104 additions & 1 deletion

File tree

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,12 @@ class Tooltip extends StatefulWidget {
103103
this.excludeFromSemantics,
104104
this.decoration,
105105
this.textStyle,
106+
this.textAlign,
106107
this.waitDuration,
107108
this.showDuration,
108-
this.child,
109109
this.triggerMode,
110110
this.enableFeedback,
111+
this.child,
111112
}) : assert((message == null) != (richMessage == null), 'Either `message` or `richMessage` must be specified'),
112113
assert(
113114
richMessage == null || textStyle == null,
@@ -197,6 +198,13 @@ class Tooltip extends StatefulWidget {
197198
/// used with [Colors.black].
198199
final TextStyle? textStyle;
199200

201+
/// How the message of the tooltip is aligned horizontally.
202+
///
203+
/// If this property is null, then [TooltipThemeData.textAlign] is used.
204+
/// If [TooltipThemeData.textAlign] is also null, the default value is
205+
/// [TextAlign.start].
206+
final TextAlign? textAlign;
207+
200208
/// The length of time that a pointer must hover over a tooltip's widget
201209
/// before the tooltip will be shown.
202210
///
@@ -298,6 +306,7 @@ class Tooltip extends StatefulWidget {
298306
properties.add(DiagnosticsProperty<Duration>('show duration', showDuration, defaultValue: null));
299307
properties.add(DiagnosticsProperty<TooltipTriggerMode>('triggerMode', triggerMode, defaultValue: null));
300308
properties.add(FlagProperty('enableFeedback', value: enableFeedback, ifTrue: 'true', showName: true));
309+
properties.add(DiagnosticsProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
301310
}
302311
}
303312

@@ -317,12 +326,14 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
317326
static const bool _defaultExcludeFromSemantics = false;
318327
static const TooltipTriggerMode _defaultTriggerMode = TooltipTriggerMode.longPress;
319328
static const bool _defaultEnableFeedback = true;
329+
static const TextAlign _defaultTextAlign = TextAlign.start;
320330

321331
late double _height;
322332
late EdgeInsetsGeometry _padding;
323333
late EdgeInsetsGeometry _margin;
324334
late Decoration _decoration;
325335
late TextStyle _textStyle;
336+
late TextAlign _textAlign;
326337
late double _verticalOffset;
327338
late bool _preferBelow;
328339
late bool _excludeFromSemantics;
@@ -570,6 +581,7 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
570581
onExit: _mouseIsConnected ? (_) => _handleMouseExit() : null,
571582
decoration: _decoration,
572583
textStyle: _textStyle,
584+
textAlign: _textAlign,
573585
animation: CurvedAnimation(
574586
parent: _controller,
575587
curve: Curves.fastOutSlowIn,
@@ -691,6 +703,7 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
691703
_excludeFromSemantics = widget.excludeFromSemantics ?? tooltipTheme.excludeFromSemantics ?? _defaultExcludeFromSemantics;
692704
_decoration = widget.decoration ?? tooltipTheme.decoration ?? defaultDecoration;
693705
_textStyle = widget.textStyle ?? tooltipTheme.textStyle ?? defaultTextStyle;
706+
_textAlign = widget.textAlign ?? tooltipTheme.textAlign ?? _defaultTextAlign;
694707
_waitDuration = widget.waitDuration ?? tooltipTheme.waitDuration ?? _defaultWaitDuration;
695708
_showDuration = widget.showDuration ?? tooltipTheme.showDuration ?? _defaultShowDuration;
696709
_hoverShowDuration = widget.showDuration ?? tooltipTheme.showDuration ?? _defaultHoverShowDuration;
@@ -786,6 +799,7 @@ class _TooltipOverlay extends StatelessWidget {
786799
this.margin,
787800
this.decoration,
788801
this.textStyle,
802+
this.textAlign,
789803
required this.animation,
790804
required this.target,
791805
required this.verticalOffset,
@@ -800,6 +814,7 @@ class _TooltipOverlay extends StatelessWidget {
800814
final EdgeInsetsGeometry? margin;
801815
final Decoration? decoration;
802816
final TextStyle? textStyle;
817+
final TextAlign? textAlign;
803818
final Animation<double> animation;
804819
final Offset target;
805820
final double verticalOffset;
@@ -826,6 +841,7 @@ class _TooltipOverlay extends StatelessWidget {
826841
child: Text.rich(
827842
richMessage,
828843
style: textStyle,
844+
textAlign: textAlign,
829845
),
830846
),
831847
),

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class TooltipThemeData with Diagnosticable {
3535
this.excludeFromSemantics,
3636
this.decoration,
3737
this.textStyle,
38+
this.textAlign,
3839
this.waitDuration,
3940
this.showDuration,
4041
this.triggerMode,
@@ -79,6 +80,9 @@ class TooltipThemeData with Diagnosticable {
7980
/// The style to use for the message of [Tooltip]s.
8081
final TextStyle? textStyle;
8182

83+
/// The [TextAlign] to use for the message of [Tooltip]s.
84+
final TextAlign? textAlign;
85+
8286
/// The length of time that a pointer must hover over a tooltip's widget
8387
/// before the tooltip will be shown.
8488
final Duration? waitDuration;
@@ -113,6 +117,7 @@ class TooltipThemeData with Diagnosticable {
113117
bool? excludeFromSemantics,
114118
Decoration? decoration,
115119
TextStyle? textStyle,
120+
TextAlign? textAlign,
116121
Duration? waitDuration,
117122
Duration? showDuration,
118123
TooltipTriggerMode? triggerMode,
@@ -127,6 +132,7 @@ class TooltipThemeData with Diagnosticable {
127132
excludeFromSemantics: excludeFromSemantics ?? this.excludeFromSemantics,
128133
decoration: decoration ?? this.decoration,
129134
textStyle: textStyle ?? this.textStyle,
135+
textAlign: textAlign ?? this.textAlign,
130136
waitDuration: waitDuration ?? this.waitDuration,
131137
showDuration: showDuration ?? this.showDuration,
132138
triggerMode: triggerMode ?? this.triggerMode,
@@ -152,6 +158,7 @@ class TooltipThemeData with Diagnosticable {
152158
excludeFromSemantics: t < 0.5 ? a?.excludeFromSemantics : b?.excludeFromSemantics,
153159
decoration: Decoration.lerp(a?.decoration, b?.decoration, t),
154160
textStyle: TextStyle.lerp(a?.textStyle, b?.textStyle, t),
161+
textAlign: t < 0.5 ? a?.textAlign: b?.textAlign,
155162
);
156163
}
157164

@@ -165,6 +172,7 @@ class TooltipThemeData with Diagnosticable {
165172
excludeFromSemantics,
166173
decoration,
167174
textStyle,
175+
textAlign,
168176
waitDuration,
169177
showDuration,
170178
triggerMode,
@@ -186,6 +194,7 @@ class TooltipThemeData with Diagnosticable {
186194
&& other.excludeFromSemantics == excludeFromSemantics
187195
&& other.decoration == decoration
188196
&& other.textStyle == textStyle
197+
&& other.textAlign == textAlign
189198
&& other.waitDuration == waitDuration
190199
&& other.showDuration == showDuration
191200
&& other.triggerMode == triggerMode
@@ -203,6 +212,7 @@ class TooltipThemeData with Diagnosticable {
203212
properties.add(FlagProperty('semantics', value: excludeFromSemantics, ifTrue: 'excluded', showName: true));
204213
properties.add(DiagnosticsProperty<Decoration>('decoration', decoration, defaultValue: null));
205214
properties.add(DiagnosticsProperty<TextStyle>('textStyle', textStyle, defaultValue: null));
215+
properties.add(DiagnosticsProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
206216
properties.add(DiagnosticsProperty<Duration>('wait duration', waitDuration, defaultValue: null));
207217
properties.add(DiagnosticsProperty<Duration>('show duration', showDuration, defaultValue: null));
208218
properties.add(DiagnosticsProperty<TooltipTriggerMode>('triggerMode', triggerMode, defaultValue: null));

packages/flutter/test/material/tooltip_test.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,41 @@ void main() {
677677
expect(textStyle.decoration, TextDecoration.underline);
678678
});
679679

680+
testWidgets('Custom tooltip message textAlign', (WidgetTester tester) async {
681+
Future<void> pumpTooltipWithTextAlign({TextAlign? textAlign}) async {
682+
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
683+
await tester.pumpWidget(
684+
MaterialApp(
685+
home: Tooltip(
686+
key: tooltipKey,
687+
textAlign: textAlign,
688+
message: tooltipText,
689+
child: Container(
690+
width: 100.0,
691+
height: 100.0,
692+
color: Colors.green[500],
693+
),
694+
),
695+
),
696+
);
697+
tooltipKey.currentState?.ensureTooltipVisible();
698+
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
699+
}
700+
701+
// Default value should be TextAlign.start
702+
await pumpTooltipWithTextAlign();
703+
TextAlign textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
704+
expect(textAlign, TextAlign.start);
705+
706+
await pumpTooltipWithTextAlign(textAlign: TextAlign.center);
707+
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
708+
expect(textAlign, TextAlign.center);
709+
710+
await pumpTooltipWithTextAlign(textAlign: TextAlign.end);
711+
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
712+
expect(textAlign, TextAlign.end);
713+
});
714+
680715
testWidgets('Tooltip overlay respects ambient Directionality', (WidgetTester tester) async {
681716
// Regression test for https://github.com/flutter/flutter/issues/40702.
682717
Widget buildApp(String text, TextDirection textDirection) {

packages/flutter/test/material/tooltip_theme_test.dart

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ void main() {
4646
expect(theme.excludeFromSemantics, null);
4747
expect(theme.decoration, null);
4848
expect(theme.textStyle, null);
49+
expect(theme.textAlign, null);
4950
expect(theme.waitDuration, null);
5051
expect(theme.showDuration, null);
5152
expect(theme.triggerMode, null);
@@ -78,6 +79,7 @@ void main() {
7879
excludeFromSemantics: true,
7980
decoration: BoxDecoration(color: Color(0xffffffff)),
8081
textStyle: TextStyle(decoration: TextDecoration.underline),
82+
textAlign: TextAlign.center,
8183
waitDuration: wait,
8284
showDuration: show,
8385
triggerMode: triggerMode,
@@ -97,6 +99,7 @@ void main() {
9799
'semantics: excluded',
98100
'decoration: BoxDecoration(color: Color(0xffffffff))',
99101
'textStyle: TextStyle(inherit: true, decoration: TextDecoration.underline)',
102+
'textAlign: TextAlign.center',
100103
'wait duration: $wait',
101104
'show duration: $show',
102105
'triggerMode: $triggerMode',
@@ -651,6 +654,45 @@ void main() {
651654
expect(textStyle.decoration, TextDecoration.underline);
652655
});
653656

657+
testWidgets('Tooltip message textAlign - TooltipTheme', (WidgetTester tester) async {
658+
Future<void> pumpTooltipWithTextAlign({TextAlign? textAlign}) async {
659+
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
660+
await tester.pumpWidget(
661+
MaterialApp(
662+
home: TooltipTheme(
663+
data: TooltipThemeData(
664+
textAlign: textAlign,
665+
),
666+
child: Tooltip(
667+
key: tooltipKey,
668+
message: tooltipText,
669+
child: Container(
670+
width: 100.0,
671+
height: 100.0,
672+
color: Colors.green[500],
673+
),
674+
),
675+
),
676+
),
677+
);
678+
tooltipKey.currentState?.ensureTooltipVisible();
679+
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
680+
}
681+
682+
// Default value should be TextAlign.start
683+
await pumpTooltipWithTextAlign();
684+
TextAlign textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
685+
expect(textAlign, TextAlign.start);
686+
687+
await pumpTooltipWithTextAlign(textAlign: TextAlign.center);
688+
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
689+
expect(textAlign, TextAlign.center);
690+
691+
await pumpTooltipWithTextAlign(textAlign: TextAlign.end);
692+
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
693+
expect(textAlign, TextAlign.end);
694+
});
695+
654696
testWidgets('Tooltip decoration - ThemeData.tooltipTheme', (WidgetTester tester) async {
655697
final GlobalKey key = GlobalKey();
656698
const Decoration customDecoration = ShapeDecoration(

0 commit comments

Comments
 (0)