blob: 3f3c28dc539fd9afe8c33a3457e3c59b593a60ec [file] [log] [blame]
// 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.
import 'dart:ui' show lerpDouble;
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'theme.dart';
// Examples can assume:
// late BuildContext context;
/// Defines the visual properties of [Tooltip] widgets.
/// Used by [TooltipTheme] to control the visual properties of tooltips in a
/// widget subtree.
/// To obtain this configuration, use [TooltipTheme.of] to access the closest
/// ancestor [TooltipTheme] of the current [BuildContext].
/// See also:
/// * [TooltipTheme], an [InheritedWidget] that propagates the theme down its
/// subtree.
/// * [TooltipThemeData], which describes the actual configuration of a
/// tooltip theme.
class TooltipThemeData with Diagnosticable {
/// Creates the set of properties used to configure [Tooltip]s.
const TooltipThemeData({
/// The height of [Tooltip.child].
final double? height;
/// If provided, the amount of space by which to inset [Tooltip.child].
final EdgeInsetsGeometry? padding;
/// If provided, the amount of empty space to surround 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 is displayed below its widget by default.
/// If there is insufficient space to display the tooltip in the preferred
/// direction, the tooltip will be displayed in the opposite direction.
final bool? preferBelow;
/// Whether the [Tooltip.message] should be excluded from the semantics
/// tree.
/// By default, [Tooltip]s will add a [Semantics] label that is set to
/// [Tooltip.message]. Set this property to true if the app is going to
/// provide its own custom semantics label.
final bool? excludeFromSemantics;
/// The [Tooltip]'s shape and background color.
final Decoration? decoration;
/// 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;
/// The length of time that the tooltip will be shown once it has appeared.
final Duration? showDuration;
/// The [TooltipTriggerMode] that will show the tooltip.
final TooltipTriggerMode? triggerMode;
/// Whether the tooltip should provide acoustic and/or haptic feedback.
/// For example, on Android a tap will produce a clicking sound and a
/// long-press will produce a short vibration, when feedback is enabled.
/// This value is used if [Tooltip.enableFeedback] is null.
/// If this value is null, the default is true.
/// See also:
/// * [Feedback], for providing platform-specific feedback to certain actions.
final bool? enableFeedback;
/// Creates a copy of this object but with the given fields replaced with the
/// new values.
TooltipThemeData copyWith({
double? height,
EdgeInsetsGeometry? padding,
EdgeInsetsGeometry? margin,
double? verticalOffset,
bool? preferBelow,
bool? excludeFromSemantics,
Decoration? decoration,
TextStyle? textStyle,
TextAlign? textAlign,
Duration? waitDuration,
Duration? showDuration,
TooltipTriggerMode? triggerMode,
bool? enableFeedback,
}) {
return TooltipThemeData(
height: height ?? this.height,
padding: padding ?? this.padding,
margin: margin ?? this.margin,
verticalOffset: verticalOffset ?? this.verticalOffset,
preferBelow: preferBelow ?? this.preferBelow,
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,
enableFeedback: enableFeedback ?? this.enableFeedback,
/// Linearly interpolate between two tooltip themes.
/// If both arguments are null, then null is returned.
/// {@macro dart.ui.shadow.lerp}
static TooltipThemeData? lerp(TooltipThemeData? a, TooltipThemeData? b, double t) {
if (identical(a, b)) {
return a;
return TooltipThemeData(
height: lerpDouble(a?.height, b?.height, t),
padding: EdgeInsetsGeometry.lerp(a?.padding, b?.padding, t),
margin: EdgeInsetsGeometry.lerp(a?.margin, b?.margin, t),
verticalOffset: lerpDouble(a?.verticalOffset, b?.verticalOffset, t),
preferBelow: t < 0.5 ? a?.preferBelow: b?.preferBelow,
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,
int get hashCode => Object.hash(
bool operator==(Object other) {
if (identical(this, other)) {
return true;
if (other.runtimeType != runtimeType) {
return false;
return other is TooltipThemeData
&& other.height == height
&& other.padding == padding
&& other.margin == margin
&& other.verticalOffset == verticalOffset
&& other.preferBelow == preferBelow
&& other.excludeFromSemantics == excludeFromSemantics
&& other.decoration == decoration
&& other.textStyle == textStyle
&& other.textAlign == textAlign
&& other.waitDuration == waitDuration
&& other.showDuration == showDuration
&& other.triggerMode == triggerMode
&& other.enableFeedback == enableFeedback;
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
properties.add(DoubleProperty('height', height, 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<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));
properties.add(FlagProperty('enableFeedback', value: enableFeedback, ifTrue: 'true', showName: true));
/// An inherited widget that defines the configuration for
/// [Tooltip]s in this widget's subtree.
/// Values specified here are used for [Tooltip] properties that are not
/// given an explicit non-null value.
/// {@tool snippet}
/// Here is an example of a tooltip theme that applies a blue foreground
/// with non-rounded corners.
/// ```dart
/// TooltipTheme(
/// data: TooltipThemeData(
/// decoration: BoxDecoration(
/// color:,
/// borderRadius:,
/// ),
/// ),
/// child: Tooltip(
/// message: 'Example tooltip',
/// child: IconButton(
/// iconSize: 36.0,
/// icon: const Icon(Icons.touch_app),
/// onPressed: () {},
/// ),
/// ),
/// )
/// ```
/// {@end-tool}
/// See also:
/// * [TooltipVisibility], which can be used to visually disable descendant [Tooltip]s.
class TooltipTheme extends InheritedTheme {
/// Creates a tooltip theme that controls the configurations for
/// [Tooltip].
/// The data argument must not be null.
const TooltipTheme({
required super.child,
/// The properties for descendant [Tooltip] widgets.
final TooltipThemeData data;
/// Returns the [data] from the closest [TooltipTheme] ancestor. If there is
/// no ancestor, it returns [ThemeData.tooltipTheme]. Applications can assume
/// that the returned value will not be null.
/// Typical usage is as follows:
/// ```dart
/// TooltipThemeData theme = TooltipTheme.of(context);
/// ```
static TooltipThemeData of(BuildContext context) {
final TooltipTheme? tooltipTheme = context.dependOnInheritedWidgetOfExactType<TooltipTheme>();
return tooltipTheme?.data ?? Theme.of(context).tooltipTheme;
Widget wrap(BuildContext context, Widget child) {
return TooltipTheme(data: data, child: child);
bool updateShouldNotify(TooltipTheme oldWidget) => data !=;
/// The method of interaction that will trigger a tooltip.
/// Used in [Tooltip.triggerMode] and [TooltipThemeData.triggerMode].
/// On desktop, a tooltip will be shown as soon as a pointer hovers over
/// the widget, regardless of the value of [Tooltip.triggerMode].
/// See also:
/// * [Tooltip.waitDuration], which defines the length of time that
/// a pointer must hover over a tooltip's widget before the tooltip
/// will be shown.
enum TooltipTriggerMode {
/// Tooltip will only be shown by calling `ensureTooltipVisible`.
/// Tooltip will be shown after a long press.
/// See also:
/// * [GestureDetector.onLongPress], the event that is used for trigger.
/// * [Feedback.forLongPress], the feedback method called when feedback is enabled.
/// Tooltip will be shown after a single tap.
/// See also:
/// * [GestureDetector.onTap], the event that is used for trigger.
/// * [Feedback.forTap], the feedback method called when feedback is enabled.