diff --git a/packages/flutter/lib/material.dart b/packages/flutter/lib/material.dart index ca49d7ffa2..edeaa36cd1 100644 --- a/packages/flutter/lib/material.dart +++ b/packages/flutter/lib/material.dart @@ -116,7 +116,6 @@ export 'src/material/time.dart'; export 'src/material/time_picker.dart'; export 'src/material/toggleable.dart'; export 'src/material/tooltip.dart'; -export 'src/material/tooltip_theme.dart'; export 'src/material/typography.dart'; export 'src/material/user_accounts_drawer_header.dart'; export 'widgets.dart'; diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart index 1e23da22b0..e0c1dd6c86 100644 --- a/packages/flutter/lib/src/material/theme_data.dart +++ b/packages/flutter/lib/src/material/theme_data.dart @@ -27,7 +27,6 @@ import 'slider_theme.dart'; import 'snack_bar_theme.dart'; import 'tab_bar_theme.dart'; import 'text_theme.dart'; -import 'tooltip_theme.dart'; import 'typography.dart'; export 'package:flutter/services.dart' show Brightness; @@ -156,7 +155,6 @@ class ThemeData extends Diagnosticable { IconThemeData accentIconTheme, SliderThemeData sliderTheme, TabBarTheme tabBarTheme, - TooltipThemeData tooltipTheme, CardTheme cardTheme, ChipThemeData chipTheme, TargetPlatform platform, @@ -259,7 +257,6 @@ class ThemeData extends Diagnosticable { sliderTheme ??= const SliderThemeData(); tabBarTheme ??= const TabBarTheme(); - tooltipTheme ??= const TooltipThemeData(); appBarTheme ??= const AppBarTheme(); bottomAppBarTheme ??= const BottomAppBarTheme(); cardTheme ??= const CardTheme(); @@ -316,7 +313,6 @@ class ThemeData extends Diagnosticable { accentIconTheme: accentIconTheme, sliderTheme: sliderTheme, tabBarTheme: tabBarTheme, - tooltipTheme: tooltipTheme, cardTheme: cardTheme, chipTheme: chipTheme, platform: platform, @@ -387,7 +383,6 @@ class ThemeData extends Diagnosticable { @required this.accentIconTheme, @required this.sliderTheme, @required this.tabBarTheme, - @required this.tooltipTheme, @required this.cardTheme, @required this.chipTheme, @required this.platform, @@ -443,7 +438,6 @@ class ThemeData extends Diagnosticable { assert(accentIconTheme != null), assert(sliderTheme != null), assert(tabBarTheme != null), - assert(tooltipTheme != null), assert(cardTheme != null), assert(chipTheme != null), assert(platform != null), @@ -664,17 +658,12 @@ class ThemeData extends Diagnosticable { /// A theme for customizing the size, shape, and color of the tab bar indicator. final TabBarTheme tabBarTheme; - /// A theme for customizing the visual properties of [Tooltip]s. - /// - /// This is the value returned from [TooltipTheme.of]. - final TooltipThemeData tooltipTheme; - /// The colors and styles used to render [Card]. /// /// This is the value returned from [CardTheme.of]. final CardTheme cardTheme; - /// The colors and styles used to render [Chip]s. + /// The colors and styles used to render [Chip], [ /// /// This is the value returned from [ChipTheme.of]. final ChipThemeData chipTheme; @@ -822,7 +811,6 @@ class ThemeData extends Diagnosticable { IconThemeData accentIconTheme, SliderThemeData sliderTheme, TabBarTheme tabBarTheme, - TooltipThemeData tooltipTheme, CardTheme cardTheme, ChipThemeData chipTheme, TargetPlatform platform, @@ -882,7 +870,6 @@ class ThemeData extends Diagnosticable { accentIconTheme: accentIconTheme ?? this.accentIconTheme, sliderTheme: sliderTheme ?? this.sliderTheme, tabBarTheme: tabBarTheme ?? this.tabBarTheme, - tooltipTheme: tooltipTheme ?? this.tooltipTheme, cardTheme: cardTheme ?? this.cardTheme, chipTheme: chipTheme ?? this.chipTheme, platform: platform ?? this.platform, @@ -1020,7 +1007,6 @@ class ThemeData extends Diagnosticable { accentIconTheme: IconThemeData.lerp(a.accentIconTheme, b.accentIconTheme, t), sliderTheme: SliderThemeData.lerp(a.sliderTheme, b.sliderTheme, t), tabBarTheme: TabBarTheme.lerp(a.tabBarTheme, b.tabBarTheme, t), - tooltipTheme: TooltipThemeData.lerp(a.tooltipTheme, b.tooltipTheme, t), cardTheme: CardTheme.lerp(a.cardTheme, b.cardTheme, t), chipTheme: ChipThemeData.lerp(a.chipTheme, b.chipTheme, t), platform: t < 0.5 ? a.platform : b.platform, @@ -1086,7 +1072,6 @@ class ThemeData extends Diagnosticable { (otherData.accentIconTheme == accentIconTheme) && (otherData.sliderTheme == sliderTheme) && (otherData.tabBarTheme == tabBarTheme) && - (otherData.tooltipTheme == tooltipTheme) && (otherData.cardTheme == cardTheme) && (otherData.chipTheme == chipTheme) && (otherData.platform == platform) && @@ -1151,7 +1136,6 @@ class ThemeData extends Diagnosticable { accentIconTheme, sliderTheme, tabBarTheme, - tooltipTheme, cardTheme, chipTheme, platform, @@ -1214,7 +1198,6 @@ class ThemeData extends Diagnosticable { properties.add(DiagnosticsProperty('accentIconTheme', accentIconTheme)); properties.add(DiagnosticsProperty('sliderTheme', sliderTheme)); properties.add(DiagnosticsProperty('tabBarTheme', tabBarTheme)); - properties.add(DiagnosticsProperty('tooltipTheme', tooltipTheme)); properties.add(DiagnosticsProperty('cardTheme', cardTheme)); properties.add(DiagnosticsProperty('chipTheme', chipTheme)); properties.add(DiagnosticsProperty('materialTapTargetSize', materialTapTargetSize)); diff --git a/packages/flutter/lib/src/material/tooltip.dart b/packages/flutter/lib/src/material/tooltip.dart index 365c4d50fc..ca0177c7df 100644 --- a/packages/flutter/lib/src/material/tooltip.dart +++ b/packages/flutter/lib/src/material/tooltip.dart @@ -8,11 +8,9 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; -import 'colors.dart'; import 'feedback.dart'; import 'theme.dart'; import 'theme_data.dart'; -import 'tooltip_theme.dart'; /// A material design tooltip. /// @@ -34,54 +32,55 @@ import 'tooltip_theme.dart'; /// See also: /// /// * -/// * [TooltipTheme] or [ThemeData.tooltipTheme] 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]. + /// By default, tooltips prefer to appear below the [child] widget when the + /// user long presses on the widget. /// - /// All parameters that are defined in the constructor will - /// override the default values _and_ the values in [TooltipTheme.of]. + /// All of the arguments except [child] and [decoration] must not be null. const Tooltip({ Key key, @required this.message, - this.height, - this.padding, - this.verticalOffset, - this.preferBelow, - this.excludeFromSemantics, + this.height = _defaultTooltipHeight, + this.padding = _defaultPadding, + this.verticalOffset = _defaultVerticalOffset, + this.preferBelow = true, + this.excludeFromSemantics = false, this.decoration, - this.textStyle, - this.waitDuration, - this.showDuration, + this.waitDuration = _defaultWaitDuration, + this.showDuration = _defaultShowDuration, this.child, }) : assert(message != null), + assert(height != null), + assert(padding != null), + assert(verticalOffset != null), + assert(preferBelow != null), + assert(excludeFromSemantics != null), + assert(waitDuration != null), + assert(showDuration != null), super(key: key); + static const Duration _defaultShowDuration = Duration(milliseconds: 1500); + static const Duration _defaultWaitDuration = Duration(milliseconds: 0); + static const double _defaultTooltipHeight = 32.0; + static const double _defaultVerticalOffset = 24.0; + static const EdgeInsetsGeometry _defaultPadding = EdgeInsets.symmetric(horizontal: 16.0); + /// The text to display in the tooltip. final String message; - /// The height of the tooltip's [child]. + /// They height of the tooltip's [child]. /// - /// If the [child] is null, then this is the tooltip's intrinsic height. + /// If the [child] is null, then this is the intrinsic height. final double height; - /// The amount of space by which to inset the tooltip's [child]. + /// The amount of space by which to inset the child. /// /// Defaults to 16.0 logical pixels in each direction. final EdgeInsetsGeometry padding; /// 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. @@ -93,10 +92,6 @@ class Tooltip extends StatefulWidget { /// Whether the tooltip's [message] should be excluded from the semantics /// tree. - /// - /// Defaults to false. A tooltip 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 widget below this widget in the tree. @@ -106,28 +101,18 @@ class Tooltip extends StatefulWidget { /// 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.dark], and - /// [Colors.white] if it is [Brightness.light]. + /// If not specified, defaults to a rounded rectangle with a border radius of + /// 4.0, and a color derived from the [ThemeData.textTheme] if the + /// [ThemeData.brightness] is dark, and [ThemeData.primaryTextTheme] if not. final Decoration decoration; - /// The style to use for the message of the tooltip. + /// The amount of time that a pointer must hover over the widget before it + /// will show a tooltip. /// - /// If null, the message's [TextStyle] will be determined based on - /// [ThemeData]. If [ThemeData.brightness] is set to [Brightness.dark], - /// [ThemeData.textTheme.body1] will be used with [Colors.white]. Otherwise, - /// if [ThemeData.brightness] is set to [Brightness.light], - /// [ThemeData.textTheme.body1] will be used with [Colors.black]. - final TextStyle textStyle; - - /// The length of time that a pointer must hover over a tooltip's widget - /// before the tooltip will be shown. - /// - /// Defaults to 0 milliseconds (tooltips are shown immediately upon hover). + /// Defaults to 0 milliseconds (tooltips show immediately upon hover). final Duration waitDuration; - /// The length of time that the tooltip will be shown once it has appeared. + /// The amount of time that the tooltip will be shown once it has appeared. /// /// Defaults to 1.5 seconds. final Duration showDuration; @@ -139,40 +124,24 @@ class Tooltip extends StatefulWidget { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(StringProperty('message', message, showName: false)); - properties.add(DoubleProperty('height', height, defaultValue: null)); - properties.add(DiagnosticsProperty('padding', padding, defaultValue: null)); - properties.add(DoubleProperty('vertical offset', verticalOffset, defaultValue: null)); - properties.add(FlagProperty('position', value: preferBelow, ifTrue: 'below', ifFalse: 'above', showName: true, defaultValue: null)); - properties.add(FlagProperty('semantics', value: excludeFromSemantics, ifTrue: 'excluded', showName: true, defaultValue: null)); - properties.add(DiagnosticsProperty('wait duration', waitDuration, defaultValue: null)); - properties.add(DiagnosticsProperty('show duration', showDuration, defaultValue: null)); + properties.add(DoubleProperty('height', height, defaultValue: _defaultTooltipHeight)); + properties.add(DiagnosticsProperty('padding', padding, defaultValue: _defaultPadding)); + properties.add(DoubleProperty('vertical offset', verticalOffset, defaultValue: _defaultVerticalOffset)); + properties.add(FlagProperty('position', value: preferBelow, ifTrue: 'below', ifFalse: 'above', showName: true)); + properties.add(FlagProperty('semantics', value: excludeFromSemantics, ifTrue: 'excluded', showName: true, defaultValue: false)); + properties.add(DiagnosticsProperty('wait duration', waitDuration, defaultValue: _defaultWaitDuration)); + properties.add(DiagnosticsProperty('show duration', showDuration, defaultValue: _defaultShowDuration)); } } class _TooltipState extends State with SingleTickerProviderStateMixin { - static const double _defaultTooltipHeight = 32.0; - static const double _defaultVerticalOffset = 24.0; - static const bool _defaultPreferBelow = true; - static const EdgeInsetsGeometry _defaultPadding = EdgeInsets.symmetric(horizontal: 16.0); static const Duration _fadeInDuration = Duration(milliseconds: 150); static const Duration _fadeOutDuration = Duration(milliseconds: 75); - static const Duration _defaultShowDuration = Duration(milliseconds: 1500); - static const Duration _defaultWaitDuration = Duration(milliseconds: 0); - static const bool _defaultExcludeFromSemantics = false; - double height; - EdgeInsetsGeometry padding; - Decoration decoration; - TextStyle textStyle; - double verticalOffset; - bool preferBelow; - bool excludeFromSemantics; AnimationController _controller; OverlayEntry _entry; Timer _hideTimer; Timer _showTimer; - Duration showDuration; - Duration waitDuration; bool _mouseIsConnected; bool _longPressActivated = false; @@ -221,7 +190,7 @@ class _TooltipState extends State with SingleTickerProviderStateMixin { } if (_longPressActivated) { // Tool tips activated by long press should stay around for 1.5s. - _hideTimer ??= Timer(showDuration, _controller.reverse); + _hideTimer ??= Timer(widget.showDuration, _controller.reverse); } else { // Tool tips activated by hover should disappear as soon as the mouse // leaves the control. @@ -237,7 +206,7 @@ class _TooltipState extends State with SingleTickerProviderStateMixin { ensureTooltipVisible(); return; } - _showTimer ??= Timer(waitDuration, ensureTooltipVisible); + _showTimer ??= Timer(widget.waitDuration, ensureTooltipVisible); } /// Shows the tooltip if it is not already visible. @@ -267,17 +236,16 @@ class _TooltipState extends State with SingleTickerProviderStateMixin { // rebuilds. final Widget overlay = _TooltipOverlay( message: widget.message, - height: height, - padding: padding, - decoration: decoration, - textStyle: textStyle, + height: widget.height, + padding: widget.padding, + decoration: widget.decoration, animation: CurvedAnimation( parent: _controller, curve: Curves.fastOutSlowIn, ), target: target, - verticalOffset: verticalOffset, - preferBelow: preferBelow, + verticalOffset: widget.verticalOffset, + preferBelow: widget.preferBelow, ); _entry = OverlayEntry(builder: (BuildContext context) => overlay); Overlay.of(context, debugRequiredFor: widget).insert(_entry); @@ -332,46 +300,12 @@ class _TooltipState extends State with SingleTickerProviderStateMixin { @override Widget build(BuildContext context) { assert(Overlay.of(context, debugRequiredFor: widget) != null); - final ThemeData theme = Theme.of(context); - final TooltipThemeData tooltipTheme = TooltipTheme.of(context); - TextStyle defaultTextStyle; - BoxDecoration defaultDecoration; - if (theme.brightness == Brightness.dark) { - defaultTextStyle = theme.textTheme.body1.copyWith( - color: Colors.black, - debugLabel: theme.textTheme.body1.debugLabel.replaceAll('white', 'black'), - ); - defaultDecoration = BoxDecoration( - color: Colors.white.withOpacity(0.9), - borderRadius: const BorderRadius.all(Radius.circular(4)), - ); - } else { - defaultTextStyle = theme.textTheme.body1.copyWith( - color: Colors.white, - debugLabel: theme.textTheme.body1.debugLabel.replaceAll('black', 'white') - ); - defaultDecoration = BoxDecoration( - color: Colors.grey[700].withOpacity(0.9), - borderRadius: const BorderRadius.all(Radius.circular(4)), - ); - } - - height = widget.height ?? tooltipTheme.height ?? _defaultTooltipHeight; - padding = widget.padding ?? tooltipTheme.padding ?? _defaultPadding; - verticalOffset = widget.verticalOffset ?? tooltipTheme.verticalOffset ?? _defaultVerticalOffset; - preferBelow = widget.preferBelow ?? tooltipTheme.preferBelow ?? _defaultPreferBelow; - excludeFromSemantics = widget.excludeFromSemantics ?? tooltipTheme.excludeFromSemantics ?? _defaultExcludeFromSemantics; - decoration = widget.decoration ?? tooltipTheme.decoration ?? defaultDecoration; - textStyle = widget.textStyle ?? tooltipTheme.textStyle ?? defaultTextStyle; - waitDuration = widget.waitDuration ?? tooltipTheme.waitDuration ?? _defaultWaitDuration; - showDuration = widget.showDuration ?? tooltipTheme.showDuration ?? _defaultShowDuration; - Widget result = GestureDetector( behavior: HitTestBehavior.opaque, onLongPress: _handleLongPress, excludeFromSemantics: true, child: Semantics( - label: excludeFromSemantics ? null : widget.message, + label: widget.excludeFromSemantics ? null : widget.message, child: widget.child, ), ); @@ -411,7 +345,7 @@ class _TooltipPositionDelegate extends SingleChildLayoutDelegate { /// tooltip. final double verticalOffset; - /// Whether the tooltip is displayed below its widget by default. + /// 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. @@ -446,7 +380,6 @@ class _TooltipOverlay extends StatelessWidget { this.height, this.padding, this.decoration, - this.textStyle, this.animation, this.target, this.verticalOffset, @@ -457,7 +390,6 @@ class _TooltipOverlay extends StatelessWidget { final double height; final EdgeInsetsGeometry padding; final Decoration decoration; - final TextStyle textStyle; final Animation animation; final Offset target; final double verticalOffset; @@ -465,6 +397,12 @@ class _TooltipOverlay extends StatelessWidget { @override Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + final ThemeData tooltipTheme = ThemeData( + brightness: Brightness.dark, + textTheme: theme.brightness == Brightness.dark ? theme.textTheme : theme.primaryTextTheme, + platform: theme.platform, + ); return Positioned.fill( child: IgnorePointer( child: CustomSingleChildLayout( @@ -478,15 +416,15 @@ class _TooltipOverlay extends StatelessWidget { child: ConstrainedBox( constraints: BoxConstraints(minHeight: height), child: Container( - decoration: decoration, + decoration: decoration ?? BoxDecoration( + color: tooltipTheme.backgroundColor.withOpacity(0.9), + borderRadius: BorderRadius.circular(4.0), + ), padding: padding, child: Center( widthFactor: 1.0, heightFactor: 1.0, - child: Text( - message, - style: textStyle, - ), + child: Text(message, style: tooltipTheme.textTheme.body1), ), ), ), diff --git a/packages/flutter/lib/src/material/tooltip_theme.dart b/packages/flutter/lib/src/material/tooltip_theme.dart deleted file mode 100644 index 713229f8e9..0000000000 --- a/packages/flutter/lib/src/material/tooltip_theme.dart +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2019 The Chromium 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'; - -/// 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 extends Diagnosticable { - /// Creates the set of properties used to configure [Tooltip]s. - const TooltipThemeData({ - this.height, - this.padding, - this.verticalOffset, - this.preferBelow, - this.excludeFromSemantics, - this.decoration, - this.textStyle, - this.waitDuration, - this.showDuration, - }); - - /// The height of [Tooltip.child]. - final double height; - - /// If provided, the amount of space by which to inset [Tooltip.child]. - final EdgeInsetsGeometry padding; - - /// 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's [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 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; - - /// Creates a copy of this object but with the given fields replaced with the - /// new values. - TooltipThemeData copyWith({ - double height, - EdgeInsetsGeometry padding, - double verticalOffset, - bool preferBelow, - bool excludeFromSemantics, - Decoration decoration, - TextStyle textStyle, - Duration waitDuration, - Duration showDuration, - }) { - return TooltipThemeData( - height: height ?? this.height, - padding: padding ?? this.padding, - verticalOffset: verticalOffset ?? this.verticalOffset, - preferBelow: preferBelow ?? this.preferBelow, - excludeFromSemantics: excludeFromSemantics ?? this.excludeFromSemantics, - decoration: decoration ?? this.decoration, - textStyle: textStyle ?? this.textStyle, - waitDuration: waitDuration ?? this.waitDuration, - showDuration: showDuration ?? this.showDuration, - ); - } - - /// 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 (a == null && b == null) - return null; - assert(t != null); - return TooltipThemeData( - height: lerpDouble(a?.height, b?.height, t), - padding: EdgeInsets.lerp(a?.padding, b?.padding, 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), - ); - } - - @override - int get hashCode { - return hashValues( - height, - padding, - verticalOffset, - preferBelow, - excludeFromSemantics, - decoration, - textStyle, - waitDuration, - showDuration, - ); - } - - @override - bool operator==(Object other) { - if (identical(this, other)) - return true; - if (other.runtimeType != runtimeType) - return false; - final TooltipThemeData typedOther = other; - return typedOther.height == height - && typedOther.padding == padding - && typedOther.verticalOffset == verticalOffset - && typedOther.preferBelow == preferBelow - && typedOther.excludeFromSemantics == excludeFromSemantics - && typedOther.decoration == decoration - && typedOther.textStyle == textStyle - && typedOther.waitDuration == waitDuration - && typedOther.showDuration == showDuration; - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(DoubleProperty('height', height, defaultValue: null)); - properties.add(DiagnosticsProperty('padding', padding, defaultValue: null)); - properties.add(DoubleProperty('vertical offset', verticalOffset, defaultValue: null)); - properties.add(FlagProperty('position', value: preferBelow, ifTrue: 'below', ifFalse: 'above', showName: true, defaultValue: null)); - properties.add(FlagProperty('semantics', value: excludeFromSemantics, ifTrue: 'excluded', showName: true, defaultValue: null)); - properties.add(DiagnosticsProperty('decoration', decoration, defaultValue: null)); - properties.add(DiagnosticsProperty('textStyle', textStyle, defaultValue: null)); - properties.add(DiagnosticsProperty('wait duration', waitDuration, defaultValue: null)); - properties.add(DiagnosticsProperty('show duration', showDuration, defaultValue: null)); - } -} - -/// 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 sample} -/// -/// Here is an example of a tooltip theme that applies a blue foreground -/// with non-rounded corners. -/// -/// ```dart -/// TooltipTheme( -/// decoration: BoxDecoration( -/// color: Colors.blue.withOpacity(0.9), -/// borderRadius: BorderRadius.zero, -/// ), -/// child: Tooltip( -/// message: 'Example tooltip', -/// child: IconButton( -/// iconSize: 36.0, -/// icon: Icon(Icons.touch_app), -/// onPressed: () {}, -/// ), -/// ), -/// ), -/// ``` -/// {@end-tool} -class TooltipTheme extends InheritedWidget { - /// Creates a tooltip theme that controls the configurations for - /// [Tooltip]. - TooltipTheme({ - Key key, - double height, - EdgeInsetsGeometry padding, - double verticalOffset, - bool preferBelow, - bool excludeFromSemantics, - Decoration decoration, - TextStyle textStyle, - Duration waitDuration, - Duration showDuration, - Widget child, - }) : data = TooltipThemeData( - height: height, - padding: padding, - verticalOffset: verticalOffset, - preferBelow: preferBelow, - excludeFromSemantics: excludeFromSemantics, - decoration: decoration, - textStyle: textStyle, - waitDuration: waitDuration, - showDuration: showDuration, - ), - super(key: key, child: 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.inheritFromWidgetOfExactType(TooltipTheme); - return tooltipTheme?.data ?? Theme.of(context).tooltipTheme; - } - - @override - bool updateShouldNotify(TooltipTheme oldWidget) => data != oldWidget.data; -} diff --git a/packages/flutter/test/material/tooltip_test.dart b/packages/flutter/test/material/tooltip_test.dart index 2e730a9996..b03a0a4c97 100644 --- a/packages/flutter/test/material/tooltip_test.dart +++ b/packages/flutter/test/material/tooltip_test.dart @@ -423,81 +423,6 @@ void main() { expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(324.0)); }, skip: isBrowser); - testWidgets('Default tooltip message textStyle - light', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - await tester.pumpWidget(MaterialApp( - home: Tooltip( - key: key, - message: tooltipText, - child: Container( - width: 100.0, - height: 100.0, - color: Colors.green[500], - ), - ), - )); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) - - final TextStyle textStyle = tester.widget(find.text(tooltipText)).style; - expect(textStyle.color, Colors.white); - expect(textStyle.fontFamily, 'Roboto'); - expect(textStyle.decoration, TextDecoration.none); - expect(textStyle.debugLabel, '(englishLike body1 2014).merge(whiteMountainView body1)'); - }); - - testWidgets('Default tooltip message textStyle - dark', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - await tester.pumpWidget(MaterialApp( - theme: ThemeData( - brightness: Brightness.dark, - ), - home: Tooltip( - key: key, - message: tooltipText, - child: Container( - width: 100.0, - height: 100.0, - color: Colors.green[500], - ), - ), - )); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) - - final TextStyle textStyle = tester.widget(find.text(tooltipText)).style; - expect(textStyle.color, Colors.black); - expect(textStyle.fontFamily, 'Roboto'); - expect(textStyle.decoration, TextDecoration.none); - expect(textStyle.debugLabel, '(englishLike body1 2014).merge(blackMountainView body1)'); - }); - - testWidgets('Custom tooltip message textStyle', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - await tester.pumpWidget(MaterialApp( - home: Tooltip( - key: key, - textStyle: const TextStyle( - color: Colors.orange, - decoration: TextDecoration.underline - ), - message: tooltipText, - child: Container( - width: 100.0, - height: 100.0, - color: Colors.green[500], - ), - ), - )); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) - - final TextStyle textStyle = tester.widget(find.text(tooltipText)).style; - expect(textStyle.color, Colors.orange); - expect(textStyle.fontFamily, null); - expect(textStyle.decoration, TextDecoration.underline); - }); - testWidgets('Does tooltip end up with the right default size, shape, and color', (WidgetTester tester) async { final GlobalKey key = GlobalKey(); await tester.pumpWidget( @@ -931,6 +856,7 @@ void main() { expect(description, [ '"message"', + 'position: below', ]); }); testWidgets('Tooltip implements debugFillProperties', (WidgetTester tester) async { diff --git a/packages/flutter/test/material/tooltip_theme_test.dart b/packages/flutter/test/material/tooltip_theme_test.dart deleted file mode 100644 index 722b6760c7..0000000000 --- a/packages/flutter/test/material/tooltip_theme_test.dart +++ /dev/null @@ -1,1118 +0,0 @@ -// Copyright 2019 The Chromium 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 'package:flutter/services.dart'; -import 'package:flutter/src/material/tooltip_theme.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; - -import '../rendering/mock_canvas.dart'; -import '../widgets/semantics_tester.dart'; - -// This file uses "as dynamic" in a few places to defeat the static -// analysis. In general you want to avoid using this style in your -// code, as it will cause the analyzer to be unable to help you catch -// errors. -// -// In this case, we do it because we are trying to call internal -// methods of the tooltip code in order to test it. Normally, the -// state of a tooltip is a private class, but by using a GlobalKey we -// can get a handle to that object and by using "as dynamic" we can -// bypass the analyzer's type checks and call methods that we aren't -// supposed to be able to know about. -// -// It's ok to do this in tests, but you really don't want to do it in -// production code. - -const String tooltipText = 'TIP'; - -void main() { - test('TooltipThemeData copyWith, ==, hashCode basics', () { - expect(const TooltipThemeData(), const TooltipThemeData().copyWith()); - expect(const TooltipThemeData().hashCode, const TooltipThemeData().copyWith().hashCode); - }); - - test('TooltipThemeData defaults', () { - const TooltipThemeData theme = TooltipThemeData(); - expect(theme.height, null); - expect(theme.padding, null); - expect(theme.verticalOffset, null); - expect(theme.preferBelow, null); - expect(theme.excludeFromSemantics, null); - expect(theme.decoration, null); - expect(theme.textStyle, null); - expect(theme.waitDuration, null); - expect(theme.showDuration, null); - }); - - testWidgets('Default TooltipThemeData debugFillProperties', (WidgetTester tester) async { - final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); - const TooltipThemeData().debugFillProperties(builder); - - final List description = builder.properties - .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) - .map((DiagnosticsNode node) => node.toString()) - .toList(); - - expect(description, []); - }); - - testWidgets('TooltipThemeData implements debugFillProperties', (WidgetTester tester) async { - final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); - const TooltipThemeData( - height: 15.0, - padding: EdgeInsets.all(20.0), - verticalOffset: 10.0, - preferBelow: false, - excludeFromSemantics: true, - decoration: BoxDecoration(color: Color(0xffffffff)), - textStyle: TextStyle(decoration: TextDecoration.underline), - waitDuration: Duration(milliseconds: 100), - showDuration: Duration(milliseconds: 200), - ).debugFillProperties(builder); - - final List description = builder.properties - .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) - .map((DiagnosticsNode node) => node.toString()) - .toList(); - - expect(description, [ - 'height: 15.0', - 'padding: EdgeInsets.all(20.0)', - 'vertical offset: 10.0', - 'position: above', - 'semantics: excluded', - 'decoration: BoxDecoration(color: Color(0xffffffff))', - 'textStyle: TextStyle(inherit: true, decoration: TextDecoration.underline)', - 'wait duration: 0:00:00.100000', - 'show duration: 0:00:00.200000' - ]); - }); - - testWidgets('Tooltip verticalOffset, preferBelow; center prefer above fits - ThemeData.tooltipTheme', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Theme( - data: ThemeData( - tooltipTheme: const TooltipThemeData( - height: 100.0, - padding: EdgeInsets.all(0.0), - verticalOffset: 100.0, - preferBelow: false, - ), - ), - child: Overlay( - initialEntries: [ - OverlayEntry( - builder: (BuildContext context) { - return Stack( - children: [ - Positioned( - left: 400.0, - top: 300.0, - child: Tooltip( - key: key, - message: tooltipText, - child: Container( - width: 0.0, - height: 0.0, - ), - ), - ), - ], - ); - }, - ), - ], - ), - ), - ), - ); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) - - /********************* 800x600 screen - * ___ * }- 10.0 margin - * |___| * }-100.0 height - * | * }-100.0 vertical offset - * o * y=300.0 - * * - * * - * * - *********************/ - - final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent; - expect(tip.size.height, equals(100.0)); - expect(tip.localToGlobal(tip.size.topLeft(Offset.zero)).dy, equals(100.0)); - expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(200.0)); - }); - - testWidgets('Tooltip verticalOffset, preferBelow; center prefer above fits - TooltipTheme', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: TooltipTheme( - height: 100.0, - padding: const EdgeInsets.all(0.0), - verticalOffset: 100.0, - preferBelow: false, - child: Overlay( - initialEntries: [ - OverlayEntry( - builder: (BuildContext context) { - return Stack( - children: [ - Positioned( - left: 400.0, - top: 300.0, - child: Tooltip( - key: key, - message: tooltipText, - child: Container( - width: 0.0, - height: 0.0, - ), - ), - ), - ], - ); - }, - ), - ], - ), - ), - ), - ); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) - - /********************* 800x600 screen - * ___ * }- 10.0 margin - * |___| * }-100.0 height - * | * }-100.0 vertical offset - * o * y=300.0 - * * - * * - * * - *********************/ - - final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent; - expect(tip.size.height, equals(100.0)); - expect(tip.localToGlobal(tip.size.topLeft(Offset.zero)).dy, equals(100.0)); - expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(200.0)); - }); - - testWidgets('Tooltip verticalOffset, preferBelow; center prefer above does not fit - ThemeData.tooltipTheme', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Theme( - data: ThemeData( - tooltipTheme: const TooltipThemeData( - height: 190.0, - padding: EdgeInsets.all(0.0), - verticalOffset: 100.0, - preferBelow: false, - ), - ), - child: Overlay( - initialEntries: [ - OverlayEntry( - builder: (BuildContext context) { - return Stack( - children: [ - Positioned( - left: 400.0, - top: 299.0, - child: Tooltip( - key: key, - message: tooltipText, - child: Container( - width: 0.0, - height: 0.0, - ), - ), - ), - ], - ); - }, - ), - ], - ), - ), - ), - ); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) - - // we try to put it here but it doesn't fit: - /********************* 800x600 screen - * ___ * }- 10.0 margin - * |___| * }-190.0 height (starts at y=9.0) - * | * }-100.0 vertical offset - * o * y=299.0 - * * - * * - * * - *********************/ - - // so we put it here: - /********************* 800x600 screen - * * - * * - * o * y=299.0 - * _|_ * }-100.0 vertical offset - * |___| * }-190.0 height - * * }- 10.0 margin - *********************/ - - final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent; - expect(tip.size.height, equals(190.0)); - expect(tip.localToGlobal(tip.size.topLeft(Offset.zero)).dy, equals(399.0)); - expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(589.0)); - }); - - testWidgets('Tooltip verticalOffset, preferBelow; center prefer above does not fit - TooltipTheme', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: TooltipTheme( - height: 190.0, - padding: const EdgeInsets.all(0.0), - verticalOffset: 100.0, - preferBelow: false, - child: Overlay( - initialEntries: [ - OverlayEntry( - builder: (BuildContext context) { - return Stack( - children: [ - Positioned( - left: 400.0, - top: 299.0, - child: Tooltip( - key: key, - message: tooltipText, - child: Container( - width: 0.0, - height: 0.0, - ), - ), - ), - ], - ); - }, - ), - ], - ), - ), - ), - ); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) - - // we try to put it here but it doesn't fit: - /********************* 800x600 screen - * ___ * }- 10.0 margin - * |___| * }-190.0 height (starts at y=9.0) - * | * }-100.0 vertical offset - * o * y=299.0 - * * - * * - * * - *********************/ - - // so we put it here: - /********************* 800x600 screen - * * - * * - * o * y=299.0 - * _|_ * }-100.0 vertical offset - * |___| * }-190.0 height - * * }- 10.0 margin - *********************/ - - final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent; - expect(tip.size.height, equals(190.0)); - expect(tip.localToGlobal(tip.size.topLeft(Offset.zero)).dy, equals(399.0)); - expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(589.0)); - }); - - testWidgets('Tooltip verticalOffset, preferBelow; center preferBelow fits - ThemeData.tooltipTheme', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Theme( - data: ThemeData( - tooltipTheme: const TooltipThemeData( - height: 190.0, - padding: EdgeInsets.all(0.0), - verticalOffset: 100.0, - preferBelow: true, - ), - ), - child: Overlay( - initialEntries: [ - OverlayEntry( - builder: (BuildContext context) { - return Stack( - children: [ - Positioned( - left: 400.0, - top: 300.0, - child: Tooltip( - key: key, - message: tooltipText, - child: Container( - width: 0.0, - height: 0.0, - ), - ), - ), - ], - ); - }, - ), - ], - ), - ), - ), - ); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pumpAndSettle(); // faded in, show timer started (and at 0.0) - - /********************* 800x600 screen - * * - * * - * o * y=300.0 - * _|_ * }-100.0 vertical offset - * |___| * }-190.0 height - * * }- 10.0 margin - *********************/ - - final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent; - expect(tip.size.height, equals(190.0)); - expect(tip.localToGlobal(tip.size.topLeft(Offset.zero)).dy, equals(400.0)); - expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(590.0)); - }); - - testWidgets('Tooltip verticalOffset, preferBelow; center prefer below fits - TooltipTheme', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: TooltipTheme( - height: 190.0, - padding: const EdgeInsets.all(0.0), - verticalOffset: 100.0, - preferBelow: true, - child: Overlay( - initialEntries: [ - OverlayEntry( - builder: (BuildContext context) { - return Stack( - children: [ - Positioned( - left: 400.0, - top: 300.0, - child: Tooltip( - key: key, - message: tooltipText, - child: Container( - width: 0.0, - height: 0.0, - ), - ), - ), - ], - ); - }, - ), - ], - ), - ), - ), - ); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pumpAndSettle(); // faded in, show timer started (and at 0.0) - - /********************* 800x600 screen - * * - * * - * o * y=300.0 - * _|_ * }-100.0 vertical offset - * |___| * }-190.0 height - * * }- 10.0 margin - *********************/ - - final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent; - expect(tip.size.height, equals(190.0)); - expect(tip.localToGlobal(tip.size.topLeft(Offset.zero)).dy, equals(400.0)); - expect(tip.localToGlobal(tip.size.bottomRight(Offset.zero)).dy, equals(590.0)); - }); - - testWidgets('Tooltip message textStyle - ThemeData.tooltipTheme', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - await tester.pumpWidget(MaterialApp( - theme: ThemeData( - tooltipTheme: const TooltipThemeData( - textStyle: TextStyle( - color: Colors.orange, - decoration: TextDecoration.underline - ), - ), - ), - home: Tooltip( - key: key, - message: tooltipText, - child: Container( - width: 100.0, - height: 100.0, - color: Colors.green[500], - ), - ), - )); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) - - final TextStyle textStyle = tester.widget(find.text(tooltipText)).style; - expect(textStyle.color, Colors.orange); - expect(textStyle.fontFamily, null); - expect(textStyle.decoration, TextDecoration.underline); - }); - - testWidgets('Tooltip message textStyle - TooltipTheme', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - await tester.pumpWidget(MaterialApp( - home: TooltipTheme( - child: Tooltip( - textStyle: const TextStyle( - color: Colors.orange, - decoration: TextDecoration.underline - ), - key: key, - message: tooltipText, - child: Container( - width: 100.0, - height: 100.0, - color: Colors.green[500], - ), - ), - ), - )); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) - - final TextStyle textStyle = tester.widget(find.text(tooltipText)).style; - expect(textStyle.color, Colors.orange); - expect(textStyle.fontFamily, null); - expect(textStyle.decoration, TextDecoration.underline); - }); - - testWidgets('Tooltip decoration - ThemeData.tooltipTheme', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - const Decoration customDecoration = ShapeDecoration( - shape: StadiumBorder(), - color: Color(0x80800000), - ); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Theme( - data: ThemeData( - tooltipTheme: const TooltipThemeData( - decoration: customDecoration, - ), - ), - child: Overlay( - initialEntries: [ - OverlayEntry( - builder: (BuildContext context) { - return Tooltip( - key: key, - message: tooltipText, - child: Container( - width: 0.0, - height: 0.0, - ), - ); - }, - ), - ], - ), - ), - ), - ); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) - - final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent.parent.parent.parent; - - expect(tip.size.height, equals(32.0)); - expect(tip.size.width, equals(74.0)); - expect(tip, paints..path( - color: const Color(0x80800000), - )); - }, skip: isBrowser); - - testWidgets('Tooltip decoration - TooltipTheme', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - const Decoration customDecoration = ShapeDecoration( - shape: StadiumBorder(), - color: Color(0x80800000), - ); - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: TooltipTheme( - decoration: customDecoration, - child: Overlay( - initialEntries: [ - OverlayEntry( - builder: (BuildContext context) { - return Tooltip( - key: key, - message: tooltipText, - child: Container( - width: 0.0, - height: 0.0, - ), - ); - }, - ), - ], - ), - ), - ), - ); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pump(const Duration(seconds: 2)); // faded in, show timer started (and at 0.0) - - final RenderBox tip = tester.renderObject(find.text(tooltipText)).parent.parent.parent.parent; - - expect(tip.size.height, equals(32.0)); - expect(tip.size.width, equals(74.0)); - expect(tip, paints..path( - color: const Color(0x80800000), - )); - }, skip: isBrowser); - - testWidgets('Tooltip height and padding - ThemeData.tooltipTheme', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - const double customTooltipHeight = 100.0; - const double customPaddingVal = 20.0; - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Theme( - data: ThemeData( - tooltipTheme: const TooltipThemeData( - height: customTooltipHeight, - padding: EdgeInsets.all(customPaddingVal), - ), - ), - child: Overlay( - initialEntries: [ - OverlayEntry( - builder: (BuildContext context) { - return Tooltip( - key: key, - message: tooltipText, - ); - }, - ), - ], - ), - ), - ), - ); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pumpAndSettle(); - - final RenderBox tip = tester.renderObject(find.ancestor( - of: find.text(tooltipText), - matching: find.byType(Padding), - )); - final RenderBox content = tester.renderObject(find.ancestor( - of: find.text(tooltipText), - matching: find.byType(Center), - )); - - expect(tip.size.height, equals(customTooltipHeight)); - expect(content.size.height, equals(customTooltipHeight - 2 * customPaddingVal)); - expect(content.size.width, equals(tip.size.width - 2 * customPaddingVal)); - }, skip: isBrowser); - - testWidgets('Tooltip height and padding - TooltipTheme', (WidgetTester tester) async { - final GlobalKey key = GlobalKey(); - const double customTooltipHeight = 100.0; - const double customPaddingVal = 20.0; - - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: TooltipTheme( - height: customTooltipHeight, - padding: const EdgeInsets.all(customPaddingVal), - child: Overlay( - initialEntries: [ - OverlayEntry( - builder: (BuildContext context) { - return Tooltip( - key: key, - message: tooltipText, - ); - }, - ), - ], - ), - ), - ), - ); - (key.currentState as dynamic).ensureTooltipVisible(); // Before using "as dynamic" in your code, see note at the top of the file. - await tester.pumpAndSettle(); - - final RenderBox tip = tester.renderObject(find.ancestor( - of: find.text(tooltipText), - matching: find.byType(Padding), - )); - final RenderBox content = tester.renderObject(find.ancestor( - of: find.text(tooltipText), - matching: find.byType(Center), - )); - - expect(tip.size.height, equals(customTooltipHeight)); - expect(content.size.height, equals(customTooltipHeight - 2 * customPaddingVal)); - expect(content.size.width, equals(tip.size.width - 2 * customPaddingVal)); - }, skip: isBrowser); - - testWidgets('Tooltip waitDuration - ThemeData.tooltipTheme', (WidgetTester tester) async { - const Duration customWaitDuration = Duration(milliseconds: 500); - final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); - await gesture.addPointer(); - await gesture.moveTo(const Offset(1.0, 1.0)); - await tester.pump(); - await gesture.moveTo(Offset.zero); - - await tester.pumpWidget( - MaterialApp( - home: Theme( - data: ThemeData( - tooltipTheme: const TooltipThemeData( - waitDuration: customWaitDuration, - ), - ), - child: Center( - child: Tooltip( - message: tooltipText, - child: Container( - width: 100.0, - height: 100.0, - ), - ), - ), - ), - ) - ); - - final Finder tooltip = find.byType(Tooltip); - await gesture.moveTo(Offset.zero); - await tester.pump(); - await gesture.moveTo(tester.getCenter(tooltip)); - await tester.pump(); - await tester.pump(const Duration(milliseconds: 250)); - expect(find.text(tooltipText), findsNothing); // Should not appear yet - await tester.pump(const Duration(milliseconds: 250)); - expect(find.text(tooltipText), findsOneWidget); // Should appear after customWaitDuration - - await gesture.moveTo(Offset.zero); - await tester.pump(); - - // Wait for it to disappear. - await tester.pump(const Duration(milliseconds: 0)); // Should immediately disappear - expect(find.text(tooltipText), findsNothing); - await gesture.removePointer(); - }); - - testWidgets('Tooltip waitDuration - TooltipTheme', (WidgetTester tester) async { - const Duration customWaitDuration = Duration(milliseconds: 500); - final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); - await gesture.addPointer(); - await gesture.moveTo(const Offset(1.0, 1.0)); - await tester.pump(); - await gesture.moveTo(Offset.zero); - - await tester.pumpWidget( - MaterialApp( - home: TooltipTheme( - waitDuration: customWaitDuration, - child: Center( - child: Tooltip( - message: tooltipText, - child: Container( - width: 100.0, - height: 100.0, - ), - ), - ), - ), - ) - ); - - final Finder tooltip = find.byType(Tooltip); - await gesture.moveTo(Offset.zero); - await tester.pump(); - await gesture.moveTo(tester.getCenter(tooltip)); - await tester.pump(); - await tester.pump(const Duration(milliseconds: 250)); - expect(find.text(tooltipText), findsNothing); // Should not appear yet - await tester.pump(const Duration(milliseconds: 250)); - expect(find.text(tooltipText), findsOneWidget); // Should appear after customWaitDuration - - await gesture.moveTo(Offset.zero); - await tester.pump(); - - // Wait for it to disappear. - await tester.pump(const Duration(milliseconds: 0)); // Should immediately disappear - expect(find.text(tooltipText), findsNothing); - await gesture.removePointer(); - }); - - testWidgets('Tooltip showDuration - ThemeData.tooltipTheme', (WidgetTester tester) async { - const Duration customShowDuration = Duration(milliseconds: 3000); - await tester.pumpWidget( - MaterialApp( - home: Theme( - data: ThemeData( - tooltipTheme: const TooltipThemeData( - showDuration: customShowDuration, - ), - ), - child: Center( - child: Tooltip( - message: tooltipText, - child: Container( - width: 100.0, - height: 100.0, - ), - ), - ), - ), - ) - ); - - final Finder tooltip = find.byType(Tooltip); - final TestGesture gesture = await tester.startGesture(tester.getCenter(tooltip)); - await tester.pump(); - await tester.pump(kLongPressTimeout); - await gesture.up(); - expect(find.text(tooltipText), findsOneWidget); - await tester.pump(); - await tester.pump(const Duration(milliseconds: 2000)); // Tooltip should remain - expect(find.text(tooltipText), findsOneWidget); - await tester.pump(const Duration(milliseconds: 1000)); - await tester.pumpAndSettle(); // Tooltip should fade out after - expect(find.text(tooltipText), findsNothing); - }); - - testWidgets('Tooltip showDuration - TooltipTheme', (WidgetTester tester) async { - const Duration customShowDuration = Duration(milliseconds: 3000); - await tester.pumpWidget( - MaterialApp( - home: TooltipTheme( - showDuration: customShowDuration, - child: Center( - child: Tooltip( - message: tooltipText, - child: Container( - width: 100.0, - height: 100.0, - ), - ), - ), - ), - ) - ); - - final Finder tooltip = find.byType(Tooltip); - final TestGesture gesture = await tester.startGesture(tester.getCenter(tooltip)); - await tester.pump(); - await tester.pump(kLongPressTimeout); - await gesture.up(); - expect(find.text(tooltipText), findsOneWidget); - await tester.pump(); - await tester.pump(const Duration(milliseconds: 2000)); // Tooltip should remain - expect(find.text(tooltipText), findsOneWidget); - await tester.pump(const Duration(milliseconds: 1000)); - await tester.pumpAndSettle(); // Tooltip should fade out after - expect(find.text(tooltipText), findsNothing); - }); - - testWidgets('Semantics included by default - ThemeData.tooltipTheme', (WidgetTester tester) async { - final SemanticsTester semantics = SemanticsTester(tester); - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(), - home: const Center( - child: Tooltip( - message: 'Foo', - child: Text('Bar'), - ), - ), - ), - ); - - expect(semantics, hasSemantics(TestSemantics.root( - children: [ - TestSemantics.rootChild( - children: [ - TestSemantics( - flags: [SemanticsFlag.scopesRoute], - children: [ - TestSemantics( - label: 'Foo\nBar', - textDirection: TextDirection.ltr, - ), - ], - ), - ], - ), - ], - ), ignoreRect: true, ignoreId: true, ignoreTransform: true)); - - semantics.dispose(); - }); - - testWidgets('Semantics included by default - TooltipTheme', (WidgetTester tester) async { - final SemanticsTester semantics = SemanticsTester(tester); - - await tester.pumpWidget( - MaterialApp( - home: TooltipTheme( - child: const Center( - child: Tooltip( - message: 'Foo', - child: Text('Bar'), - ), - ), - ), - ), - ); - - expect(semantics, hasSemantics(TestSemantics.root( - children: [ - TestSemantics.rootChild( - children: [ - TestSemantics( - flags: [SemanticsFlag.scopesRoute], - children: [ - TestSemantics( - label: 'Foo\nBar', - textDirection: TextDirection.ltr, - ), - ], - ), - ], - ), - ], - ), ignoreRect: true, ignoreId: true, ignoreTransform: true)); - - semantics.dispose(); - }); - - testWidgets('Semantics excluded - ThemeData.tooltipTheme', (WidgetTester tester) async { - final SemanticsTester semantics = SemanticsTester(tester); - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData( - tooltipTheme: const TooltipThemeData( - excludeFromSemantics: true, - ), - ), - home: const Center( - child: Tooltip( - message: 'Foo', - child: Text('Bar'), - ), - ), - ), - ); - - expect(semantics, hasSemantics(TestSemantics.root( - children: [ - TestSemantics.rootChild( - children: [ - TestSemantics( - flags: [SemanticsFlag.scopesRoute], - children: [ - TestSemantics( - label: 'Bar', - textDirection: TextDirection.ltr, - ), - ], - ), - ], - ), - ], - ), ignoreRect: true, ignoreId: true, ignoreTransform: true)); - - semantics.dispose(); - }); - - testWidgets('Semantics excluded - TooltipTheme', (WidgetTester tester) async { - final SemanticsTester semantics = SemanticsTester(tester); - - await tester.pumpWidget( - MaterialApp( - home: TooltipTheme( - excludeFromSemantics: true, - child: const Center( - child: Tooltip( - message: 'Foo', - child: Text('Bar'), - ), - ), - ), - ), - ); - - expect(semantics, hasSemantics(TestSemantics.root( - children: [ - TestSemantics.rootChild( - children: [ - TestSemantics( - flags: [SemanticsFlag.scopesRoute], - children: [ - TestSemantics( - label: 'Bar', - textDirection: TextDirection.ltr, - ), - ], - ), - ], - ), - ], - ), ignoreRect: true, ignoreId: true, ignoreTransform: true)); - - semantics.dispose(); - }); - - testWidgets('has semantic events by default - ThemeData.tooltipTheme', (WidgetTester tester) async { - final List semanticEvents = []; - SystemChannels.accessibility.setMockMessageHandler((dynamic message) async { - semanticEvents.add(message); - }); - final SemanticsTester semantics = SemanticsTester(tester); - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(), - home: Center( - child: Tooltip( - message: 'Foo', - child: Container( - width: 100.0, - height: 100.0, - color: Colors.green[500], - ), - ), - ), - ), - ); - - await tester.longPress(find.byType(Tooltip)); - final RenderObject object = tester.firstRenderObject(find.byType(Tooltip)); - - expect(semanticEvents, unorderedEquals([ - { - 'type': 'longPress', - 'nodeId': findDebugSemantics(object).id, - 'data': {}, - }, - { - 'type': 'tooltip', - 'data': { - 'message': 'Foo', - }, - }, - ])); - semantics.dispose(); - SystemChannels.accessibility.setMockMessageHandler(null); - }); - - testWidgets('has semantic events by default - TooltipTheme', (WidgetTester tester) async { - final List semanticEvents = []; - SystemChannels.accessibility.setMockMessageHandler((dynamic message) async { - semanticEvents.add(message); - }); - final SemanticsTester semantics = SemanticsTester(tester); - - await tester.pumpWidget( - MaterialApp( - home: TooltipTheme( - child: Center( - child: Tooltip( - message: 'Foo', - child: Container( - width: 100.0, - height: 100.0, - color: Colors.green[500], - ), - ), - ), - ), - ), - ); - - await tester.longPress(find.byType(Tooltip)); - final RenderObject object = tester.firstRenderObject(find.byType(Tooltip)); - - expect(semanticEvents, unorderedEquals([ - { - 'type': 'longPress', - 'nodeId': findDebugSemantics(object).id, - 'data': {}, - }, - { - 'type': 'tooltip', - 'data': { - 'message': 'Foo', - }, - }, - ])); - semantics.dispose(); - SystemChannels.accessibility.setMockMessageHandler(null); - }); - - testWidgets('default Tooltip debugFillProperties', (WidgetTester tester) async { - final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); - - const Tooltip(message: 'message',).debugFillProperties(builder); - - final List description = builder.properties - .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) - .map((DiagnosticsNode node) => node.toString()).toList(); - - expect(description, [ - '"message"', - ]); - }); -} - -SemanticsNode findDebugSemantics(RenderObject object) { - if (object.debugSemantics != null) - return object.debugSemantics; - return findDebugSemantics(object.parent); -}