| // 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. |
| @immutable |
| class TooltipThemeData with Diagnosticable { |
| /// Creates the set of properties used to configure [Tooltip]s. |
| const TooltipThemeData({ |
| this.height, |
| this.padding, |
| this.margin, |
| this.verticalOffset, |
| this.preferBelow, |
| this.excludeFromSemantics, |
| this.decoration, |
| this.textStyle, |
| this.textAlign, |
| this.waitDuration, |
| this.showDuration, |
| this.triggerMode, |
| this.enableFeedback, |
| }); |
| |
| /// 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, |
| ); |
| } |
| |
| @override |
| int get hashCode => Object.hash( |
| height, |
| padding, |
| margin, |
| verticalOffset, |
| preferBelow, |
| excludeFromSemantics, |
| decoration, |
| textStyle, |
| textAlign, |
| waitDuration, |
| showDuration, |
| triggerMode, |
| enableFeedback, |
| ); |
| |
| @override |
| 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; |
| } |
| |
| @override |
| void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
| super.debugFillProperties(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: Colors.blue.withOpacity(0.9), |
| /// borderRadius: BorderRadius.zero, |
| /// ), |
| /// ), |
| /// 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({ |
| super.key, |
| required this.data, |
| 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; |
| } |
| |
| @override |
| Widget wrap(BuildContext context, Widget child) { |
| return TooltipTheme(data: data, child: child); |
| } |
| |
| @override |
| bool updateShouldNotify(TooltipTheme oldWidget) => data != 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`. |
| manual, |
| |
| /// 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. |
| longPress, |
| |
| /// 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. |
| tap, |
| } |