From 0f3f736fe6e83d7549c78d1ec4307cff54ab28a6 Mon Sep 17 00:00:00 2001 From: faiyaz Date: Wed, 15 Mar 2023 18:33:27 +0530 Subject: [PATCH 1/3] =?UTF-8?q?feature:=20=E2=9C=A8=20Tooltip=20action=20w?= =?UTF-8?q?idget?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added `toolTipAction` parameter in `Showcase` which is used to get action widget configuration and defaults to `null` - Added `DefaultToolTipActionWidget` class for default tooltip action widgets --- example/lib/main.dart | 45 ++++---- lib/showcaseview.dart | 1 + lib/src/showcase.dart | 9 ++ lib/src/tooltip_action.dart | 81 +++++++++++++++ lib/src/tooltip_widget.dart | 197 ++++++++++++++++++++---------------- 5 files changed, 224 insertions(+), 109 deletions(-) create mode 100644 lib/src/tooltip_action.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index 950c78c6..060cc69f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -232,6 +232,10 @@ class _MailPageState extends State { ), child: Image.asset('assets/simform.png'), ), + toolTipAction: DefaultToolTipActionWidget( + color: Colors.white, + showCaseWidgetState: ShowCaseWidget.of(context), + ), ), const SizedBox( width: 12, @@ -311,27 +315,28 @@ class _MailPageState extends State { child: Container( padding: const EdgeInsets.symmetric(vertical: 8), child: Showcase( - key: key, - description: 'Tap to check mail', - tooltipPosition: TooltipPosition.top, - disposeOnTap: true, - onTargetClick: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => const Detail(), - ), - ).then((_) { - setState(() { - ShowCaseWidget.of(context).startShowCase([_four, _five]); - }); + key: key, + description: 'Tap to check mail', + tooltipPosition: TooltipPosition.top, + disposeOnTap: true, + onTargetClick: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const Detail(), + ), + ).then((_) { + setState(() { + ShowCaseWidget.of(context).startShowCase([_four, _five]); }); - }, - child: MailTile( - mail: mail, - showCaseKey: _four, - showCaseDetail: showCaseDetail, - )), + }); + }, + child: MailTile( + mail: mail, + showCaseKey: _four, + showCaseDetail: showCaseDetail, + ), + ), ), ); } diff --git a/lib/showcaseview.dart b/lib/showcaseview.dart index 1c6a0cc0..7ffd2f13 100644 --- a/lib/showcaseview.dart +++ b/lib/showcaseview.dart @@ -25,3 +25,4 @@ library showcaseview; export 'src/enum.dart'; export 'src/showcase.dart'; export 'src/showcase_widget.dart'; +export 'src/tooltip_action.dart'; diff --git a/lib/src/showcase.dart b/lib/src/showcase.dart index 1779f98b..d690e6ef 100644 --- a/lib/src/showcase.dart +++ b/lib/src/showcase.dart @@ -31,6 +31,7 @@ import 'get_position.dart'; import 'layout_overlays.dart'; import 'shape_clipper.dart'; import 'showcase_widget.dart'; +import 'tooltip_action.dart'; import 'tooltip_widget.dart'; class Showcase extends StatefulWidget { @@ -231,6 +232,11 @@ class Showcase extends StatefulWidget { /// Provides padding around the description. Default padding is zero. final EdgeInsets? descriptionPadding; + /// Provides tooTip action widgets at bottom in tool tip. + /// + /// one can use [DefaultToolTipActionWidget] class to use default action + final Widget? toolTipAction; + /// Provides text direction of tooltip title. final TextDirection? titleTextDirection; @@ -293,6 +299,7 @@ class Showcase extends StatefulWidget { this.tooltipPosition, this.titlePadding, this.descriptionPadding, + this.toolTipAction, this.titleTextDirection, this.descriptionTextDirection, this.onBarrierClick, @@ -357,6 +364,7 @@ class Showcase extends StatefulWidget { tooltipPadding = const EdgeInsets.symmetric(vertical: 8), titlePadding = null, descriptionPadding = null, + toolTipAction = null, titleTextDirection = null, descriptionTextDirection = null, assert(overlayOpacity >= 0.0 && overlayOpacity <= 1.0, @@ -629,6 +637,7 @@ class _ShowcaseState extends State { tooltipPosition: widget.tooltipPosition, titlePadding: widget.titlePadding, descriptionPadding: widget.descriptionPadding, + toolTipAction: widget.toolTipAction, titleTextDirection: widget.titleTextDirection, descriptionTextDirection: widget.descriptionTextDirection, toolTipSlideEndDistance: widget.toolTipSlideEndDistance, diff --git a/lib/src/tooltip_action.dart b/lib/src/tooltip_action.dart new file mode 100644 index 00000000..37ef25cf --- /dev/null +++ b/lib/src/tooltip_action.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; + +import 'showcase_widget.dart'; + +/// Default Tooltip action Widget Nav +/// Shows tooltip navigation and index / count elements if the conditions are +/// indicated. +class DefaultToolTipActionWidget extends StatelessWidget { + const DefaultToolTipActionWidget({ + Key? key, + required this.color, + required this.showCaseWidgetState, + this.padding = const EdgeInsets.only(top: 5), + this.textStyle, + this.iconSize, + }) : super(key: key); + + final Color? color; + final ShowCaseWidgetState showCaseWidgetState; + final EdgeInsets padding; + final TextStyle? textStyle; + final double? iconSize; + + @override + Widget build(BuildContext context) { + var ids = showCaseWidgetState.ids; + var activeWidgetId = showCaseWidgetState.activeWidgetId; + bool isFirstTip = activeWidgetId == 0; + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: (isFirstTip) + ? null + : () { + showCaseWidgetState.previous(); + }, + child: Padding( + padding: padding, + child: Icon( + Icons.keyboard_arrow_left, + size: iconSize, + color: (isFirstTip) + ? color?.withOpacity(0.3) ?? Colors.black26 + : color, + ), + ), + ), + if (ids != null && activeWidgetId != null) ...[ + const SizedBox(width: 4.0), + Padding( + padding: padding, + child: Text( + "${activeWidgetId + 1} / ${ids.length}", + style: textStyle ?? + Theme.of(context).textTheme.bodyMedium?.copyWith( + color: color, + ), + ), + ), + const SizedBox(width: 4.0), + ], + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + showCaseWidgetState.next(); + }, + child: Padding( + padding: padding, + child: Icon( + Icons.keyboard_arrow_right, + color: color, + size: iconSize, + ), + ), + ), + ], + ); + } +} diff --git a/lib/src/tooltip_widget.dart b/lib/src/tooltip_widget.dart index dba238a2..b4073891 100644 --- a/lib/src/tooltip_widget.dart +++ b/lib/src/tooltip_widget.dart @@ -60,6 +60,7 @@ class ToolTipWidget extends StatefulWidget { final TooltipPosition? tooltipPosition; final EdgeInsets? titlePadding; final EdgeInsets? descriptionPadding; + final Widget? toolTipAction; final TextDirection? titleTextDirection; final TextDirection? descriptionTextDirection; final double toolTipSlideEndDistance; @@ -94,6 +95,7 @@ class ToolTipWidget extends StatefulWidget { this.tooltipPosition, this.titlePadding, this.descriptionPadding, + this.toolTipAction, this.titleTextDirection, this.descriptionTextDirection, this.toolTipSlideEndDistance = 7, @@ -115,15 +117,20 @@ class _ToolTipWidgetState extends State late final Animation _scaleAnimation; double tooltipWidth = 0; + double toolTipHeight = 0; double tooltipScreenEdgePadding = 20; double tooltipTextPadding = 15; + double actionWidgetHeight = 0.0; TooltipPosition findPositionForContent(Offset position) { - var height = 120.0; + var height = toolTipHeight; height = widget.contentHeight ?? height; - final bottomPosition = - position.dy + ((widget.position?.getHeight() ?? 0) / 2); - final topPosition = position.dy - ((widget.position?.getHeight() ?? 0) / 2); + final bottomPosition = position.dy + + ((widget.position?.getHeight() ?? 0) / 2) + + actionWidgetHeight; + final topPosition = position.dy - + ((widget.position?.getHeight() ?? 0) / 2) - + actionWidgetHeight; final hasSpaceInTop = topPosition >= height; // TODO: need to update for flutter version > 3.8.X // ignore: deprecated_member_use @@ -240,14 +247,7 @@ class _ToolTipWidgetState extends State } } - double _getAlignmentY() { - var dy = isArrowUp - ? -1.0 - : (MediaQuery.of(context).size.height / 2) < widget.position!.getTop() - ? -1.0 - : 1.0; - return dy; - } + double _getAlignmentY() => -1; final GlobalKey _customContainerKey = GlobalKey(); final ValueNotifier _customContainerWidth = ValueNotifier(1); @@ -388,69 +388,92 @@ class _ToolTipWidgetState extends State ).animate(_movingAnimation), child: Material( type: MaterialType.transparency, - child: Container( - padding: widget.showArrow - ? EdgeInsets.only( - top: paddingTop - (isArrowUp ? arrowHeight : 0), - bottom: paddingBottom - (isArrowUp ? 0 : arrowHeight), - ) - : null, - child: Stack( - alignment: isArrowUp - ? Alignment.topLeft - : _getLeft() == null - ? Alignment.bottomRight - : Alignment.bottomLeft, - children: [ - if (widget.showArrow) - Positioned( - left: _getArrowLeft(arrowWidth), - right: _getArrowRight(arrowWidth), - child: CustomPaint( - painter: _Arrow( - strokeColor: widget.tooltipBackgroundColor!, - strokeWidth: 10, - paintingStyle: PaintingStyle.fill, - isUpArrow: isArrowUp, - ), - child: const SizedBox( - height: arrowHeight, - width: arrowWidth, + child: MeasureSize( + onSizeChange: onTooltipSizeChanged, + child: Container( + padding: widget.showArrow + ? EdgeInsets.only( + top: paddingTop - (isArrowUp ? arrowHeight : 0), + bottom: + paddingBottom - (isArrowUp ? 0 : arrowHeight), + ) + : null, + child: Stack( + alignment: isArrowUp + ? Alignment.topLeft + : _getLeft() == null + ? Alignment.bottomRight + : Alignment.bottomLeft, + children: [ + if (widget.showArrow) + Positioned( + left: _getArrowLeft(arrowWidth), + right: _getArrowRight(arrowWidth), + child: CustomPaint( + painter: _Arrow( + strokeColor: widget.tooltipBackgroundColor!, + strokeWidth: 10, + paintingStyle: PaintingStyle.fill, + isUpArrow: isArrowUp, + ), + child: const SizedBox( + height: arrowHeight, + width: arrowWidth, + ), ), ), - ), - Padding( - padding: EdgeInsets.only( - top: isArrowUp ? arrowHeight - 1 : 0, - bottom: isArrowUp ? 0 : arrowHeight - 1, - ), - child: ClipRRect( - borderRadius: widget.tooltipBorderRadius ?? - BorderRadius.circular(8.0), - child: GestureDetector( - onTap: widget.onTooltipTap, - child: Container( - width: tooltipWidth, - padding: widget.tooltipPadding, - color: widget.tooltipBackgroundColor, - child: Column( - crossAxisAlignment: widget.title != null - ? CrossAxisAlignment.start - : CrossAxisAlignment.center, - children: [ - if (widget.title != null) + Padding( + padding: EdgeInsets.only( + top: isArrowUp ? arrowHeight - 1 : 0, + bottom: isArrowUp ? 0 : arrowHeight - 1, + ), + child: ClipRRect( + borderRadius: widget.tooltipBorderRadius ?? + BorderRadius.circular(8.0), + child: GestureDetector( + onTap: widget.onTooltipTap, + child: Container( + width: tooltipWidth, + padding: widget.tooltipPadding, + color: widget.tooltipBackgroundColor, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: widget.title != null + ? CrossAxisAlignment.start + : CrossAxisAlignment.center, + children: [ + if (widget.title != null) + Padding( + padding: widget.titlePadding ?? + EdgeInsets.zero, + child: Text( + widget.title!, + textAlign: widget.titleAlignment, + textDirection: + widget.titleTextDirection, + style: widget.titleTextStyle ?? + Theme.of(context) + .textTheme + .titleLarge! + .merge( + TextStyle( + color: widget.textColor, + ), + ), + ), + ), Padding( - padding: widget.titlePadding ?? + padding: widget.descriptionPadding ?? EdgeInsets.zero, child: Text( - widget.title!, - textAlign: widget.titleAlignment, + widget.description!, + textAlign: widget.descriptionAlignment, textDirection: - widget.titleTextDirection, - style: widget.titleTextStyle ?? + widget.descriptionTextDirection, + style: widget.descTextStyle ?? Theme.of(context) .textTheme - .titleLarge! + .titleSmall! .merge( TextStyle( color: widget.textColor, @@ -458,32 +481,16 @@ class _ToolTipWidgetState extends State ), ), ), - Padding( - padding: widget.descriptionPadding ?? - EdgeInsets.zero, - child: Text( - widget.description!, - textAlign: widget.descriptionAlignment, - textDirection: - widget.descriptionTextDirection, - style: widget.descTextStyle ?? - Theme.of(context) - .textTheme - .titleSmall! - .merge( - TextStyle( - color: widget.textColor, - ), - ), - ), - ), - ], + widget.toolTipAction ?? + const SizedBox.shrink() + ], + ), ), ), ), ), - ), - ], + ], + ), ), ), ), @@ -563,6 +570,18 @@ class _ToolTipWidgetState extends State (_getRight() ?? 0) - (arrowWidth / 2); } + + void onTooltipSizeChanged(Size? size) { + if (size == null) return; + setState(() { + if (size.width > widget.screenSize.width - tooltipScreenEdgePadding) { + tooltipWidth = widget.screenSize.width - tooltipScreenEdgePadding; + } else { + tooltipWidth = size.width; + } + toolTipHeight = size.height; + }); + } } class _Arrow extends CustomPainter { From 001c0b2ca3ec63d2585f974a2ac0f7a9944eab8a Mon Sep 17 00:00:00 2001 From: Rashi Shah Date: Wed, 3 Jul 2024 19:22:06 +0530 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=E2=9C=A8=20Tooltip=20action=20widg?= =?UTF-8?q?et?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added `ToolTipActionButton` class for tooltip action buttons --- lib/src/tooltip_action.dart | 55 +++++++++++++----------------- lib/src/toottip_action_button.dart | 34 ++++++++++++++++++ 2 files changed, 58 insertions(+), 31 deletions(-) create mode 100644 lib/src/toottip_action_button.dart diff --git a/lib/src/tooltip_action.dart b/lib/src/tooltip_action.dart index 37ef25cf..107f013a 100644 --- a/lib/src/tooltip_action.dart +++ b/lib/src/tooltip_action.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'showcase_widget.dart'; +import 'toottip_action_button.dart'; /// Default Tooltip action Widget Nav /// Shows tooltip navigation and index / count elements if the conditions are @@ -26,28 +27,23 @@ class DefaultToolTipActionWidget extends StatelessWidget { var ids = showCaseWidgetState.ids; var activeWidgetId = showCaseWidgetState.activeWidgetId; bool isFirstTip = activeWidgetId == 0; + bool isLastTip = activeWidgetId == (ids!.length - 1); + Color disabledIconColor = color?.withOpacity(0.3) ?? Colors.black26; return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: (isFirstTip) - ? null - : () { - showCaseWidgetState.previous(); - }, - child: Padding( + if (ids.isNotEmpty && activeWidgetId != null) ...[ + ToolTipActionButton( + action: (isFirstTip) + ? null + : () { + showCaseWidgetState.previous(); + }, padding: padding, - child: Icon( - Icons.keyboard_arrow_left, - size: iconSize, - color: (isFirstTip) - ? color?.withOpacity(0.3) ?? Colors.black26 - : color, - ), + icon: Icons.keyboard_arrow_left, + iconSize: iconSize, + color: (isFirstTip) ? disabledIconColor : color, ), - ), - if (ids != null && activeWidgetId != null) ...[ const SizedBox(width: 4.0), Padding( padding: padding, @@ -60,21 +56,18 @@ class DefaultToolTipActionWidget extends StatelessWidget { ), ), const SizedBox(width: 4.0), - ], - GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - showCaseWidgetState.next(); - }, - child: Padding( + ToolTipActionButton( + action: (isLastTip) + ? null + : () { + showCaseWidgetState.next(); + }, padding: padding, - child: Icon( - Icons.keyboard_arrow_right, - color: color, - size: iconSize, - ), - ), - ), + icon: Icons.keyboard_arrow_right, + iconSize: iconSize, + color: (isLastTip) ? disabledIconColor : color, + ) + ], ], ); } diff --git a/lib/src/toottip_action_button.dart b/lib/src/toottip_action_button.dart new file mode 100644 index 00000000..ba2f5c16 --- /dev/null +++ b/lib/src/toottip_action_button.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class ToolTipActionButton extends StatelessWidget { + const ToolTipActionButton({ + super.key, + required this.action, + required this.padding, + required this.icon, + required this.iconSize, + required this.color, + }); + + final VoidCallback? action; + final EdgeInsetsGeometry padding; + final IconData icon; + final double? iconSize; + final Color? color; + + @override + Widget build(BuildContext context) { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: action, + child: Padding( + padding: padding, + child: Icon( + icon, + size: iconSize, + color: color, + ), + ), + ); + } +} From af39e1042a6e68fb732fa47b169d3ad000129e48 Mon Sep 17 00:00:00 2001 From: harsh-u-simform Date: Thu, 11 Jul 2024 16:03:14 +0530 Subject: [PATCH 3/3] feat: :sparkle: Provide custom widget in tooltip action --- example/lib/main.dart | 18 +- lib/showcaseview.dart | 2 +- ...ction.dart => default_tooltip_action.dart} | 59 +++++-- lib/src/showcase.dart | 3 +- ...button.dart => tooltip_action_button.dart} | 27 ++- lib/src/tooltip_widget.dart | 161 ++++++++++-------- 6 files changed, 167 insertions(+), 103 deletions(-) rename lib/src/{tooltip_action.dart => default_tooltip_action.dart} (57%) rename lib/src/{toottip_action_button.dart => tooltip_action_button.dart} (57%) diff --git a/example/lib/main.dart b/example/lib/main.dart index 060cc69f..a8ae4ab2 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -232,9 +232,19 @@ class _MailPageState extends State { ), child: Image.asset('assets/simform.png'), ), - toolTipAction: DefaultToolTipActionWidget( + toolTipAction: DefaultToolTipAction( color: Colors.white, showCaseWidgetState: ShowCaseWidget.of(context), + back: const Icon( + Icons.arrow_back, + color: Colors.white, + ), + forward: const Icon( + Icons.arrow_forward, + color: Colors.white, + ), + onBackPress: () => debugPrint('Back Pressed!'), + onForwardPress: () => debugPrint('Forward Pressed!'), ), ), const SizedBox( @@ -451,6 +461,12 @@ class MailTile extends StatelessWidget { ) ], ), + toolTipAction: DefaultToolTipAction( + color: Colors.white, + showCaseWidgetState: ShowCaseWidget.of(context), + onBackPress: () => debugPrint('Back Pressed!'), + onForwardPress: () => debugPrint('Forward Pressed!'), + ), child: const SAvatarExampleChild(), ) else diff --git a/lib/showcaseview.dart b/lib/showcaseview.dart index 7ffd2f13..223507e1 100644 --- a/lib/showcaseview.dart +++ b/lib/showcaseview.dart @@ -22,7 +22,7 @@ library showcaseview; +export 'src/default_tooltip_action.dart'; export 'src/enum.dart'; export 'src/showcase.dart'; export 'src/showcase_widget.dart'; -export 'src/tooltip_action.dart'; diff --git a/lib/src/tooltip_action.dart b/lib/src/default_tooltip_action.dart similarity index 57% rename from lib/src/tooltip_action.dart rename to lib/src/default_tooltip_action.dart index 107f013a..925fdb5d 100644 --- a/lib/src/tooltip_action.dart +++ b/lib/src/default_tooltip_action.dart @@ -1,26 +1,36 @@ import 'package:flutter/material.dart'; import 'showcase_widget.dart'; -import 'toottip_action_button.dart'; +import 'tooltip_action_button.dart'; /// Default Tooltip action Widget Nav /// Shows tooltip navigation and index / count elements if the conditions are /// indicated. -class DefaultToolTipActionWidget extends StatelessWidget { - const DefaultToolTipActionWidget({ - Key? key, - required this.color, +class DefaultToolTipAction extends StatelessWidget { + const DefaultToolTipAction({ + super.key, + this.color = Colors.black, required this.showCaseWidgetState, this.padding = const EdgeInsets.only(top: 5), this.textStyle, this.iconSize, - }) : super(key: key); + this.back, + this.forward, + this.buttonColor, + this.onBackPress, + this.onForwardPress, + }); - final Color? color; + final Color color; final ShowCaseWidgetState showCaseWidgetState; final EdgeInsets padding; final TextStyle? textStyle; final double? iconSize; + final Widget? back; + final Widget? forward; + final Color? buttonColor; + final VoidCallback? onBackPress; + final VoidCallback? onForwardPress; @override Widget build(BuildContext context) { @@ -28,23 +38,30 @@ class DefaultToolTipActionWidget extends StatelessWidget { var activeWidgetId = showCaseWidgetState.activeWidgetId; bool isFirstTip = activeWidgetId == 0; bool isLastTip = activeWidgetId == (ids!.length - 1); - Color disabledIconColor = color?.withOpacity(0.3) ?? Colors.black26; + return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, children: [ if (ids.isNotEmpty && activeWidgetId != null) ...[ ToolTipActionButton( - action: (isFirstTip) + action: isFirstTip ? null : () { showCaseWidgetState.previous(); + onBackPress?.call(); }, padding: padding, - icon: Icons.keyboard_arrow_left, - iconSize: iconSize, - color: (isFirstTip) ? disabledIconColor : color, + widget: back ?? + Icon( + Icons.keyboard_arrow_left, + color: buttonColor ?? color, + ), + opacity: isFirstTip ? 0.3 : 1, + ), + const SizedBox( + width: 4.0, ), - const SizedBox(width: 4.0), Padding( padding: padding, child: Text( @@ -55,17 +72,23 @@ class DefaultToolTipActionWidget extends StatelessWidget { ), ), ), - const SizedBox(width: 4.0), + const SizedBox( + width: 4.0, + ), ToolTipActionButton( - action: (isLastTip) + action: isLastTip ? null : () { showCaseWidgetState.next(); + onForwardPress?.call(); }, padding: padding, - icon: Icons.keyboard_arrow_right, - iconSize: iconSize, - color: (isLastTip) ? disabledIconColor : color, + widget: forward ?? + Icon( + Icons.keyboard_arrow_right, + color: buttonColor ?? color, + ), + opacity: isLastTip ? 0.3 : 1, ) ], ], diff --git a/lib/src/showcase.dart b/lib/src/showcase.dart index d690e6ef..7f41fe02 100644 --- a/lib/src/showcase.dart +++ b/lib/src/showcase.dart @@ -31,7 +31,6 @@ import 'get_position.dart'; import 'layout_overlays.dart'; import 'shape_clipper.dart'; import 'showcase_widget.dart'; -import 'tooltip_action.dart'; import 'tooltip_widget.dart'; class Showcase extends StatefulWidget { @@ -346,6 +345,7 @@ class Showcase extends StatefulWidget { this.onBarrierClick, this.disableBarrierInteraction = false, this.toolTipSlideEndDistance = 7, + this.toolTipAction, }) : showArrow = false, onToolTipClick = null, scaleAnimationDuration = const Duration(milliseconds: 300), @@ -364,7 +364,6 @@ class Showcase extends StatefulWidget { tooltipPadding = const EdgeInsets.symmetric(vertical: 8), titlePadding = null, descriptionPadding = null, - toolTipAction = null, titleTextDirection = null, descriptionTextDirection = null, assert(overlayOpacity >= 0.0 && overlayOpacity <= 1.0, diff --git a/lib/src/toottip_action_button.dart b/lib/src/tooltip_action_button.dart similarity index 57% rename from lib/src/toottip_action_button.dart rename to lib/src/tooltip_action_button.dart index ba2f5c16..4991c733 100644 --- a/lib/src/toottip_action_button.dart +++ b/lib/src/tooltip_action_button.dart @@ -2,31 +2,30 @@ import 'package:flutter/material.dart'; class ToolTipActionButton extends StatelessWidget { const ToolTipActionButton({ - super.key, + Key? key, required this.action, required this.padding, - required this.icon, - required this.iconSize, - required this.color, - }); + required this.widget, + required this.opacity, + }) : super(key: key); final VoidCallback? action; final EdgeInsetsGeometry padding; - final IconData icon; - final double? iconSize; - final Color? color; + final Widget? widget; + final double opacity; @override Widget build(BuildContext context) { return GestureDetector( behavior: HitTestBehavior.translucent, onTap: action, - child: Padding( - padding: padding, - child: Icon( - icon, - size: iconSize, - color: color, + child: IgnorePointer( + child: Opacity( + opacity: opacity, + child: Padding( + padding: padding, + child: widget, + ), ), ), ); diff --git a/lib/src/tooltip_widget.dart b/lib/src/tooltip_widget.dart index b4073891..65bccd0f 100644 --- a/lib/src/tooltip_widget.dart +++ b/lib/src/tooltip_widget.dart @@ -432,29 +432,54 @@ class _ToolTipWidgetState extends State BorderRadius.circular(8.0), child: GestureDetector( onTap: widget.onTooltipTap, - child: Container( - width: tooltipWidth, - padding: widget.tooltipPadding, - color: widget.tooltipBackgroundColor, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: widget.title != null - ? CrossAxisAlignment.start - : CrossAxisAlignment.center, - children: [ - if (widget.title != null) + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: + MediaQuery.of(context).size.width - 30, + ), + child: Container( + width: tooltipWidth, + padding: widget.tooltipPadding, + color: widget.tooltipBackgroundColor, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: widget.title != null + ? CrossAxisAlignment.start + : CrossAxisAlignment.center, + children: [ + if (widget.title != null) + Padding( + padding: widget.titlePadding ?? + EdgeInsets.zero, + child: Text( + widget.title!, + textAlign: widget.titleAlignment, + textDirection: + widget.titleTextDirection, + style: widget.titleTextStyle ?? + Theme.of(context) + .textTheme + .titleLarge! + .merge( + TextStyle( + color: widget.textColor, + ), + ), + ), + ), Padding( - padding: widget.titlePadding ?? + padding: widget.descriptionPadding ?? EdgeInsets.zero, child: Text( - widget.title!, - textAlign: widget.titleAlignment, + widget.description!, + textAlign: + widget.descriptionAlignment, textDirection: - widget.titleTextDirection, - style: widget.titleTextStyle ?? + widget.descriptionTextDirection, + style: widget.descTextStyle ?? Theme.of(context) .textTheme - .titleLarge! + .titleSmall! .merge( TextStyle( color: widget.textColor, @@ -462,28 +487,10 @@ class _ToolTipWidgetState extends State ), ), ), - Padding( - padding: widget.descriptionPadding ?? - EdgeInsets.zero, - child: Text( - widget.description!, - textAlign: widget.descriptionAlignment, - textDirection: - widget.descriptionTextDirection, - style: widget.descTextStyle ?? - Theme.of(context) - .textTheme - .titleSmall! - .merge( - TextStyle( - color: widget.textColor, - ), - ), - ), - ), - widget.toolTipAction ?? - const SizedBox.shrink() - ], + widget.toolTipAction ?? + const SizedBox.shrink() + ], + ), ), ), ), @@ -504,30 +511,47 @@ class _ToolTipWidgetState extends State Positioned( left: _getSpace(), top: contentY - (10 * contentOffsetMultiplier), - child: FractionalTranslation( - translation: Offset(0.0, contentFractionalOffset as double), - child: ToolTipSlideTransition( - position: Tween( - begin: Offset.zero, - end: Offset( - 0, - widget.toolTipSlideEndDistance * contentOffsetMultiplier, + child: ScaleTransition( + scale: _scaleAnimation, + alignment: widget.scaleAnimationAlignment ?? + Alignment( + _getAlignmentX(), + _getAlignmentY(), ), - ).animate(_movingAnimation), - child: Material( - color: Colors.transparent, - child: GestureDetector( - onTap: widget.onTooltipTap, - child: Container( - padding: EdgeInsets.only( - top: paddingTop, - bottom: paddingBottom, - ), - color: Colors.transparent, - child: Center( - child: MeasureSize( - onSizeChange: onSizeChange, - child: widget.container, + child: FractionalTranslation( + translation: Offset(0.0, contentFractionalOffset as double), + child: ToolTipSlideTransition( + position: Tween( + begin: Offset.zero, + end: Offset( + 0, + widget.toolTipSlideEndDistance * contentOffsetMultiplier, + ), + ).animate(_movingAnimation), + child: Material( + color: Colors.transparent, + child: GestureDetector( + onTap: widget.onTooltipTap, + child: Container( + padding: EdgeInsets.only( + top: paddingTop, + bottom: paddingBottom, + ), + color: Colors.transparent, + child: Center( + child: MeasureSize( + onSizeChange: onSizeChange, + child: Column( + children: [ + widget.container!, + SizedBox( + width: widget.contentWidth, + child: widget.toolTipAction ?? + const SizedBox.shrink(), + ) + ], + ), + ), ), ), ), @@ -561,14 +585,14 @@ class _ToolTipWidgetState extends State double? _getArrowLeft(double arrowWidth) { final left = _getLeft(); if (left == null) return null; - return (widget.position!.getCenter() - (arrowWidth / 2) - left); + return (widget.position!.getCenter() - (arrowWidth * 0.5) - left); } double? _getArrowRight(double arrowWidth) { if (_getLeft() != null) return null; return (widget.screenSize.width - widget.position!.getCenter()) - (_getRight() ?? 0) - - (arrowWidth / 2); + (arrowWidth * 0.5); } void onTooltipSizeChanged(Size? size) { @@ -603,21 +627,24 @@ class _Arrow extends CustomPainter { @override void paint(Canvas canvas, Size size) { - canvas.drawPath(getTrianglePath(size.width, size.height), _paint); + canvas.drawPath( + getTrianglePath(size.width, size.height), + _paint, + ); } Path getTrianglePath(double x, double y) { if (isUpArrow) { return Path() ..moveTo(0, y) - ..lineTo(x / 2, 0) + ..lineTo(x * 0.5, 0) ..lineTo(x, y) ..lineTo(0, y); } return Path() ..moveTo(0, 0) ..lineTo(x, 0) - ..lineTo(x / 2, y) + ..lineTo(x * 0.5, y) ..lineTo(0, 0); }