From 7915a3b61459c8321a0b9d4f8bd3b3bbf3f3a70d Mon Sep 17 00:00:00 2001 From: Michael Cadavillo Date: Thu, 21 Sep 2023 16:25:23 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E2=9C=A8Add=20staticContainer=20in?= =?UTF-8?q?to=20ToolTipWidget=20for=20a=20custom=20tooltip=20that=20can=20?= =?UTF-8?q?be=20placed=20anywhere=20on=20screen=20and=20is=20not=20affecte?= =?UTF-8?q?d=20by=20the=20animation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michael Angelo Cadavillo --- example/lib/main.dart | 27 +++++++++++++++++++++++++++ lib/src/showcase.dart | 6 ++++++ lib/src/tooltip_widget.dart | 4 ++++ 3 files changed, 37 insertions(+) diff --git a/example/lib/main.dart b/example/lib/main.dart index 950c78c6..0e5a23ad 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -416,6 +416,33 @@ 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, + ), + ), + ), + ), + child: const Text('Skip Showcase'), + onPressed: () => + ShowCaseWidget.of(context).dismiss(), + ), + ), + ], + ), container: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/src/showcase.dart b/lib/src/showcase.dart index a4d98644..5f9e1106 100644 --- a/lib/src/showcase.dart +++ b/lib/src/showcase.dart @@ -98,6 +98,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; + /// Defines background color for tooltip widget. /// /// Default to [Colors.white] @@ -308,6 +311,7 @@ class Showcase extends StatefulWidget { }) : 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, @@ -323,6 +327,7 @@ class Showcase extends StatefulWidget { required this.width, required this.container, required this.child, + this.staticContainer, this.targetShapeBorder = const RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(8), @@ -617,6 +622,7 @@ class _ShowcaseState extends State { titleTextStyle: widget.titleTextStyle, descTextStyle: widget.descTextStyle, container: widget.container, + staticContainer: widget.staticContainer, tooltipBackgroundColor: widget.tooltipBackgroundColor, textColor: widget.textColor, showArrow: widget.showArrow, diff --git a/lib/src/tooltip_widget.dart b/lib/src/tooltip_widget.dart index d4b67034..9ff532c8 100644 --- a/lib/src/tooltip_widget.dart +++ b/lib/src/tooltip_widget.dart @@ -40,6 +40,7 @@ class ToolTipWidget extends StatefulWidget { final TextStyle? titleTextStyle; final TextStyle? descTextStyle; final Widget? container; + final Widget? staticContainer; final Color? tooltipBackgroundColor; final Color? textColor; final bool showArrow; @@ -74,6 +75,7 @@ class ToolTipWidget extends StatefulWidget { required this.titleTextStyle, required this.descTextStyle, required this.container, + required this.staticContainer, required this.tooltipBackgroundColor, required this.textColor, required this.showArrow, @@ -492,6 +494,7 @@ class _ToolTipWidgetState extends State ), ); } + return Stack( children: [ Positioned( @@ -529,6 +532,7 @@ class _ToolTipWidgetState extends State ), ), ), + if (widget.staticContainer != null) ...[widget.staticContainer!], ], ); } From f2d362e664f41ed5b630adbabfd4dcebe8569698 Mon Sep 17 00:00:00 2001 From: Sahil-Simform Date: Tue, 22 Oct 2024 13:47:18 +0530 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=A9=B9Updated:=20Updated=20name=20of?= =?UTF-8?q?=20parameter=20and=20also=20added=20support=20for=20the=20float?= =?UTF-8?q?ingAction=20Widget=20for=20the=20default=20showcase=20widget?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 4 +- README.md | 97 +++++++-------- example/lib/main.dart | 79 ++++++++---- lib/showcaseview.dart | 1 + lib/src/showcase.dart | 12 +- lib/src/tooltip_widget.dart | 22 +++- lib/src/widget/floating_action_widget.dart | 134 +++++++++++++++++++++ 7 files changed, 270 insertions(+), 79 deletions(-) create mode 100644 lib/src/widget/floating_action_widget.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 43da4b3c..2f723aa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ ## [3.0.1] -- Feature [#475](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/475) - Add +- Feature [#475](https://github.com/SimformSolutionsPvtLtd/flutter_showcaseview/issues/475) - Added 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 425b8665..fc61d994 100644 --- a/README.md +++ b/README.md @@ -129,57 +129,58 @@ WidgetsBinding.instance.addPostFrameCallback((_) => | onStart | Function(int?, GlobalKey)? | | Triggered on start of each showcase. | | onComplete | Function(int?, GlobalKey)? | | Triggered on completion of each showcase. | | onFinish | VoidCallback? | | Triggered when all the showcases are completed | -| enableShowcase | bool | true | Enable or disable showcase globally. | -| toolTipMargin | double | 14 | For tooltip margin | +| enableShowcase | bool | true | Enable or disable showcase globally. | | ## Properties of `Showcase` and `Showcase.withWidget`: -| Name | Type | Default Behaviour | Description | `Showcase` | `ShowCaseWidget` | -|------------------------------|------------------|--------------------------------------------------|----------------------------------------------------------------------------------------------------|------------|------------------| -| key | GlobalKey | | Unique Global key for each showcase. | ✅ | ✅ | -| child | Widget | | The Target widget that you want to be showcased | ✅ | ✅ | -| title | String? | | Title of default tooltip | ✅ | | -| description | String? | | Description of default tooltip | ✅ | | -| container | Widget? | | Allows to create custom tooltip widget. | | ✅ | -| height | double? | | Height of custom tooltip widget | | ✅ | -| width | double? | | Width of custom tooltip widget | | ✅ | -| titleTextStyle | TextStyle? | | Text Style of title | ✅ | | -| descTextStyle | TextStyle? | | Text Style of description | ✅ | | -| titleAlignment | TextAlign | TextAlign.start | Alignment of title | ✅ | | -| descriptionAlignment | TextAlign | TextAlign.start | Alignment of description | ✅ | | -| targetShapeBorder | ShapeBorder | | If `targetBorderRadius` param is not provided then it applies shape border to target widget | ✅ | ✅ | -| targetBorderRadius | BorderRadius? | | Border radius of target widget | ✅ | ✅ | -| tooltipBorderRadius | BorderRadius? | BorderRadius.circular(8.0) | Border radius of tooltip | ✅ | | -| blurValue | double? | `ShowCaseWidget.blurValue` | Gaussian blur effect on overlay | ✅ | ✅ | -| tooltipPadding | EdgeInsets | EdgeInsets.symmetric(vertical: 8, horizontal: 8) | Padding to tooltip content | ✅ | | -| targetPadding | EdgeInsets | EdgeInsets.zero | Padding to target widget | ✅ | ✅ | -| overlayOpacity | double | 0.75 | Opacity of overlay layer | ✅ | ✅ | -| overlayColor | Color | Colors.black45 | Color of overlay layer | ✅ | ✅ | -| tooltipBackgroundColor | Color | Colors.white | Background Color of default tooltip | ✅ | | -| textColor | Color | Colors.black | Color of tooltip text | ✅ | | -| scrollLoadingWidget | Widget | | Loading widget on overlay until active showcase is visible to viewport when `autoScroll` is enable | ✅ | ✅ | -| movingAnimationDuration | Duration | Duration(milliseconds: 2000) | Duration of time this moving animation should last. | ✅ | ✅ | -| showArrow | bool | true | Shows tooltip with arrow | ✅ | | -| disableDefaultTargetGestures | bool | false | disable default gestures of target widget | ✅ | ✅ | -| disposeOnTap | bool? | false | Dismiss all showcases on target/tooltip tap | ✅ | ✅ | -| disableMovingAnimation | bool? | `ShowCaseWidget.disableMovingAnimation` | Disable bouncing/moving transition | ✅ | ✅ | -| disableScaleAnimation | bool? | `ShowCaseWidget.disableScaleAnimation` | Disable initial scale transition when showcase is being started and completed | ✅ | | -| scaleAnimationDuration | Duration | Duration(milliseconds: 300) | Duration of time scale animation should last. | ✅ | | -| scaleAnimationCurve | Curve | Curves.easeIn | Curve to use in scale animation. | ✅ | | -| scaleAnimationAlignment | Alignment? | | Origin of the coordinate in which the scale takes place, relative to the size of the box. | ✅ | | -| onToolTipClick | VoidCallback? | | Triggers when tooltip is being clicked. | ✅ | | -| onTargetClick | VoidCallback? | | Triggers when target widget is being clicked | ✅ | ✅ | -| onTargetDoubleTap | VoidCallback? | | Triggers when target widget is being double clicked | ✅ | ✅ | -| onTargetLongPress | VoidCallback? | | Triggers when target widget is being long pressed | ✅ | ✅ | -| onBarrierClick | VoidCallback? | | Triggers when barrier is clicked | ✅ | ✅ | -| tooltipPosition | TooltipPosition? | | Defines vertical position of tooltip respective to Target widget | ✅ | ✅ | -| titlePadding | EdgeInsets? | EdgeInsets.zero | Padding to title | ✅ | | -| descriptionPadding | EdgeInsets? | EdgeInsets.zero | Padding to description | ✅ | | -| titleTextDirection | TextDirection? | | Give textDirection to title | ✅ | | -| descriptionTextDirection | TextDirection? | | Give textDirection to description | ✅ | | -| descriptionTextDirection | TextDirection? | | Give textDirection to description | ✅ | | -| disableBarrierInteraction | bool | false | Disables barrier interaction for a particular showCase | ✅ | ✅ | -| toolTipSlideEndDistance | double | 7 | Defines motion range for tooltip slide animation | ✅ | ✅ | +| Name | Type | Default Behaviour | Description | `Showcase` | `ShowCaseWidget` | +|------------------------------|----------------------|--------------------------------------------------|----------------------------------------------------------------------------------------------------|------------|------------------| +| key | GlobalKey | | Unique Global key for each showcase. | ✅ | ✅ | +| child | Widget | | The Target widget that you want to be showcased | ✅ | ✅ | +| title | String? | | Title of default tooltip | ✅ | | +| description | String? | | Description of default tooltip | ✅ | | +| container | Widget? | | Allows to create custom tooltip widget. | | ✅ | +| height | double? | | Height of custom tooltip widget | | ✅ | +| width | double? | | Width of custom tooltip widget | | ✅ | +| titleTextStyle | TextStyle? | | Text Style of title | ✅ | | +| descTextStyle | TextStyle? | | Text Style of description | ✅ | | +| titleAlignment | TextAlign | TextAlign.start | Alignment of title | ✅ | | +| descriptionAlignment | TextAlign | TextAlign.start | Alignment of description | ✅ | | +| targetShapeBorder | ShapeBorder | | If `targetBorderRadius` param is not provided then it applies shape border to target widget | ✅ | ✅ | +| targetBorderRadius | BorderRadius? | | Border radius of target widget | ✅ | ✅ | +| tooltipBorderRadius | BorderRadius? | BorderRadius.circular(8.0) | Border radius of tooltip | ✅ | | +| blurValue | double? | `ShowCaseWidget.blurValue` | Gaussian blur effect on overlay | ✅ | ✅ | +| tooltipPadding | EdgeInsets | EdgeInsets.symmetric(vertical: 8, horizontal: 8) | Padding to tooltip content | ✅ | | +| targetPadding | EdgeInsets | EdgeInsets.zero | Padding to target widget | ✅ | ✅ | +| overlayOpacity | double | 0.75 | Opacity of overlay layer | ✅ | ✅ | +| overlayColor | Color | Colors.black45 | Color of overlay layer | ✅ | ✅ | +| tooltipBackgroundColor | Color | Colors.white | Background Color of default tooltip | ✅ | | +| textColor | Color | Colors.black | Color of tooltip text | ✅ | | +| scrollLoadingWidget | Widget | | Loading widget on overlay until active showcase is visible to viewport when `autoScroll` is enable | ✅ | ✅ | +| movingAnimationDuration | Duration | Duration(milliseconds: 2000) | Duration of time this moving animation should last. | ✅ | ✅ | +| showArrow | bool | true | Shows tooltip with arrow | ✅ | | +| disableDefaultTargetGestures | bool | false | disable default gestures of target widget | ✅ | ✅ | +| disposeOnTap | bool? | false | Dismiss all showcases on target/tooltip tap | ✅ | ✅ | +| disableMovingAnimation | bool? | `ShowCaseWidget.disableMovingAnimation` | Disable bouncing/moving transition | ✅ | ✅ | +| disableScaleAnimation | bool? | `ShowCaseWidget.disableScaleAnimation` | Disable initial scale transition when showcase is being started and completed | ✅ | | +| scaleAnimationDuration | Duration | Duration(milliseconds: 300) | Duration of time scale animation should last. | ✅ | | +| scaleAnimationCurve | Curve | Curves.easeIn | Curve to use in scale animation. | ✅ | | +| scaleAnimationAlignment | Alignment? | | Origin of the coordinate in which the scale takes place, relative to the size of the box. | ✅ | | +| onToolTipClick | VoidCallback? | | Triggers when tooltip is being clicked. | ✅ | | +| onTargetClick | VoidCallback? | | Triggers when target widget is being clicked | ✅ | ✅ | +| onTargetDoubleTap | VoidCallback? | | Triggers when target widget is being double clicked | ✅ | ✅ | +| onTargetLongPress | VoidCallback? | | Triggers when target widget is being long pressed | ✅ | ✅ | +| onBarrierClick | VoidCallback? | | Triggers when barrier is clicked | ✅ | ✅ | +| tooltipPosition | TooltipPosition? | | Defines vertical position of tooltip respective to Target widget | ✅ | ✅ | +| titlePadding | EdgeInsets? | EdgeInsets.zero | Padding to title | ✅ | | +| descriptionPadding | EdgeInsets? | EdgeInsets.zero | Padding to description | ✅ | | +| titleTextDirection | TextDirection? | | Give textDirection to title | ✅ | | +| descriptionTextDirection | TextDirection? | | Give textDirection to description | ✅ | | +| descriptionTextDirection | TextDirection? | | Give textDirection to description | ✅ | | +| disableBarrierInteraction | bool | false | Disables barrier interaction for a particular showCase | ✅ | ✅ | +| toolTipSlideEndDistance | double | 7 | Defines motion range for tooltip slide animation | ✅ | ✅ | +| toolTipMargin | double | 14 | For tooltip margin | ✅ | | +| floatingActionWidget | FloatingActionWidget | | Provided a floating static action widget to show at any place on the screen | ✅ | ✅ | ## How to use diff --git a/example/lib/main.dart b/example/lib/main.dart index 0e5a23ad..57fdc390 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -221,6 +221,38 @@ 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: 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', + style: TextStyle( + color: Colors.white, + fontSize: 15, + ), + ), + onPressed: () => + ShowCaseWidget.of(context).dismiss(), + ), + ), + ), targetShapeBorder: const CircleBorder(), child: Container( padding: const EdgeInsets.all(5), @@ -416,32 +448,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: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/showcaseview.dart b/lib/showcaseview.dart index 1c6a0cc0..8ea6b7d2 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/widget/floating_action_widget.dart'; diff --git a/lib/src/showcase.dart b/lib/src/showcase.dart index 5f9e1106..57ca33dc 100644 --- a/lib/src/showcase.dart +++ b/lib/src/showcase.dart @@ -32,6 +32,7 @@ import 'layout_overlays.dart'; import 'shape_clipper.dart'; import 'showcase_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. @@ -98,8 +99,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. /// @@ -308,10 +310,10 @@ class Showcase extends StatefulWidget { this.disableBarrierInteraction = false, this.toolTipSlideEndDistance = 7, this.toolTipMargin = 14, + 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, @@ -327,7 +329,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), @@ -622,7 +624,7 @@ class _ShowcaseState extends State { titleTextStyle: widget.titleTextStyle, descTextStyle: widget.descTextStyle, container: widget.container, - staticContainer: widget.staticContainer, + floatingActionWidget: widget.floatingActionWidget, tooltipBackgroundColor: widget.tooltipBackgroundColor, textColor: widget.textColor, showArrow: widget.showArrow, diff --git a/lib/src/tooltip_widget.dart b/lib/src/tooltip_widget.dart index 9ff532c8..6f079ba8 100644 --- a/lib/src/tooltip_widget.dart +++ b/lib/src/tooltip_widget.dart @@ -27,6 +27,7 @@ import 'package:flutter/material.dart'; import 'enum.dart'; import 'get_position.dart'; import 'measure_size.dart'; +import 'widget/floating_action_widget.dart'; import 'widget/tooltip_slide_transition.dart'; class ToolTipWidget extends StatefulWidget { @@ -40,7 +41,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; @@ -75,7 +76,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, @@ -367,7 +368,7 @@ class _ToolTipWidgetState extends State } if (widget.container == null) { - return Positioned( + final defaultToolTipWidget = Positioned( top: contentY, left: _getLeft(), right: _getRight(), @@ -493,9 +494,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(), @@ -532,7 +546,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, + ); + } +}