Skip to content

Commit

Permalink
Add Tooltip textAlign property (#103475)
Browse files Browse the repository at this point in the history
  • Loading branch information
bleroux authored May 12, 2022
1 parent aa39fa5 commit 190d776
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 1 deletion.
18 changes: 17 additions & 1 deletion packages/flutter/lib/src/material/tooltip.dart
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,12 @@ class Tooltip extends StatefulWidget {
this.excludeFromSemantics,
this.decoration,
this.textStyle,
this.textAlign,
this.waitDuration,
this.showDuration,
this.child,
this.triggerMode,
this.enableFeedback,
this.child,
}) : assert((message == null) != (richMessage == null), 'Either `message` or `richMessage` must be specified'),
assert(
richMessage == null || textStyle == null,
Expand Down Expand Up @@ -197,6 +198,13 @@ class Tooltip extends StatefulWidget {
/// used with [Colors.black].
final TextStyle? textStyle;

/// How the message of the tooltip is aligned horizontally.
///
/// If this property is null, then [TooltipThemeData.textAlign] is used.
/// If [TooltipThemeData.textAlign] is also null, the default value is
/// [TextAlign.start].
final TextAlign? textAlign;

/// The length of time that a pointer must hover over a tooltip's widget
/// before the tooltip will be shown.
///
Expand Down Expand Up @@ -298,6 +306,7 @@ class Tooltip extends StatefulWidget {
properties.add(DiagnosticsProperty<Duration>('show duration', showDuration, defaultValue: null));
properties.add(DiagnosticsProperty<TooltipTriggerMode>('triggerMode', triggerMode, defaultValue: null));
properties.add(FlagProperty('enableFeedback', value: enableFeedback, ifTrue: 'true', showName: true));
properties.add(DiagnosticsProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
}
}

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

late double _height;
late EdgeInsetsGeometry _padding;
late EdgeInsetsGeometry _margin;
late Decoration _decoration;
late TextStyle _textStyle;
late TextAlign _textAlign;
late double _verticalOffset;
late bool _preferBelow;
late bool _excludeFromSemantics;
Expand Down Expand Up @@ -570,6 +581,7 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
onExit: _mouseIsConnected ? (_) => _handleMouseExit() : null,
decoration: _decoration,
textStyle: _textStyle,
textAlign: _textAlign,
animation: CurvedAnimation(
parent: _controller,
curve: Curves.fastOutSlowIn,
Expand Down Expand Up @@ -691,6 +703,7 @@ class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
_excludeFromSemantics = widget.excludeFromSemantics ?? tooltipTheme.excludeFromSemantics ?? _defaultExcludeFromSemantics;
_decoration = widget.decoration ?? tooltipTheme.decoration ?? defaultDecoration;
_textStyle = widget.textStyle ?? tooltipTheme.textStyle ?? defaultTextStyle;
_textAlign = widget.textAlign ?? tooltipTheme.textAlign ?? _defaultTextAlign;
_waitDuration = widget.waitDuration ?? tooltipTheme.waitDuration ?? _defaultWaitDuration;
_showDuration = widget.showDuration ?? tooltipTheme.showDuration ?? _defaultShowDuration;
_hoverShowDuration = widget.showDuration ?? tooltipTheme.showDuration ?? _defaultHoverShowDuration;
Expand Down Expand Up @@ -786,6 +799,7 @@ class _TooltipOverlay extends StatelessWidget {
this.margin,
this.decoration,
this.textStyle,
this.textAlign,
required this.animation,
required this.target,
required this.verticalOffset,
Expand All @@ -800,6 +814,7 @@ class _TooltipOverlay extends StatelessWidget {
final EdgeInsetsGeometry? margin;
final Decoration? decoration;
final TextStyle? textStyle;
final TextAlign? textAlign;
final Animation<double> animation;
final Offset target;
final double verticalOffset;
Expand All @@ -826,6 +841,7 @@ class _TooltipOverlay extends StatelessWidget {
child: Text.rich(
richMessage,
style: textStyle,
textAlign: textAlign,
),
),
),
Expand Down
10 changes: 10 additions & 0 deletions packages/flutter/lib/src/material/tooltip_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class TooltipThemeData with Diagnosticable {
this.excludeFromSemantics,
this.decoration,
this.textStyle,
this.textAlign,
this.waitDuration,
this.showDuration,
this.triggerMode,
Expand Down Expand Up @@ -79,6 +80,9 @@ class TooltipThemeData with Diagnosticable {
/// The style to use for the message of [Tooltip]s.
final TextStyle? textStyle;

/// The [TextAlign] to use for the message of [Tooltip]s.
final TextAlign? textAlign;

/// The length of time that a pointer must hover over a tooltip's widget
/// before the tooltip will be shown.
final Duration? waitDuration;
Expand Down Expand Up @@ -113,6 +117,7 @@ class TooltipThemeData with Diagnosticable {
bool? excludeFromSemantics,
Decoration? decoration,
TextStyle? textStyle,
TextAlign? textAlign,
Duration? waitDuration,
Duration? showDuration,
TooltipTriggerMode? triggerMode,
Expand All @@ -127,6 +132,7 @@ class TooltipThemeData with Diagnosticable {
excludeFromSemantics: excludeFromSemantics ?? this.excludeFromSemantics,
decoration: decoration ?? this.decoration,
textStyle: textStyle ?? this.textStyle,
textAlign: textAlign ?? this.textAlign,
waitDuration: waitDuration ?? this.waitDuration,
showDuration: showDuration ?? this.showDuration,
triggerMode: triggerMode ?? this.triggerMode,
Expand All @@ -152,6 +158,7 @@ class TooltipThemeData with Diagnosticable {
excludeFromSemantics: t < 0.5 ? a?.excludeFromSemantics : b?.excludeFromSemantics,
decoration: Decoration.lerp(a?.decoration, b?.decoration, t),
textStyle: TextStyle.lerp(a?.textStyle, b?.textStyle, t),
textAlign: t < 0.5 ? a?.textAlign: b?.textAlign,
);
}

Expand All @@ -165,6 +172,7 @@ class TooltipThemeData with Diagnosticable {
excludeFromSemantics,
decoration,
textStyle,
textAlign,
waitDuration,
showDuration,
triggerMode,
Expand All @@ -186,6 +194,7 @@ class TooltipThemeData with Diagnosticable {
&& other.excludeFromSemantics == excludeFromSemantics
&& other.decoration == decoration
&& other.textStyle == textStyle
&& other.textAlign == textAlign
&& other.waitDuration == waitDuration
&& other.showDuration == showDuration
&& other.triggerMode == triggerMode
Expand All @@ -203,6 +212,7 @@ class TooltipThemeData with Diagnosticable {
properties.add(FlagProperty('semantics', value: excludeFromSemantics, ifTrue: 'excluded', showName: true));
properties.add(DiagnosticsProperty<Decoration>('decoration', decoration, defaultValue: null));
properties.add(DiagnosticsProperty<TextStyle>('textStyle', textStyle, defaultValue: null));
properties.add(DiagnosticsProperty<TextAlign>('textAlign', textAlign, defaultValue: null));
properties.add(DiagnosticsProperty<Duration>('wait duration', waitDuration, defaultValue: null));
properties.add(DiagnosticsProperty<Duration>('show duration', showDuration, defaultValue: null));
properties.add(DiagnosticsProperty<TooltipTriggerMode>('triggerMode', triggerMode, defaultValue: null));
Expand Down
35 changes: 35 additions & 0 deletions packages/flutter/test/material/tooltip_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,41 @@ void main() {
expect(textStyle.decoration, TextDecoration.underline);
});

testWidgets('Custom tooltip message textAlign', (WidgetTester tester) async {
Future<void> pumpTooltipWithTextAlign({TextAlign? textAlign}) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
MaterialApp(
home: Tooltip(
key: tooltipKey,
textAlign: textAlign,
message: tooltipText,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.green[500],
),
),
),
);
tooltipKey.currentState?.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
}

// Default value should be TextAlign.start
await pumpTooltipWithTextAlign();
TextAlign textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.start);

await pumpTooltipWithTextAlign(textAlign: TextAlign.center);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.center);

await pumpTooltipWithTextAlign(textAlign: TextAlign.end);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.end);
});

testWidgets('Tooltip overlay respects ambient Directionality', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/40702.
Widget buildApp(String text, TextDirection textDirection) {
Expand Down
42 changes: 42 additions & 0 deletions packages/flutter/test/material/tooltip_theme_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ void main() {
expect(theme.excludeFromSemantics, null);
expect(theme.decoration, null);
expect(theme.textStyle, null);
expect(theme.textAlign, null);
expect(theme.waitDuration, null);
expect(theme.showDuration, null);
expect(theme.triggerMode, null);
Expand Down Expand Up @@ -78,6 +79,7 @@ void main() {
excludeFromSemantics: true,
decoration: BoxDecoration(color: Color(0xffffffff)),
textStyle: TextStyle(decoration: TextDecoration.underline),
textAlign: TextAlign.center,
waitDuration: wait,
showDuration: show,
triggerMode: triggerMode,
Expand All @@ -97,6 +99,7 @@ void main() {
'semantics: excluded',
'decoration: BoxDecoration(color: Color(0xffffffff))',
'textStyle: TextStyle(inherit: true, decoration: TextDecoration.underline)',
'textAlign: TextAlign.center',
'wait duration: $wait',
'show duration: $show',
'triggerMode: $triggerMode',
Expand Down Expand Up @@ -651,6 +654,45 @@ void main() {
expect(textStyle.decoration, TextDecoration.underline);
});

testWidgets('Tooltip message textAlign - TooltipTheme', (WidgetTester tester) async {
Future<void> pumpTooltipWithTextAlign({TextAlign? textAlign}) async {
final GlobalKey<TooltipState> tooltipKey = GlobalKey<TooltipState>();
await tester.pumpWidget(
MaterialApp(
home: TooltipTheme(
data: TooltipThemeData(
textAlign: textAlign,
),
child: Tooltip(
key: tooltipKey,
message: tooltipText,
child: Container(
width: 100.0,
height: 100.0,
color: Colors.green[500],
),
),
),
),
);
tooltipKey.currentState?.ensureTooltipVisible();
await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0)
}

// Default value should be TextAlign.start
await pumpTooltipWithTextAlign();
TextAlign textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.start);

await pumpTooltipWithTextAlign(textAlign: TextAlign.center);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.center);

await pumpTooltipWithTextAlign(textAlign: TextAlign.end);
textAlign = tester.widget<Text>(find.text(tooltipText)).textAlign!;
expect(textAlign, TextAlign.end);
});

testWidgets('Tooltip decoration - ThemeData.tooltipTheme', (WidgetTester tester) async {
final GlobalKey key = GlobalKey();
const Decoration customDecoration = ShapeDecoration(
Expand Down

0 comments on commit 190d776

Please sign in to comment.