diff --git a/examples/api/lib/material/tooltip/tooltip.1.dart b/examples/api/lib/material/tooltip/tooltip.1.dart index 4e34d148ea..b68f29498c 100644 --- a/examples/api/lib/material/tooltip/tooltip.1.dart +++ b/examples/api/lib/material/tooltip/tooltip.1.dart @@ -34,7 +34,7 @@ class TooltipSample extends StatelessWidget { borderRadius: BorderRadius.circular(25), gradient: const LinearGradient(colors: [Colors.amber, Colors.red]), ), - height: 50, + constraints: const BoxConstraints(minWidth: 250), padding: const EdgeInsets.all(8.0), preferBelow: true, textStyle: const TextStyle(fontSize: 24), diff --git a/packages/flutter/lib/fix_data/fix_material/fix_tooltip.yaml b/packages/flutter/lib/fix_data/fix_material/fix_tooltip.yaml new file mode 100644 index 0000000000..545331167a --- /dev/null +++ b/packages/flutter/lib/fix_data/fix_material/fix_tooltip.yaml @@ -0,0 +1,83 @@ +# 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. + +# For details regarding the *Flutter Fix* feature, see https://flutter.dev/to/flutter-fix + +# Please add new fixes to the top of the file, separated by one blank line +# from other fixes. In a comment, include a link to the PR where the change +# requiring the fix was made. + +# Every fix must be tested. See the flutter/packages/flutter/test_fixes/README.md +# file for instructions on testing these data driven fixes. + +# For documentation about this file format, see https://dart.dev/go/data-driven-fixes. + +# * Fixes in this file are for the Tooltip widget from the Material library. * + +version: 1 +transforms: + # Changes made in https://github.com/flutter/flutter/pull/163314 + - title: "Migrate to 'constraints'" + date: 2025-02-14 + element: + uris: ["package:flutter/material.dart"] + constructor: "" + inClass: "Tooltip" + oneOf: + - if: "height == 'null'" + changes: + - kind: "removeParameter" + name: "height" + - if: "constraints == '' && height != ''" + changes: + - kind: "addParameter" + index: 2 + name: "constraints" + style: "optional_named" + argumentValue: + expression: "{% BoxConstraints %}(minHeight: {% height %})" + requiredIf: "height != '' && height != 'null'" + - kind: "removeParameter" + name: "height" + - if: "constraints == 'null' && height != ''" + changes: + - kind: "removeParameter" + name: "constraints" + - kind: "addParameter" + index: 2 + name: "constraints" + style: "optional_named" + argumentValue: + expression: "{% BoxConstraints %}(minHeight: {% height %})" + requiredIf: "height != '' && height != 'null'" + - kind: "removeParameter" + name: "height" + - if: "constraints != '' && height != ''" + changes: + - kind: "removeParameter" + name: "height" + variables: + constraints: + kind: "fragment" + value: "arguments[constraints]" + height: + kind: "fragment" + value: "arguments[height]" + BoxConstraints: + kind: "import" + uris: ["package:flutter/rendering.dart"] + name: "BoxConstraints" + + # Changes made in https://github.com/flutter/flutter/pull/163314 + - title: "Migrate to 'constraints'" + date: 2025-02-14 + element: + uris: ["package:flutter/material.dart"] + getter: "height" + inClass: "Tooltip" + changes: + - kind: "rename" + newName: "constraints?.minHeight" + +# Before adding a new fix: read instructions at the top of this file. diff --git a/packages/flutter/lib/fix_data/fix_material/fix_tooltip_theme_data.yaml b/packages/flutter/lib/fix_data/fix_material/fix_tooltip_theme_data.yaml new file mode 100644 index 0000000000..a5b8581d06 --- /dev/null +++ b/packages/flutter/lib/fix_data/fix_material/fix_tooltip_theme_data.yaml @@ -0,0 +1,135 @@ +# 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. + +# For details regarding the *Flutter Fix* feature, see https://flutter.dev/to/flutter-fix + +# Please add new fixes to the top of the file, separated by one blank line +# from other fixes. In a comment, include a link to the PR where the change +# requiring the fix was made. + +# Every fix must be tested. See the flutter/packages/flutter/test_fixes/README.md +# file for instructions on testing these data driven fixes. + +# For documentation about this file format, see https://dart.dev/go/data-driven-fixes. + +# * Fixes in this file are for the TooltipThemeData class from the Material library. * + +version: 1 +transforms: + # Changes made in https://github.com/flutter/flutter/pull/163314 + - title: "Migrate to 'constraints'" + date: 2025-02-14 + element: + uris: ["package:flutter/material.dart"] + constructor: "" + inClass: "TooltipThemeData" + oneOf: + - if: "height == 'null'" + changes: + - kind: "removeParameter" + name: "height" + - if: "constraints == '' && height != ''" + changes: + - kind: "addParameter" + index: 0 + name: "constraints" + style: "optional_named" + argumentValue: + expression: "{% BoxConstraints %}(minHeight: {% height %})" + requiredIf: "height != '' && height != 'null'" + - kind: "removeParameter" + name: "height" + - if: "constraints == 'null' && height != ''" + changes: + - kind: "removeParameter" + name: "constraints" + - kind: "addParameter" + index: 0 + name: "constraints" + style: "optional_named" + argumentValue: + expression: "{% BoxConstraints %}(minHeight: {% height %})" + requiredIf: "height != '' && height != 'null'" + - kind: "removeParameter" + name: "height" + - if: "constraints != '' && height != ''" + changes: + - kind: "removeParameter" + name: "height" + variables: + constraints: + kind: "fragment" + value: "arguments[constraints]" + height: + kind: "fragment" + value: "arguments[height]" + BoxConstraints: + kind: "import" + uris: ["package:flutter/rendering.dart"] + name: "BoxConstraints" + + # Changes made in https://github.com/flutter/flutter/pull/163314 + - title: "Migrate to 'constraints'" + date: 2025-02-14 + element: + uris: ["package:flutter/material.dart"] + getter: "height" + inClass: "TooltipThemeData" + changes: + - kind: "rename" + newName: "constraints?.minHeight" + + # Changes made in https://github.com/flutter/flutter/pull/163314 + - title: "Migrate to 'constraints'" + date: 2025-02-14 + element: + uris: ["package:flutter/material.dart"] + method: "copyWith" + inClass: "TooltipThemeData" + oneOf: + - if: "height == 'null'" + changes: + - kind: "removeParameter" + name: "height" + - if: "constraints == '' && height != ''" + changes: + - kind: "addParameter" + index: 0 + name: "constraints" + style: "optional_named" + argumentValue: + expression: "{% BoxConstraints %}(minHeight: {% height %})" + requiredIf: "height != '' && height != 'null'" + - kind: "removeParameter" + name: "height" + - if: "constraints == 'null' && height != ''" + changes: + - kind: "removeParameter" + name: "constraints" + - kind: "addParameter" + index: 0 + name: "constraints" + style: "optional_named" + argumentValue: + expression: "{% BoxConstraints %}(minHeight: {% height %})" + requiredIf: "height != '' && height != 'null'" + - kind: "removeParameter" + name: "height" + - if: "constraints != '' && height != ''" + changes: + - kind: "removeParameter" + name: "height" + variables: + constraints: + kind: "fragment" + value: "arguments[constraints]" + height: + kind: "fragment" + value: "arguments[height]" + BoxConstraints: + kind: "import" + uris: ["package:flutter/rendering.dart"] + name: "BoxConstraints" + +# Before adding a new fix: read instructions at the top of this file. diff --git a/packages/flutter/lib/src/material/tooltip.dart b/packages/flutter/lib/src/material/tooltip.dart index 6a9f551128..cdf7338f3b 100644 --- a/packages/flutter/lib/src/material/tooltip.dart +++ b/packages/flutter/lib/src/material/tooltip.dart @@ -118,7 +118,7 @@ class _RenderExclusiveMouseRegion extends RenderMouseRegion { /// {@tool dartpad} /// This example covers most of the attributes available in Tooltip. /// `decoration` has been used to give a gradient and borderRadius to Tooltip. -/// `height` has been used to set a specific height of the 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. @@ -169,7 +169,12 @@ class Tooltip extends StatefulWidget { 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, @@ -191,6 +196,10 @@ class Tooltip extends StatefulWidget { }) : 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. @@ -203,12 +212,22 @@ class Tooltip extends StatefulWidget { /// Only one of [message] and [richMessage] may be non-null. final InlineSpan? richMessage; - /// The height of the tooltip's [child]. - /// - /// If the [child] is null, then this is the tooltip's intrinsic height. + /// 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; - /// The amount of space by which to inset the tooltip's [child]. + /// 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. @@ -225,6 +244,10 @@ class Tooltip extends StatefulWidget { /// 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. @@ -412,6 +435,9 @@ class Tooltip extends StatefulWidget { ), ); properties.add(DoubleProperty('height', height, defaultValue: null)); + properties.add( + DiagnosticsProperty('constraints', constraints, defaultValue: null), + ); properties.add(DiagnosticsProperty('padding', padding, defaultValue: null)); properties.add(DiagnosticsProperty('margin', margin, defaultValue: null)); properties.add(DoubleProperty('vertical offset', verticalOffset, defaultValue: null)); @@ -837,9 +863,12 @@ class TooltipState extends State with SingleTickerProviderStateMixin { }; final TooltipThemeData tooltipTheme = _tooltipTheme; + final BoxConstraints defaultConstraints = BoxConstraints( + minHeight: widget.height ?? tooltipTheme.height ?? _getDefaultTooltipHeight(), + ); final _TooltipOverlay overlayChild = _TooltipOverlay( richMessage: widget.richMessage ?? TextSpan(text: widget.message), - height: widget.height ?? tooltipTheme.height ?? _getDefaultTooltipHeight(), + constraints: widget.constraints ?? tooltipTheme.constraints ?? defaultConstraints, padding: widget.padding ?? tooltipTheme.padding ?? _getDefaultPadding(), margin: widget.margin ?? tooltipTheme.margin ?? _defaultMargin, onEnter: _handleMouseEnter, @@ -967,8 +996,8 @@ class _TooltipPositionDelegate extends SingleChildLayoutDelegate { class _TooltipOverlay extends StatelessWidget { const _TooltipOverlay({ - required this.height, required this.richMessage, + required this.constraints, this.padding, this.margin, this.decoration, @@ -984,7 +1013,7 @@ class _TooltipOverlay extends StatelessWidget { }); final InlineSpan richMessage; - final double height; + final BoxConstraints constraints; final EdgeInsetsGeometry? padding; final EdgeInsetsGeometry? margin; final Decoration? decoration; @@ -1003,7 +1032,7 @@ class _TooltipOverlay extends StatelessWidget { Widget result = FadeTransition( opacity: animation, child: ConstrainedBox( - constraints: BoxConstraints(minHeight: height), + constraints: constraints, child: DefaultTextStyle( style: textStyle, textAlign: textAlign, diff --git a/packages/flutter/lib/src/material/tooltip_theme.dart b/packages/flutter/lib/src/material/tooltip_theme.dart index 2506d59e31..4362e8fff7 100644 --- a/packages/flutter/lib/src/material/tooltip_theme.dart +++ b/packages/flutter/lib/src/material/tooltip_theme.dart @@ -38,7 +38,12 @@ import 'theme.dart'; class TooltipThemeData with Diagnosticable { /// Creates the set of properties used to configure [Tooltip]s. const TooltipThemeData({ + @Deprecated( + 'Use TooltipThemeData.constraints instead. ' + 'This feature was deprecated after v3.30.0-0.1.pre.', + ) this.height, + this.constraints, this.padding, this.margin, this.verticalOffset, @@ -52,12 +57,22 @@ class TooltipThemeData with Diagnosticable { this.exitDuration, this.triggerMode, this.enableFeedback, - }); + }) : assert( + height == null || constraints == null, + 'Only one of `height` and `constraints` may be specified.', + ); - /// The height of [Tooltip.child]. + /// The minimum height of the [Tooltip]'s message. + @Deprecated( + 'Use TooltipThemeData.constraints instead. ' + 'This feature was deprecated after v3.30.0-0.1.pre.', + ) final double? height; - /// If provided, the amount of space by which to inset [Tooltip.child]. + /// Constrains the size of the [Tooltip]'s message. + final BoxConstraints? constraints; + + /// If provided, the amount of space by which to inset the [Tooltip]'s message. final EdgeInsetsGeometry? padding; /// If provided, the amount of empty space to surround the [Tooltip]. @@ -128,7 +143,12 @@ class TooltipThemeData with Diagnosticable { /// Creates a copy of this object but with the given fields replaced with the /// new values. TooltipThemeData copyWith({ + @Deprecated( + 'Use TooltipThemeData.constraints instead. ' + 'This feature was deprecated after v3.30.0-0.1.pre.', + ) double? height, + BoxConstraints? constraints, EdgeInsetsGeometry? padding, EdgeInsetsGeometry? margin, double? verticalOffset, @@ -145,6 +165,7 @@ class TooltipThemeData with Diagnosticable { }) { return TooltipThemeData( height: height ?? this.height, + constraints: constraints ?? this.constraints, padding: padding ?? this.padding, margin: margin ?? this.margin, verticalOffset: verticalOffset ?? this.verticalOffset, @@ -171,6 +192,7 @@ class TooltipThemeData with Diagnosticable { } return TooltipThemeData( height: lerpDouble(a?.height, b?.height, t), + constraints: BoxConstraints.lerp(a?.constraints, b?.constraints, t), padding: EdgeInsetsGeometry.lerp(a?.padding, b?.padding, t), margin: EdgeInsetsGeometry.lerp(a?.margin, b?.margin, t), verticalOffset: lerpDouble(a?.verticalOffset, b?.verticalOffset, t), @@ -185,6 +207,7 @@ class TooltipThemeData with Diagnosticable { @override int get hashCode => Object.hash( height, + constraints, padding, margin, verticalOffset, @@ -210,6 +233,7 @@ class TooltipThemeData with Diagnosticable { } return other is TooltipThemeData && other.height == height && + other.constraints == constraints && other.padding == padding && other.margin == margin && other.verticalOffset == verticalOffset && @@ -229,6 +253,9 @@ class TooltipThemeData with Diagnosticable { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(DoubleProperty('height', height, defaultValue: null)); + properties.add( + DiagnosticsProperty('constraints', constraints, defaultValue: null), + ); properties.add(DiagnosticsProperty('padding', padding, defaultValue: null)); properties.add(DiagnosticsProperty('margin', margin, defaultValue: null)); properties.add(DoubleProperty('vertical offset', verticalOffset, defaultValue: null)); diff --git a/packages/flutter/test/material/tooltip_test.dart b/packages/flutter/test/material/tooltip_test.dart index 25ec32cb8b..80e26ecd36 100644 --- a/packages/flutter/test/material/tooltip_test.dart +++ b/packages/flutter/test/material/tooltip_test.dart @@ -3449,6 +3449,35 @@ void main() { expect(textStyle, same(expectedTextStyle)); expect(defaultTextStyle, same(expectedTextStyle)); }); + + testWidgets('Tooltip respects and prefers the given constraints over theme constraints', ( + WidgetTester tester, + ) async { + final GlobalKey tooltipKey = GlobalKey(); + const BoxConstraints themeConstraints = BoxConstraints.tightFor(width: 300, height: 150); + const BoxConstraints tooltipConstraints = BoxConstraints.tightFor(width: 500, height: 250); + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(tooltipTheme: const TooltipThemeData(constraints: themeConstraints)), + home: Tooltip( + key: tooltipKey, + message: tooltipText, + constraints: tooltipConstraints, + padding: EdgeInsets.zero, + child: const ColoredBox(color: Colors.green), + ), + ), + ); + + tooltipKey.currentState?.ensureTooltipVisible(); + await tester.pump(const Duration(seconds: 2)); + + final Finder textAncestors = find.ancestor( + of: find.text(tooltipText), + matching: find.byWidgetPredicate((_) => true), + ); + expect(tester.element(textAncestors.first).size, equals(tooltipConstraints.biggest)); + }); } Future setWidgetForTooltipMode( diff --git a/packages/flutter/test/material/tooltip_theme_test.dart b/packages/flutter/test/material/tooltip_theme_test.dart index 6dc5610253..291cabd5f5 100644 --- a/packages/flutter/test/material/tooltip_theme_test.dart +++ b/packages/flutter/test/material/tooltip_theme_test.dart @@ -27,6 +27,7 @@ void main() { test('TooltipThemeData defaults', () { const TooltipThemeData theme = TooltipThemeData(); expect(theme.height, null); + expect(theme.constraints, null); expect(theme.padding, null); expect(theme.verticalOffset, null); expect(theme.preferBelow, null); @@ -1520,6 +1521,31 @@ void main() { expect(description, ['"message"']); }); + + testWidgets('Tooltip respects constraints from the ambient theme', (WidgetTester tester) async { + final GlobalKey tooltipKey = GlobalKey(); + const BoxConstraints themeConstraints = BoxConstraints.tightFor(width: 300, height: 150); + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(tooltipTheme: const TooltipThemeData(constraints: themeConstraints)), + home: Tooltip( + key: tooltipKey, + message: tooltipText, + padding: EdgeInsets.zero, + child: const ColoredBox(color: Colors.green), + ), + ), + ); + + tooltipKey.currentState?.ensureTooltipVisible(); + await tester.pump(const Duration(seconds: 2)); + + final Finder textAncestors = find.ancestor( + of: find.text(tooltipText), + matching: find.byWidgetPredicate((_) => true), + ); + expect(tester.element(textAncestors.first).size, equals(themeConstraints.biggest)); + }); } SemanticsNode findDebugSemantics(RenderObject object) { diff --git a/packages/flutter/test_fixes/material/tooltip.dart b/packages/flutter/test_fixes/material/tooltip.dart new file mode 100644 index 0000000000..8332893ce7 --- /dev/null +++ b/packages/flutter/test_fixes/material/tooltip.dart @@ -0,0 +1,15 @@ +// 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 'package:flutter/material.dart'; + +void main() { + // Changes made in https://github.com/flutter/flutter/pull/163314 + Tooltip tooltip = Tooltip(); + tooltip = Tooltip(height: null); + tooltip = Tooltip(height: 15.0); + tooltip = Tooltip(constraints: null, height: 15.0); + tooltip = Tooltip(constraints: BoxConstraints(maxWidth: 20.0), height: 15.0); + tooltip.height; +} diff --git a/packages/flutter/test_fixes/material/tooltip.dart.expect b/packages/flutter/test_fixes/material/tooltip.dart.expect new file mode 100644 index 0000000000..1e550e9860 --- /dev/null +++ b/packages/flutter/test_fixes/material/tooltip.dart.expect @@ -0,0 +1,15 @@ +// 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 'package:flutter/material.dart'; + +void main() { + // Changes made in https://github.com/flutter/flutter/pull/163314 + Tooltip tooltip = Tooltip(); + tooltip = Tooltip(); + tooltip = Tooltip(constraints: BoxConstraints(minHeight: 15.0)); + tooltip = Tooltip(constraints: BoxConstraints(minHeight: 15.0)); + tooltip = Tooltip(constraints: BoxConstraints(maxWidth: 20.0)); + tooltip.constraints?.minHeight; +} diff --git a/packages/flutter/test_fixes/material/tooltip_theme_data.dart b/packages/flutter/test_fixes/material/tooltip_theme_data.dart new file mode 100644 index 0000000000..683e6f703f --- /dev/null +++ b/packages/flutter/test_fixes/material/tooltip_theme_data.dart @@ -0,0 +1,25 @@ +// 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 'package:flutter/material.dart'; + +void main() { + // Changes made in https://github.com/flutter/flutter/pull/163314 + TooltipThemeData tooltipThemeData = TooltipThemeData(); + tooltipThemeData = TooltipThemeData(height: null); + tooltipThemeData = TooltipThemeData(height: 15.0); + tooltipThemeData = TooltipThemeData(constraints: null, height: 15.0); + tooltipThemeData = TooltipThemeData( + constraints: BoxConstraints(maxWidth: 20.0), + height: 15.0, + ); + tooltipThemeData.height; + tooltipThemeData.copyWith(height: null); + tooltipThemeData.copyWith(height: 15.0); + tooltipThemeData.copyWith(constraints: null, height: 15.0); + tooltipThemeData.copyWith( + constraints: BoxConstraints(maxWidth: 20.0), + height: 15.0, + ); +} diff --git a/packages/flutter/test_fixes/material/tooltip_theme_data.dart.expect b/packages/flutter/test_fixes/material/tooltip_theme_data.dart.expect new file mode 100644 index 0000000000..27f04422eb --- /dev/null +++ b/packages/flutter/test_fixes/material/tooltip_theme_data.dart.expect @@ -0,0 +1,23 @@ +// 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 'package:flutter/material.dart'; + +void main() { + // Changes made in https://github.com/flutter/flutter/pull/163314 + TooltipThemeData tooltipThemeData = TooltipThemeData(); + tooltipThemeData = TooltipThemeData(); + tooltipThemeData = TooltipThemeData(constraints: BoxConstraints(minHeight: 15.0)); + tooltipThemeData = TooltipThemeData(constraints: BoxConstraints(minHeight: 15.0)); + tooltipThemeData = TooltipThemeData( + constraints: BoxConstraints(maxWidth: 20.0), + ); + tooltipThemeData.constraints?.minHeight; + tooltipThemeData.copyWith(); + tooltipThemeData.copyWith(constraints: BoxConstraints(minHeight: 15.0)); + tooltipThemeData.copyWith(constraints: BoxConstraints(minHeight: 15.0)); + tooltipThemeData.copyWith( + constraints: BoxConstraints(maxWidth: 20.0), + ); +}