From fda1fc784d9d8a835f78e339b80512b59211543e Mon Sep 17 00:00:00 2001 From: Sahil-Simform Date: Tue, 22 Oct 2024 13:47:18 +0530 Subject: [PATCH] =?UTF-8?q?=F0=9F=A9=B9Updated:=20Updated=20name=20of=20pa?= =?UTF-8?q?rameter=20and=20also=20added=20support=20for=20the=20floatingAc?= =?UTF-8?q?tion=20Widget=20for=20the=20default=20showcase=20widget?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 + README.md | 3 + example/lib/main.dart | 65 ++++++---- lib/showcaseview.dart | 1 + lib/src/showcase.dart | 14 ++- lib/src/showcase_widget.dart | 8 +- lib/src/tooltip_widget.dart | 22 +++- lib/src/widget/floating_action_widget.dart | 134 +++++++++++++++++++++ 8 files changed, 218 insertions(+), 31 deletions(-) create mode 100644 lib/src/widget/floating_action_widget.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b4a4cca..a89aa208 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ - Feature ✨: Added Action widget for tooltip - Feature [#475](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/475) - Add feasibility to change margin of tooltip with `toolTipMargin`. +- Feature [#395](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/395) - + Added `floatingActionWidget` to give a static fixed widget at any place on the screen. ## [3.0.0] - [BREAKING] Fixed [#434](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/434) removed deprecated text style after Flutter 3.22 follow [migration guide](https://docs.flutter.dev/release/breaking-changes/3-19-deprecations#texttheme) diff --git a/README.md b/README.md index 23ad0f9a..f2c1e77c 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,8 @@ WidgetsBinding.instance.addPostFrameCallback((_) => | toolTipMargin | double | 14 | For tooltip margin | | globalTooltipActionConfig | TooltipActionConfig? | | Global tooltip actionbar config | | globalTooltipActions | List? | | Global list of tooltip actions | +| globalTooltipActionConfig | FloatingActionWidget | | Global Config for tooltip action to auto apply for all the toolTip | + ## Properties of `Showcase` and `Showcase.withWidget`: @@ -208,6 +210,7 @@ WidgetsBinding.instance.addPostFrameCallback((_) => | toolTipSlideEndDistance | double | 7 | Defines motion range for tooltip slide animation | ✅ | ✅ | | tooltipActions | List? | [] | Provide a list of tooltip actions | ✅ | ✅ | | tooltipActionConfig | TooltipActionConfig? | | Give configurations (alignment, position, etc...) to the tooltip actionbar | ✅ | ✅ | +| floatingActionWidget | FloatingActionWidget | | Provided a floating static action widget to show at any place on the screen | ✅ | ✅ | ## Properties of `TooltipActionButton` and `TooltipActionButton.custom`: diff --git a/example/lib/main.dart b/example/lib/main.dart index 156a4420..f369ed49 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -256,6 +256,24 @@ class _MailPageState extends State { "Tap to see profile which contains user's name, profile picture, mobile number and country", tooltipBackgroundColor: Theme.of(context).primaryColor, textColor: Colors.white, + floatingActionWidget: FloatingActionWidget( + left: 16, + bottom: 16, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton( + child: const Text( + 'Skip Showcase', + style: TextStyle( + color: Colors.pink, + fontSize: 15, + ), + ), + onPressed: () => + ShowCaseWidget.of(context).dismiss(), + ), + ), + ), targetShapeBorder: const CircleBorder(), tooltipActionConfig: const TooltipActionConfig( alignment: MainAxisAlignment.spaceBetween, @@ -564,32 +582,37 @@ class MailTile extends StatelessWidget { targetBorderRadius: const BorderRadius.all( Radius.circular(150), ), - staticContainer: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Padding( - padding: const EdgeInsets.all(16.0), - child: ElevatedButton( - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all( - Theme.of(context).primaryColor), - shape: MaterialStateProperty.all< - RoundedRectangleBorder>( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(18.0), - side: BorderSide( - color: Theme.of(context).primaryColor, - width: 2.0, - ), + floatingActionWidget: FloatingActionWidget.directional( + textDirection: Directionality.of(context), + start: 0, + bottom: 0, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton( + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + Theme.of(context).primaryColor), + shape: MaterialStateProperty.all< + RoundedRectangleBorder>( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + side: BorderSide( + color: Theme.of(context).primaryColor, + width: 2.0, ), ), ), - child: const Text('Skip Showcase'), - onPressed: () => - ShowCaseWidget.of(context).dismiss(), ), + child: const Text( + 'Skip Showcase', + style: TextStyle( + color: Colors.white, + fontSize: 15, + ), + ), + onPressed: () => ShowCaseWidget.of(context).dismiss(), ), - ], + ), ), container: Container( padding: const EdgeInsets.all(10), diff --git a/lib/showcaseview.dart b/lib/showcaseview.dart index 3da2f1cd..3d597dd7 100644 --- a/lib/showcaseview.dart +++ b/lib/showcaseview.dart @@ -29,3 +29,4 @@ export 'src/models/tooltip_action_config.dart'; export 'src/showcase.dart'; export 'src/showcase_widget.dart'; export 'src/tooltip_action_button_widget.dart'; +export 'src/widget/floating_action_widget.dart'; diff --git a/lib/src/showcase.dart b/lib/src/showcase.dart index ef17113c..b2b1d2ad 100644 --- a/lib/src/showcase.dart +++ b/lib/src/showcase.dart @@ -35,6 +35,7 @@ import 'shape_clipper.dart'; import 'showcase_widget.dart'; import 'tooltip_action_button_widget.dart'; import 'tooltip_widget.dart'; +import 'widget/floating_action_widget.dart'; class Showcase extends StatefulWidget { /// A key that is unique across the entire app. @@ -96,8 +97,9 @@ class Showcase extends StatefulWidget { /// Custom tooltip widget when [Showcase.withWidget] is used. final Widget? container; - /// Custom static tooltip widget when [Showcase.withWidget] is used. - final Widget? staticContainer; + /// Custom static floating action widget to show a static widget anywhere + /// on the screen + final FloatingActionWidget? floatingActionWidget; /// Defines background color for tooltip widget. /// @@ -403,10 +405,10 @@ class Showcase extends StatefulWidget { this.toolTipMargin = 14, this.tooltipActions, this.tooltipActionConfig, + this.floatingActionWidget, }) : height = null, width = null, container = null, - staticContainer = null, assert(overlayOpacity >= 0.0 && overlayOpacity <= 1.0, "overlay opacity must be between 0 and 1."), assert(onTargetClick == null || disposeOnTap != null, @@ -456,6 +458,7 @@ class Showcase extends StatefulWidget { /// - `toolTipSlideEndDistance`: The distance the tooltip slides in from the edge of the screen (defaults to 7dp). /// - `tooltipActions`: A list of custom actions (widgets) to display within the tooltip. /// - `tooltipActionConfig`: Configuration options for custom tooltip actions. + /// - `floatingActionWidget`: Custom static floating action widget to show a static widget anywhere /// /// **Differences from default constructor:** /// @@ -473,7 +476,7 @@ class Showcase extends StatefulWidget { required this.width, required this.container, required this.child, - this.staticContainer, + this.floatingActionWidget, this.targetShapeBorder = const RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(8), @@ -774,7 +777,8 @@ class _ShowcaseState extends State { titleTextStyle: widget.titleTextStyle, descTextStyle: widget.descTextStyle, container: widget.container, - staticContainer: widget.staticContainer, + floatingActionWidget: widget.floatingActionWidget ?? + showCaseWidgetState.widget.globalFloatingActionWidget, tooltipBackgroundColor: widget.tooltipBackgroundColor, textColor: widget.textColor, showArrow: widget.showArrow, diff --git a/lib/src/showcase_widget.dart b/lib/src/showcase_widget.dart index c01d9109..91e6fd84 100644 --- a/lib/src/showcase_widget.dart +++ b/lib/src/showcase_widget.dart @@ -83,10 +83,14 @@ class ShowCaseWidget extends StatefulWidget { /// Enable/disable showcase globally. Enabled by default. final bool enableShowcase; + /// Custom static floating action widget to show a static widget anywhere + /// on the screen for all the showcase widget + final FloatingActionWidget? globalFloatingActionWidget; + /// Global action to apply on every tooltip widget final List? globalTooltipActions; - /// Global Config for tooltip action to auto apply for all the toolTip + /// Global Config for tooltip action to auto apply for all the toolTip. final TooltipActionConfig? globalTooltipActionConfig; /// A widget that manages multiple Showcase widgets. @@ -115,6 +119,7 @@ class ShowCaseWidget extends StatefulWidget { /// - `enableShowcase`: Enables or disables the showcase functionality globally (defaults to `true`). /// - `globalTooltipActions`: A list of custom actions to be added to all tooltips. /// - `globalTooltipActionConfig`: Configuration options for the global tooltip actions. + /// - `floatingActionWidget`: Custom static floating action widget to show a static widget anywhere for all the showcase widgets const ShowCaseWidget({ required this.builder, this.onFinish, @@ -132,6 +137,7 @@ class ShowCaseWidget extends StatefulWidget { this.enableShowcase = true, this.globalTooltipActionConfig, this.globalTooltipActions, + this.globalFloatingActionWidget, }); static GlobalKey? activeTargetWidget(BuildContext context) { diff --git a/lib/src/tooltip_widget.dart b/lib/src/tooltip_widget.dart index fec5e570..e634e316 100644 --- a/lib/src/tooltip_widget.dart +++ b/lib/src/tooltip_widget.dart @@ -29,6 +29,7 @@ import 'get_position.dart'; import 'measure_size.dart'; import 'models/tooltip_action_config.dart'; import 'widget/action_widget.dart'; +import 'widget/floating_action_widget.dart'; import 'widget/tooltip_slide_transition.dart'; class ToolTipWidget extends StatefulWidget { @@ -44,7 +45,7 @@ class ToolTipWidget extends StatefulWidget { final TextStyle? titleTextStyle; final TextStyle? descTextStyle; final Widget? container; - final Widget? staticContainer; + final FloatingActionWidget? floatingActionWidget; final Color? tooltipBackgroundColor; final Color? textColor; final bool showArrow; @@ -80,7 +81,7 @@ class ToolTipWidget extends StatefulWidget { required this.titleTextStyle, required this.descTextStyle, required this.container, - required this.staticContainer, + required this.floatingActionWidget, required this.tooltipBackgroundColor, required this.textColor, required this.showArrow, @@ -472,7 +473,7 @@ class _ToolTipWidgetState extends State } if (widget.container == null) { - return Positioned( + final defaultToolTipWidget = Positioned( top: contentY, left: _getLeft(), right: _getRight(), @@ -653,9 +654,22 @@ class _ToolTipWidgetState extends State ), ), ); + + if (widget.floatingActionWidget != null) { + return Stack( + fit: StackFit.expand, + children: [ + defaultToolTipWidget, + widget.floatingActionWidget!, + ], + ); + } else { + return defaultToolTipWidget; + } } return Stack( + fit: StackFit.expand, children: [ Positioned( left: _getSpace(), @@ -723,7 +737,7 @@ class _ToolTipWidgetState extends State ), ), ), - if (widget.staticContainer != null) ...[widget.staticContainer!], + if (widget.floatingActionWidget != null) widget.floatingActionWidget!, ], ); } diff --git a/lib/src/widget/floating_action_widget.dart b/lib/src/widget/floating_action_widget.dart new file mode 100644 index 00000000..c36af817 --- /dev/null +++ b/lib/src/widget/floating_action_widget.dart @@ -0,0 +1,134 @@ +import 'package:flutter/material.dart'; + +class FloatingActionWidget extends StatelessWidget { + const FloatingActionWidget({ + super.key, + required this.child, + this.right, + this.width, + this.height, + this.left, + this.bottom, + this.top, + }); + + /// This is same as the Positioned.directional widget + /// Creates a widget that controls where a child of a [Stack] is positioned. + /// + /// Only two out of the three horizontal values (`start`, `end`, + /// [width]), and only two out of the three vertical values ([top], + /// [bottom], [height]), can be set. In each case, at least one of + /// the three must be null. + /// + /// If `textDirection` is [TextDirection.rtl], then the `start` argument is + /// used for the [right] property and the `end` argument is used for the + /// [left] property. Otherwise, if `textDirection` is [TextDirection.ltr], + /// then the `start` argument is used for the [left] property and the `end` + /// argument is used for the [right] property. + factory FloatingActionWidget.directional({ + Key? key, + required TextDirection textDirection, + required Widget child, + double? start, + double? top, + double? end, + double? bottom, + double? width, + double? height, + }) { + double? left; + double? right; + switch (textDirection) { + case TextDirection.rtl: + left = end; + right = start; + break; + case TextDirection.ltr: + left = start; + right = end; + } + return FloatingActionWidget( + key: key, + left: left, + top: top, + right: right, + bottom: bottom, + width: width, + height: height, + child: child, + ); + } + + /// The widget below this widget in the tree. + /// + /// {@macro flutter.widgets.ProxyWidget.child} + final Widget child; + + /// The distance that the child's left edge is inset from the left of the stack. + /// + /// Only two out of the three horizontal values ([left], [right], [width]) can be + /// set. The third must be null. + /// + /// If all three are null, the [Stack.alignment] is used to position the child + /// horizontally. + final double? left; + + /// The distance that the child's top edge is inset from the top of the stack. + /// + /// Only two out of the three vertical values ([top], [bottom], [height]) can be + /// set. The third must be null. + /// + /// If all three are null, the [Stack.alignment] is used to position the child + /// vertically. + final double? top; + + /// The distance that the child's right edge is inset from the right of the stack. + /// + /// Only two out of the three horizontal values ([left], [right], [width]) can be + /// set. The third must be null. + /// + /// If all three are null, the [Stack.alignment] is used to position the child + /// horizontally. + final double? right; + + /// The distance that the child's bottom edge is inset from the bottom of the stack. + /// + /// Only two out of the three vertical values ([top], [bottom], [height]) can be + /// set. The third must be null. + /// + /// If all three are null, the [Stack.alignment] is used to position the child + /// vertically. + final double? bottom; + + /// The child's width. + /// + /// Only two out of the three horizontal values ([left], [right], [width]) can be + /// set. The third must be null. + /// + /// If all three are null, the [Stack.alignment] is used to position the child + /// horizontally. + final double? width; + + /// The child's height. + /// + /// Only two out of the three vertical values ([top], [bottom], [height]) can be + /// set. The third must be null. + /// + /// If all three are null, the [Stack.alignment] is used to position the child + /// vertically. + final double? height; + + @override + Widget build(BuildContext context) { + return Positioned( + key: key, + left: left, + top: top, + right: right, + bottom: bottom, + width: width, + height: height, + child: child, + ); + } +}