From c8d4dc151b714d3ed24076e8bfb825f9323854cc Mon Sep 17 00:00:00 2001 From: Julian Kalinowski Date: Mon, 7 Mar 2022 22:55:21 +0100 Subject: [PATCH 1/5] fix reactions box horizontal position --- lib/src/ui/reactions_box.dart | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/src/ui/reactions_box.dart b/lib/src/ui/reactions_box.dart index 4143f1e..0474c79 100644 --- a/lib/src/ui/reactions_box.dart +++ b/lib/src/ui/reactions_box.dart @@ -52,10 +52,8 @@ class ReactionsBox extends StatefulWidget { _ReactionsBoxState createState() => _ReactionsBoxState(); } -class _ReactionsBoxState extends State - with TickerProviderStateMixin { - final StreamController _dragStreamController = - StreamController(); +class _ReactionsBoxState extends State with TickerProviderStateMixin { + final StreamController _dragStreamController = StreamController(); late Stream _dragStream; @@ -94,18 +92,15 @@ class _ReactionsBoxState extends State // Calculating how much we should scale up when item hovered _itemScale = 1 + widget.itemScale; - _boxSizeController = - AnimationController(vsync: this, duration: widget.duration); + _boxSizeController = AnimationController(vsync: this, duration: widget.duration); _boxSizeTween = Tween(); _boxSizeAnimation = _boxSizeTween.animate(_boxSizeController); - _scaleController = - AnimationController(vsync: this, duration: widget.duration); + _scaleController = AnimationController(vsync: this, duration: widget.duration); final Tween scaleTween = Tween(begin: 0, end: 1); _scaleAnimation = scaleTween.animate(_scaleController) ..addStatusListener((status) { - if (status == AnimationStatus.reverse) - Navigator.of(context).pop(_selectedReaction); + if (status == AnimationStatus.reverse) Navigator.of(context).pop(_selectedReaction); }); _scaleController @@ -243,17 +238,22 @@ class _ReactionsBoxState extends State if (buttonX + (_boxSizeAnimation.value?.width ?? 0) < screenWidth) return buttonX - buttonRadius; + final value = buttonX + buttonRadius - (_boxSizeAnimation.value?.width ?? 0); + + //add this below code. + if (value.isNegative) { + return 20; // this is 20 horizontal width is fix you can play with it as you want. + } return buttonX + buttonRadius - (_boxSizeAnimation.value?.width ?? 0); } double _getVerticalPosition() { // check if TOP space not enough for the box - if (_getTopPosition() - widget.buttonSize.height * 4.5 < 0) - return _getBottomPosition(); + if (_getTopPosition() - widget.buttonSize.height * 4.5 < 0) return _getBottomPosition(); // check if BOTTOM space not enough for the box - if (_getBottomPosition() + (widget.buttonSize.height * 5.5) > - context.screenSize.height) return _getTopPosition(); + if (_getBottomPosition() + (widget.buttonSize.height * 5.5) > context.screenSize.height) + return _getTopPosition(); if (widget.position == Position.TOP) return _getTopPosition(); From cbb319158cdcf6502c39064ff36b33d136566234 Mon Sep 17 00:00:00 2001 From: Julian Kalinowski Date: Tue, 8 Mar 2022 13:04:05 +0100 Subject: [PATCH 2/5] add fixes for reaction box positioning --- lib/src/ui/reaction_button.dart | 4 ++-- lib/src/ui/reaction_button_toggle.dart | 14 +++++--------- lib/src/ui/reactions_box.dart | 14 +++++++------- pubspec.yaml | 2 +- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/lib/src/ui/reaction_button.dart b/lib/src/ui/reaction_button.dart index c4b3a0a..7c427a4 100644 --- a/lib/src/ui/reaction_button.dart +++ b/lib/src/ui/reaction_button.dart @@ -92,7 +92,7 @@ class _ReactionButtonState extends State> { } void _showReactionsBox() async { - final buttonOffset = _buttonKey.widgetPositionOffset; + final buttonRect = _buttonKey.widgetPositionRect; final buttonSize = _buttonKey.widgetSize; final reactionButton = await Navigator.of(context).push( PageRouteBuilder( @@ -100,7 +100,7 @@ class _ReactionButtonState extends State> { transitionDuration: const Duration(milliseconds: 200), pageBuilder: (_, __, ___) { return ReactionsBox( - buttonOffset: buttonOffset, + buttonRect: buttonRect!, buttonSize: buttonSize, reactions: widget.reactions, position: widget.boxPosition, diff --git a/lib/src/ui/reaction_button_toggle.dart b/lib/src/ui/reaction_button_toggle.dart index 69df9f4..17bf5f3 100644 --- a/lib/src/ui/reaction_button_toggle.dart +++ b/lib/src/ui/reaction_button_toggle.dart @@ -79,8 +79,7 @@ class _ReactionButtonToggleState extends State> { void _init() { _isChecked = widget.isChecked; - _selectedReaction = - _isChecked ? widget.selectedReaction : widget.initialReaction; + _selectedReaction = _isChecked ? widget.selectedReaction : widget.initialReaction; } @override @@ -124,14 +123,12 @@ class _ReactionButtonToggleState extends State> { void _onClickReactionButton() { _isChecked = !_isChecked; _updateReaction( - _isChecked - ? widget.selectedReaction ?? widget.reactions[0] - : widget.initialReaction, + _isChecked ? widget.selectedReaction ?? widget.reactions[0] : widget.initialReaction, ); } void _showReactionsBox() async { - final buttonOffset = _buttonKey.widgetPositionOffset; + final buttonRect = _buttonKey.widgetPositionRect; final buttonSize = _buttonKey.widgetSize; final reactionButton = await Navigator.of(context).push( PageRouteBuilder( @@ -139,7 +136,7 @@ class _ReactionButtonToggleState extends State> { transitionDuration: const Duration(milliseconds: 200), pageBuilder: (_, __, ___) { return ReactionsBox( - buttonOffset: buttonOffset, + buttonRect: buttonRect!, buttonSize: buttonSize, reactions: widget.reactions, position: widget.boxPosition, @@ -162,8 +159,7 @@ class _ReactionButtonToggleState extends State> { Reaction? reaction, [ bool isSelectedFromDialog = false, ]) { - _isChecked = - isSelectedFromDialog ? true : reaction != widget.initialReaction; + _isChecked = isSelectedFromDialog ? true : reaction != widget.initialReaction; widget.onReactionChanged.call( reaction?.value, _isChecked, diff --git a/lib/src/ui/reactions_box.dart b/lib/src/ui/reactions_box.dart index 0474c79..21338dd 100644 --- a/lib/src/ui/reactions_box.dart +++ b/lib/src/ui/reactions_box.dart @@ -10,7 +10,7 @@ import 'reactions_box_item.dart'; import 'widget_size_render_object.dart'; class ReactionsBox extends StatefulWidget { - final Offset buttonOffset; + final Rect buttonRect; final Size buttonSize; @@ -34,7 +34,7 @@ class ReactionsBox extends StatefulWidget { const ReactionsBox({ Key? key, - required this.buttonOffset, + required this.buttonRect, required this.buttonSize, required this.reactions, required this.position, @@ -231,7 +231,7 @@ class _ReactionsBoxState extends State with TickerProviderStateMix } double _getHorizontalPosition() { - final buttonX = widget.buttonOffset.dx; + final buttonX = widget.buttonRect.width; final buttonRadius = (widget.buttonSize.width / 2); final screenWidth = MediaQuery.of(context).size.width; @@ -249,10 +249,10 @@ class _ReactionsBoxState extends State with TickerProviderStateMix double _getVerticalPosition() { // check if TOP space not enough for the box - if (_getTopPosition() - widget.buttonSize.height * 4.5 < 0) return _getBottomPosition(); + if (_getTopPosition() - (_boxSizeAnimation.value?.height ?? 0) < 0) return _getBottomPosition(); // check if BOTTOM space not enough for the box - if (_getBottomPosition() + (widget.buttonSize.height * 5.5) > context.screenSize.height) + if (_getBottomPosition() + (_boxSizeAnimation.value?.height ?? 0) > context.screenSize.height) return _getTopPosition(); if (widget.position == Position.TOP) return _getTopPosition(); @@ -261,10 +261,10 @@ class _ReactionsBoxState extends State with TickerProviderStateMix } double _getTopPosition() { - return widget.buttonOffset.dy - (widget.buttonSize.height * 5); + return widget.buttonRect.top - (_boxSizeAnimation.value?.height ?? 0) - 8; } double _getBottomPosition() { - return widget.buttonOffset.dy; + return widget.buttonRect.bottom; } } diff --git a/pubspec.yaml b/pubspec.yaml index 76a0979..e81735f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_reaction_button description: Flutter button reaction it is fully customizable widget such as Facebook reaction button -version: 2.0.0+1 +version: 2.0.0+2 homepage: https://github.com/GeekAbdelouahed/flutter-reaction-button repository: https://github.com/GeekAbdelouahed/flutter-reaction-button.git From 6aac087c1005ef8dacabab47c3a5786aacfd3eb7 Mon Sep 17 00:00:00 2001 From: Julian Kalinowski Date: Thu, 26 Oct 2023 04:22:30 +0200 Subject: [PATCH 3/5] add positioning strategy --- lib/src/widgets/reactions_box.dart | 78 +++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/lib/src/widgets/reactions_box.dart b/lib/src/widgets/reactions_box.dart index 1c90f7c..2f550ac 100644 --- a/lib/src/widgets/reactions_box.dart +++ b/lib/src/widgets/reactions_box.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:flutter_reaction_button/flutter_reaction_button.dart'; import 'package:flutter_reaction_button/src/common/position.dart'; @@ -21,10 +23,13 @@ class ReactionsBox extends StatefulWidget { required this.onReactionSelected, required this.onClose, required this.animateBox, + this.positioningStrategy = const TryStayInBoundariesStrategy(), }) : assert(itemScale > 0.0 && itemScale < 1); final Offset offset; + final ReactionBoxPositioningStrategy positioningStrategy; + final Size itemSize; final List?> reactions; @@ -73,11 +78,7 @@ class _ReactionsBoxState extends State> (widget.itemSpace * (widget.reactions.length - 1)) + widget.boxPadding.horizontal; - bool get shouldStartFromEnd => - MediaQuery.sizeOf(context).width - boxWidth < widget.offset.dx; - - bool get shouldStartFromBottom => - widget.offset.dy < boxHeight + widget.boxPadding.vertical; + double get screenWidth => MediaQuery.sizeOf(context).width; bool _isOffsetOutsideBox(Offset offset) { final Rect boxRect = Rect.fromLTWH(0, 0, boxWidth, boxHeight); @@ -129,14 +130,16 @@ class _ReactionsBoxState extends State> ), ), PositionedDirectional( - start: shouldStartFromEnd - ? widget.offset.dx - boxWidth - : widget.offset.dx, - top: shouldStartFromBottom - ? widget.offset.dy + widget.itemSize.height - : widget.offset.dy - - widget.itemSize.height - - widget.boxPadding.vertical, + start: widget.positioningStrategy.getHorizontalOffset(context, + boxWidth: boxWidth, + screenWidth: screenWidth, + offset: widget.offset, + boxPadding: widget.boxPadding), + top: widget.positioningStrategy.getVerticalOffset(context, + boxHeight: boxHeight, + screenWidth: screenWidth, + offset: widget.offset, + boxPadding: widget.boxPadding), child: Listener( onPointerDown: (point) { _positionNotifier.value = PositionData( @@ -222,3 +225,52 @@ class _ReactionsBoxState extends State> ); } } + +abstract class ReactionBoxPositioningStrategy { + const ReactionBoxPositioningStrategy(); + double getHorizontalOffset(BuildContext context, + {required double boxWidth, + required double screenWidth, + required Offset offset, + required EdgeInsetsGeometry boxPadding}); + + double getVerticalOffset(BuildContext context, + {required double boxHeight, + required double screenWidth, + required Offset offset, + required EdgeInsetsGeometry boxPadding}) { + final shouldStartFromBottom = offset.dy < boxHeight + boxPadding.vertical; + return shouldStartFromBottom + ? offset.dy + boxHeight + : offset.dy - boxHeight - boxPadding.vertical; + } +} + +class TryStayInBoundariesStrategy extends ReactionBoxPositioningStrategy { + const TryStayInBoundariesStrategy(); + @override + double getHorizontalOffset(BuildContext context, + {required double boxWidth, + required double screenWidth, + required Offset offset, + required EdgeInsetsGeometry boxPadding}) { + final shouldStartFromEnd = offset.dx + boxWidth > screenWidth; + if (shouldStartFromEnd) { + return max(0, min(screenWidth - boxWidth, offset.dx - boxWidth)); + } else { + return max(0, offset.dx); + } + } +} + +class CenterHorizontallyStrategy extends ReactionBoxPositioningStrategy { + const CenterHorizontallyStrategy(); + @override + double getHorizontalOffset(BuildContext context, + {required double boxWidth, + required double screenWidth, + required Offset offset, + required EdgeInsetsGeometry boxPadding}) { + return (screenWidth - boxWidth) / 2; + } +} From 8129e9b3561a005e8f536c27613c0a91cc5766ee Mon Sep 17 00:00:00 2001 From: Julian Kalinowski Date: Thu, 26 Oct 2023 04:31:33 +0200 Subject: [PATCH 4/5] restore original pubspec --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 6356fe0..86cbdea 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ repository: https://github.com/GeekAbdelouahed/flutter-reaction-button.git version: 3.0.0+2 environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.0.0 <4.0.0' dependencies: flutter: From 3e030f79bf8e9df53ca83c9eee0a657cc7cccf3d Mon Sep 17 00:00:00 2001 From: Julian Kalinowski Date: Thu, 26 Oct 2023 05:12:47 +0200 Subject: [PATCH 5/5] add positioningStrategy to ReactionButton --- lib/src/widgets/reaction_button.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/src/widgets/reaction_button.dart b/lib/src/widgets/reaction_button.dart index 24cf855..769dd6a 100644 --- a/lib/src/widgets/reaction_button.dart +++ b/lib/src/widgets/reaction_button.dart @@ -25,6 +25,7 @@ class ReactionButton extends StatefulWidget { this.itemAnimationDuration = const Duration(milliseconds: 100), this.hoverDuration = const Duration(milliseconds: 400), this.child, + this.positioningStrategy = const TryStayInBoundariesStrategy(), }) : _type = child != null ? ReactionType.container : ReactionType.button; /// This triggers when reaction button value changed. @@ -66,6 +67,9 @@ class ReactionButton extends StatefulWidget { /// Animation duration while moving [default = const Duration(milliseconds: 100)] final Duration itemAnimationDuration; + /// Reaction box positioning strategy [default = const TryStayInBoundariesStrategy()] + final ReactionBoxPositioningStrategy positioningStrategy; + final Size itemSize; final bool animateBox; @@ -127,6 +131,7 @@ class _ReactionButtonState extends State> { itemScale: widget.itemScale, itemScaleDuration: widget.itemAnimationDuration, animateBox: widget.animateBox, + positioningStrategy: widget.positioningStrategy, onReactionSelected: (reaction) { _updateReaction(reaction); _disposeOverlayEntry();