mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
The raw widget used to make a [Tooltip](https://api.flutter.dev/flutter/material/Tooltip-class.html). Design doc: flutter.dev/go/codeshare-tooltip ## Constructor ```dart class RawTooltip extends StatefulWidget { const RawTooltip({ super.key, required this.semanticsTooltip, required this.tooltipBuilder, this.enableTapToDismiss = true, this.triggerMode = TooltipTriggerMode.longPress, this.enableFeedback = true, this.onTriggered, this.hoverDelay = Duration.zero, this.touchDelay = const Duration(milliseconds: 1500), this.dismissDelay = const Duration(milliseconds: 100), this.animationStyle = _kDefaultAnimationStyle, this.positionDelegate, required this.child, }); ``` ## Properties ```dart final String semanticsTooltip; final TooltipComponentBuilder tooltipBuilder; final Duration hoverDelay; final Duration touchDelay; final Duration dismissDelay; final bool enableTapToDismiss; final TooltipTriggerMode triggerMode; final bool enableFeedback; final TooltipTriggeredCallback? onTriggered; final AnimationStyle animationStyle; final TooltipPositionDelegate? positionDelegate; final Widget child; ``` Part of [☂️ Reinforcement: Add more basic components to the core framework](https://github.com/flutter/flutter/issues/97496) Part of [☂️ Reinforcement: Refactor widgets from design into the core before decoupling](https://github.com/flutter/flutter/issues/53059) Fixes [Custom Overlay for Tooltip Widget ](https://github.com/flutter/flutter/issues/45034) --------- Co-authored-by: Tong Mu <dkwingsmt@users.noreply.github.com> Co-authored-by: chunhtai <47866232+chunhtai@users.noreply.github.com>
595 lines
22 KiB
Dart
595 lines
22 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
/// @docImport 'package:flutter/widgets.dart';
|
|
///
|
|
/// @docImport 'app.dart';
|
|
/// @docImport 'floating_action_button.dart';
|
|
/// @docImport 'icon_button.dart';
|
|
/// @docImport 'popup_menu.dart';
|
|
library;
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
import 'colors.dart';
|
|
import 'text_theme.dart';
|
|
import 'theme.dart';
|
|
import 'tooltip_theme.dart';
|
|
import 'tooltip_visibility.dart';
|
|
|
|
/// A Material Design tooltip.
|
|
///
|
|
/// Tooltips provide text labels which help explain the function of a button or
|
|
/// other user interface action. Wrap the button in a [Tooltip] widget and provide
|
|
/// a message which will be shown when the widget is long pressed.
|
|
///
|
|
/// Many widgets, such as [IconButton], [FloatingActionButton], and
|
|
/// [PopupMenuButton] have a `tooltip` property that, when non-null, causes the
|
|
/// widget to include a [Tooltip] in its build.
|
|
///
|
|
/// Tooltips improve the accessibility of visual widgets by proving a textual
|
|
/// representation of the widget, which, for example, can be vocalized by a
|
|
/// screen reader.
|
|
///
|
|
/// {@youtube 560 315 https://www.youtube.com/watch?v=EeEfD5fI-5Q}
|
|
///
|
|
/// {@tool dartpad}
|
|
/// This example show a basic [Tooltip] which has a [Text] as child.
|
|
/// [message] contains your label to be shown by the tooltip when
|
|
/// the child that Tooltip wraps is hovered over on web or desktop. On mobile,
|
|
/// the tooltip is shown when the widget is long pressed.
|
|
///
|
|
/// This tooltip will default to showing above the [Text] instead of below
|
|
/// because its ambient [TooltipThemeData.preferBelow] is false.
|
|
/// (See the use of [MaterialApp.theme].)
|
|
/// Setting that piece of theme data is recommended to avoid having a finger or
|
|
/// cursor hide the tooltip. For other ways to set that piece of theme data see:
|
|
///
|
|
/// * [Theme.data], [ThemeData.tooltipTheme]
|
|
/// * [TooltipTheme.data]
|
|
///
|
|
/// or it can be set directly on each tooltip with [Tooltip.preferBelow].
|
|
///
|
|
/// ** See code in examples/api/lib/material/tooltip/tooltip.0.dart **
|
|
/// {@end-tool}
|
|
///
|
|
/// {@tool dartpad}
|
|
/// This example covers most of the attributes available in Tooltip.
|
|
/// `decoration` has been used to give a gradient and borderRadius to Tooltip.
|
|
/// `constraints` has been used to set the minimum width of the Tooltip.
|
|
/// `preferBelow` is true; the tooltip will prefer showing below [Tooltip]'s child widget.
|
|
/// However, it may show the tooltip above if there's not enough space
|
|
/// below the widget.
|
|
/// `textStyle` has been used to set the font size of the 'message'.
|
|
/// `showDuration` accepts a Duration to continue showing the message after the long
|
|
/// press has been released or the mouse pointer exits the child widget.
|
|
/// `waitDuration` accepts a Duration for which a mouse pointer has to hover over the child
|
|
/// widget before the tooltip is shown.
|
|
///
|
|
/// ** See code in examples/api/lib/material/tooltip/tooltip.1.dart **
|
|
/// {@end-tool}
|
|
///
|
|
/// {@tool dartpad}
|
|
/// This example shows a rich [Tooltip] that specifies the [richMessage]
|
|
/// parameter instead of the [message] parameter (only one of these may be
|
|
/// non-null. Any [InlineSpan] can be specified for the [richMessage] attribute,
|
|
/// including [WidgetSpan].
|
|
///
|
|
/// ** See code in examples/api/lib/material/tooltip/tooltip.2.dart **
|
|
/// {@end-tool}
|
|
///
|
|
/// {@tool dartpad}
|
|
/// This example shows how [Tooltip] can be shown manually with [TooltipTriggerMode.manual]
|
|
/// by calling the [TooltipState.ensureTooltipVisible] function.
|
|
///
|
|
/// ** See code in examples/api/lib/material/tooltip/tooltip.3.dart **
|
|
/// {@end-tool}
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * <https://material.io/design/components/tooltips.html>
|
|
/// * [TooltipTheme] or [ThemeData.tooltipTheme]
|
|
/// * [TooltipVisibility]
|
|
class Tooltip extends StatefulWidget {
|
|
/// Creates a tooltip.
|
|
///
|
|
/// By default, tooltips should adhere to the
|
|
/// [Material specification](https://material.io/design/components/tooltips.html#spec).
|
|
/// If the optional constructor parameters are not defined, the values
|
|
/// provided by [TooltipTheme.of] will be used if a [TooltipTheme] is present
|
|
/// or specified in [ThemeData].
|
|
///
|
|
/// All parameters that are defined in the constructor will
|
|
/// override the default values _and_ the values in [TooltipTheme.of].
|
|
///
|
|
/// Only one of [message] and [richMessage] may be non-null.
|
|
const Tooltip({
|
|
super.key,
|
|
this.message,
|
|
this.richMessage,
|
|
@Deprecated(
|
|
'Use Tooltip.constraints instead. '
|
|
'This feature was deprecated after v3.30.0-0.1.pre.',
|
|
)
|
|
this.height,
|
|
this.constraints,
|
|
this.padding,
|
|
this.margin,
|
|
this.verticalOffset,
|
|
this.preferBelow,
|
|
this.excludeFromSemantics,
|
|
this.decoration,
|
|
this.textStyle,
|
|
this.textAlign,
|
|
this.waitDuration,
|
|
this.showDuration,
|
|
this.exitDuration,
|
|
this.enableTapToDismiss = true,
|
|
this.triggerMode,
|
|
this.enableFeedback,
|
|
this.onTriggered,
|
|
this.mouseCursor,
|
|
this.ignorePointer,
|
|
this.positionDelegate,
|
|
this.child,
|
|
}) : assert(
|
|
(message == null) != (richMessage == null),
|
|
'Either `message` or `richMessage` must be specified',
|
|
),
|
|
assert(
|
|
height == null || constraints == null,
|
|
'Only one of `height` and `constraints` may be specified.',
|
|
);
|
|
|
|
/// The text to display in the tooltip.
|
|
///
|
|
/// Only one of [message] and [richMessage] may be non-null.
|
|
final String? message;
|
|
|
|
/// The rich text to display in the tooltip.
|
|
///
|
|
/// Only one of [message] and [richMessage] may be non-null.
|
|
final InlineSpan? richMessage;
|
|
|
|
/// The minimum height of the [Tooltip]'s message.
|
|
@Deprecated(
|
|
'Use Tooltip.constraints instead. '
|
|
'This feature was deprecated after v3.30.0-0.1.pre.',
|
|
)
|
|
final double? height;
|
|
|
|
/// Constrains the size of the [Tooltip]'s message.
|
|
///
|
|
/// If null, then the [TooltipThemeData.constraints] of the ambient [ThemeData.tooltipTheme]
|
|
/// will be used. If that is also null, then a default value will be picked based on the current
|
|
/// platform. For desktop platforms, the default value is `BoxConstraints(minHeight: 24.0)`,
|
|
/// while for mobile platforms the default value is `BoxConstraints(minHeight: 32.0)`.
|
|
final BoxConstraints? constraints;
|
|
|
|
/// The amount of space by which to inset the [Tooltip]'s message.
|
|
///
|
|
/// On mobile, defaults to 16.0 logical pixels horizontally and 4.0 vertically.
|
|
/// On desktop, defaults to 8.0 logical pixels horizontally and 4.0 vertically.
|
|
final EdgeInsetsGeometry? padding;
|
|
|
|
/// The empty space that surrounds the tooltip.
|
|
///
|
|
/// Defines the tooltip's outer [Container.margin]. By default, a
|
|
/// long tooltip will span the width of its window. If long enough,
|
|
/// a tooltip might also span the window's height. This property allows
|
|
/// one to define how much space the tooltip must be inset from the edges
|
|
/// of their display window.
|
|
///
|
|
/// If this property is null, then [TooltipThemeData.margin] is used.
|
|
/// If [TooltipThemeData.margin] is also null, the default margin is
|
|
/// 0.0 logical pixels on all sides.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [constraints], which allow setting an explicit size for the tooltip.
|
|
final EdgeInsetsGeometry? margin;
|
|
|
|
/// The vertical gap between the widget and the displayed tooltip.
|
|
///
|
|
/// When [preferBelow] is set to true and tooltips have sufficient space to
|
|
/// display themselves, this property defines how much vertical space
|
|
/// tooltips will position themselves under their corresponding widgets.
|
|
/// Otherwise, tooltips will position themselves above their corresponding
|
|
/// widgets with the given offset.
|
|
final double? verticalOffset;
|
|
|
|
/// Whether the tooltip defaults to being displayed below the widget.
|
|
///
|
|
/// If there is insufficient space to display the tooltip in
|
|
/// the preferred direction, the tooltip will be displayed in the opposite
|
|
/// direction.
|
|
///
|
|
/// If this property is null, then [TooltipThemeData.preferBelow] is used.
|
|
/// If that is also null, the default value is true.
|
|
///
|
|
/// Applying [TooltipThemeData.preferBelow]: `false` for the entire app
|
|
/// is recommended to avoid having a finger or cursor hide a tooltip.
|
|
final bool? preferBelow;
|
|
|
|
/// Whether the tooltip's [message] or [richMessage] should be excluded from
|
|
/// the semantics tree.
|
|
///
|
|
/// Defaults to false. A tooltip will add a [Semantics] label that is set to
|
|
/// [Tooltip.message] if non-null, or the plain text value of
|
|
/// [Tooltip.richMessage] otherwise. Set this property to true if the app is
|
|
/// going to provide its own custom semantics label.
|
|
final bool? excludeFromSemantics;
|
|
|
|
/// The widget below this widget in the tree.
|
|
///
|
|
/// {@macro flutter.widgets.ProxyWidget.child}
|
|
final Widget? child;
|
|
|
|
/// Specifies the tooltip's shape and background color.
|
|
///
|
|
/// The tooltip shape defaults to a rounded rectangle with a border radius of
|
|
/// 4.0. Tooltips will also default to an opacity of 90% and with the color
|
|
/// [Colors.grey]\[700\] if [ThemeData.brightness] is [Brightness.light], and
|
|
/// [Colors.white] if it is [Brightness.dark].
|
|
final Decoration? decoration;
|
|
|
|
/// The style to use for the message of the tooltip.
|
|
///
|
|
/// If null, the message's [TextStyle] will be determined based on
|
|
/// [ThemeData]. If [ThemeData.brightness] is set to [Brightness.dark],
|
|
/// [TextTheme.bodyMedium] of [ThemeData.textTheme] will be used with
|
|
/// [Colors.white]. Otherwise, if [ThemeData.brightness] is set to
|
|
/// [Brightness.light], [TextTheme.bodyMedium] of [ThemeData.textTheme] will be
|
|
/// 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;
|
|
|
|
/// {@macro flutter.widgets.RawTooltip.hoverDelay}
|
|
final Duration? waitDuration;
|
|
|
|
/// {@macro flutter.widgets.RawTooltip.touchDelay}
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [exitDuration], which allows configuring the time until a pointer
|
|
/// disappears when hovering.
|
|
final Duration? showDuration;
|
|
|
|
/// {@macro flutter.widgets.RawTooltip.dismissDelay}
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [showDuration], which allows configuring the length of time that a
|
|
/// tooltip will be visible after touch events are released.
|
|
final Duration? exitDuration;
|
|
|
|
/// {@macro flutter.widgets.RawTooltip.enableTapToDismiss}
|
|
final bool enableTapToDismiss;
|
|
|
|
/// {@macro flutter.widgets.RawTooltip.triggerMode}
|
|
///
|
|
/// If this property is null, then [TooltipThemeData.triggerMode] is used.
|
|
/// If [TooltipThemeData.triggerMode] is also null, the default mode is
|
|
/// [TooltipTriggerMode.longPress].
|
|
final TooltipTriggerMode? triggerMode;
|
|
|
|
/// {@macro flutter.widgets.RawTooltip.enableFeedback}
|
|
final bool? enableFeedback;
|
|
|
|
/// {@macro flutter.widgets.RawTooltip.onTriggered}
|
|
final TooltipTriggeredCallback? onTriggered;
|
|
|
|
/// The cursor for a mouse pointer when it enters or is hovering over the
|
|
/// widget.
|
|
///
|
|
/// If this property is null, [MouseCursor.defer] will be used.
|
|
final MouseCursor? mouseCursor;
|
|
|
|
/// Whether this tooltip should be invisible to hit testing.
|
|
///
|
|
/// If no value is passed, pointer events are ignored unless the tooltip has a
|
|
/// [richMessage] instead of a [message].
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [IgnorePointer], for more information about how pointer events are
|
|
/// handled or ignored.
|
|
final bool? ignorePointer;
|
|
|
|
/// {@macro flutter.widgets.RawTooltip.positionDelegate}
|
|
final TooltipPositionDelegate? positionDelegate;
|
|
|
|
/// {@macro flutter.widgets.RawTooltip.dismissAllToolTips}
|
|
static bool dismissAllToolTips() {
|
|
return RawTooltip.dismissAllToolTips();
|
|
}
|
|
|
|
@override
|
|
State<Tooltip> createState() => TooltipState();
|
|
|
|
@override
|
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
super.debugFillProperties(properties);
|
|
properties.add(
|
|
StringProperty(
|
|
'message',
|
|
message,
|
|
showName: message == null,
|
|
defaultValue: message == null ? null : kNoDefaultValue,
|
|
),
|
|
);
|
|
properties.add(
|
|
StringProperty(
|
|
'richMessage',
|
|
richMessage?.toPlainText(),
|
|
showName: richMessage == null,
|
|
defaultValue: richMessage == null ? null : kNoDefaultValue,
|
|
),
|
|
);
|
|
properties.add(DoubleProperty('height', height, defaultValue: null));
|
|
properties.add(
|
|
DiagnosticsProperty<BoxConstraints>('constraints', constraints, defaultValue: null),
|
|
);
|
|
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
|
|
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('margin', margin, defaultValue: null));
|
|
properties.add(DoubleProperty('vertical offset', verticalOffset, defaultValue: null));
|
|
properties.add(
|
|
FlagProperty(
|
|
'position',
|
|
value: preferBelow,
|
|
ifTrue: 'below',
|
|
ifFalse: 'above',
|
|
showName: true,
|
|
),
|
|
);
|
|
properties.add(
|
|
FlagProperty('semantics', value: excludeFromSemantics, ifTrue: 'excluded', showName: true),
|
|
);
|
|
properties.add(
|
|
DiagnosticsProperty<Duration>('wait duration', waitDuration, defaultValue: null),
|
|
);
|
|
properties.add(
|
|
DiagnosticsProperty<Duration>('show duration', showDuration, defaultValue: null),
|
|
);
|
|
properties.add(
|
|
DiagnosticsProperty<Duration>('exit duration', exitDuration, 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));
|
|
properties.add(
|
|
DiagnosticsProperty<TooltipPositionDelegate>(
|
|
'positionDelegate',
|
|
positionDelegate,
|
|
defaultValue: null,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Contains the state for a [Tooltip].
|
|
///
|
|
/// This class can be used to programmatically show the Tooltip, see the
|
|
/// [ensureTooltipVisible] method.
|
|
class TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
|
|
static const double _defaultVerticalOffset = 24.0;
|
|
static const bool _defaultPreferBelow = true;
|
|
static const EdgeInsetsGeometry _defaultMargin = EdgeInsets.zero;
|
|
static const Duration _defaultShowDuration = Duration(milliseconds: 1500);
|
|
static const Duration _defaultExitDuration = Duration(milliseconds: 100);
|
|
static const Duration _defaultWaitDuration = Duration.zero;
|
|
static const bool _defaultExcludeFromSemantics = false;
|
|
static const TooltipTriggerMode _defaultTriggerMode = TooltipTriggerMode.longPress;
|
|
static const bool _defaultEnableFeedback = true;
|
|
static const TextAlign _defaultTextAlign = TextAlign.start;
|
|
|
|
final GlobalKey<RawTooltipState> _tooltipKey = GlobalKey<RawTooltipState>();
|
|
|
|
// From InheritedWidgets
|
|
late bool _visible;
|
|
late TooltipThemeData _tooltipTheme;
|
|
|
|
/// {@macro flutter.widgets.RawTooltipState.ensureTooltipVisible}
|
|
bool ensureTooltipVisible() {
|
|
return _tooltipKey.currentState?.ensureTooltipVisible() ?? false;
|
|
}
|
|
|
|
@protected
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
_visible = TooltipVisibility.of(context);
|
|
_tooltipTheme = TooltipTheme.of(context);
|
|
}
|
|
|
|
// https://material.io/components/tooltips#specs
|
|
double _getDefaultTooltipHeight() {
|
|
return switch (Theme.of(context).platform) {
|
|
TargetPlatform.macOS || TargetPlatform.linux || TargetPlatform.windows => 24.0,
|
|
TargetPlatform.android || TargetPlatform.fuchsia || TargetPlatform.iOS => 32.0,
|
|
};
|
|
}
|
|
|
|
EdgeInsets _getDefaultPadding() {
|
|
return switch (Theme.of(context).platform) {
|
|
TargetPlatform.macOS ||
|
|
TargetPlatform.linux ||
|
|
TargetPlatform.windows => const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
|
TargetPlatform.android ||
|
|
TargetPlatform.fuchsia ||
|
|
TargetPlatform.iOS => const EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
|
|
};
|
|
}
|
|
|
|
static double _getDefaultFontSize(TargetPlatform platform) {
|
|
return switch (platform) {
|
|
TargetPlatform.macOS || TargetPlatform.linux || TargetPlatform.windows => 12.0,
|
|
TargetPlatform.android || TargetPlatform.fuchsia || TargetPlatform.iOS => 14.0,
|
|
};
|
|
}
|
|
|
|
Offset _getDefaultPositionDelegate(TooltipPositionContext context) {
|
|
final double effectiveVerticalOffset =
|
|
widget.verticalOffset ?? _tooltipTheme.verticalOffset ?? _defaultVerticalOffset;
|
|
final bool effectivePreferBelow =
|
|
widget.preferBelow ?? _tooltipTheme.preferBelow ?? _defaultPreferBelow;
|
|
final resolvedContext = TooltipPositionContext(
|
|
target: context.target,
|
|
targetSize: context.targetSize,
|
|
tooltipSize: context.tooltipSize,
|
|
overlaySize: context.overlaySize,
|
|
verticalOffset: effectiveVerticalOffset,
|
|
preferBelow: effectivePreferBelow,
|
|
);
|
|
return widget.positionDelegate?.call(resolvedContext) ??
|
|
positionDependentBox(
|
|
size: context.overlaySize,
|
|
childSize: context.tooltipSize,
|
|
target: context.target,
|
|
verticalOffset: effectiveVerticalOffset,
|
|
preferBelow: effectivePreferBelow,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final (TextStyle defaultTextStyle, BoxDecoration defaultDecoration) = switch (Theme.of(
|
|
context,
|
|
)) {
|
|
ThemeData(
|
|
brightness: Brightness.dark,
|
|
:final TextTheme textTheme,
|
|
:final TargetPlatform platform,
|
|
) =>
|
|
(
|
|
textTheme.bodyMedium!.copyWith(
|
|
color: Colors.black,
|
|
fontSize: _getDefaultFontSize(platform),
|
|
),
|
|
BoxDecoration(
|
|
color: Colors.white.withOpacity(0.9),
|
|
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
|
),
|
|
),
|
|
ThemeData(
|
|
brightness: Brightness.light,
|
|
:final TextTheme textTheme,
|
|
:final TargetPlatform platform,
|
|
) =>
|
|
(
|
|
textTheme.bodyMedium!.copyWith(
|
|
color: Colors.white,
|
|
fontSize: _getDefaultFontSize(platform),
|
|
),
|
|
BoxDecoration(
|
|
color: Colors.grey[700]!.withOpacity(0.9),
|
|
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
|
),
|
|
),
|
|
};
|
|
final defaultConstraints = BoxConstraints(
|
|
minHeight: widget.height ?? _tooltipTheme.height ?? _getDefaultTooltipHeight(),
|
|
);
|
|
|
|
final Widget tooltipBox = _TooltipBox(
|
|
constraints: widget.constraints ?? _tooltipTheme.constraints ?? defaultConstraints,
|
|
textStyle: widget.textStyle ?? _tooltipTheme.textStyle ?? defaultTextStyle,
|
|
textAlign: widget.textAlign ?? _tooltipTheme.textAlign ?? _defaultTextAlign,
|
|
decoration: widget.decoration ?? _tooltipTheme.decoration ?? defaultDecoration,
|
|
padding: widget.padding ?? _tooltipTheme.padding ?? _getDefaultPadding(),
|
|
margin: widget.margin ?? _tooltipTheme.margin ?? _defaultMargin,
|
|
richMessage: widget.richMessage ?? TextSpan(text: widget.message),
|
|
);
|
|
|
|
Widget effectiveChild = MouseRegion(
|
|
cursor: widget.mouseCursor ?? MouseCursor.defer,
|
|
child: widget.child ?? const SizedBox.shrink(),
|
|
);
|
|
|
|
final bool excludeFromSemantics =
|
|
widget.excludeFromSemantics ??
|
|
_tooltipTheme.excludeFromSemantics ??
|
|
_defaultExcludeFromSemantics;
|
|
|
|
if (_visible) {
|
|
effectiveChild = RawTooltip(
|
|
key: _tooltipKey,
|
|
semanticsTooltip: excludeFromSemantics
|
|
? null
|
|
: widget.message ?? widget.richMessage?.toPlainText() ?? '',
|
|
tooltipBuilder: (BuildContext context, Animation<double> animation) => IgnorePointer(
|
|
ignoring: widget.ignorePointer ?? widget.message != null,
|
|
child: FadeTransition(opacity: animation, child: tooltipBox),
|
|
),
|
|
touchDelay: widget.showDuration ?? _tooltipTheme.showDuration ?? _defaultShowDuration,
|
|
triggerMode: widget.triggerMode ?? _tooltipTheme.triggerMode ?? _defaultTriggerMode,
|
|
enableFeedback:
|
|
widget.enableFeedback ?? _tooltipTheme.enableFeedback ?? _defaultEnableFeedback,
|
|
hoverDelay: widget.waitDuration ?? _tooltipTheme.waitDuration ?? _defaultWaitDuration,
|
|
enableTapToDismiss: widget.enableTapToDismiss,
|
|
onTriggered: widget.onTriggered,
|
|
dismissDelay: widget.exitDuration ?? _tooltipTheme.exitDuration ?? _defaultExitDuration,
|
|
positionDelegate: _getDefaultPositionDelegate,
|
|
child: effectiveChild,
|
|
);
|
|
}
|
|
|
|
return effectiveChild;
|
|
}
|
|
}
|
|
|
|
class _TooltipBox extends StatelessWidget {
|
|
const _TooltipBox({
|
|
required this.constraints,
|
|
required this.textStyle,
|
|
required this.textAlign,
|
|
required this.decoration,
|
|
required this.padding,
|
|
required this.margin,
|
|
required this.richMessage,
|
|
});
|
|
|
|
final BoxConstraints constraints;
|
|
final TextStyle textStyle;
|
|
final TextAlign textAlign;
|
|
final Decoration? decoration;
|
|
final EdgeInsetsGeometry? padding;
|
|
final EdgeInsetsGeometry? margin;
|
|
final InlineSpan richMessage;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ConstrainedBox(
|
|
constraints: constraints,
|
|
child: DefaultTextStyle(
|
|
style: textStyle,
|
|
textAlign: textAlign,
|
|
child: Container(
|
|
decoration: decoration,
|
|
padding: padding,
|
|
margin: margin,
|
|
child: Center(
|
|
widthFactor: 1.0,
|
|
heightFactor: 1.0,
|
|
child: Text.rich(richMessage, style: textStyle, textAlign: textAlign),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|