From c315d5b97078adf4c21e23dc94fcb79d58bfde77 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 5 Nov 2024 11:31:44 -0500 Subject: [PATCH] added press animations to toolbar buttons --- lib/config/app_config.dart | 1 + lib/pangea/enum/message_mode_enum.dart | 6 +- .../chat/message_selection_overlay.dart | 4 +- .../widgets/chat/message_toolbar_buttons.dart | 75 ++++++++----- lib/pangea/widgets/pressable_button.dart | 106 ++++++++++++++++++ 5 files changed, 155 insertions(+), 37 deletions(-) create mode 100644 lib/pangea/widgets/pressable_button.dart diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index ab05ffd99..bec1df519 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -25,6 +25,7 @@ abstract class AppConfig { static const double toolbarMaxHeight = 300.0; static const double toolbarMinHeight = 70.0; static const double toolbarMinWidth = 270.0; + static const double toolbarButtonsHeight = 50.0; // #Pangea // static const Color primaryColor = Color(0xFF5625BA); // static const Color primaryColorLight = Color(0xFFCCBDEA); diff --git a/lib/pangea/enum/message_mode_enum.dart b/lib/pangea/enum/message_mode_enum.dart index cfc42f63b..f8ad41a5b 100644 --- a/lib/pangea/enum/message_mode_enum.dart +++ b/lib/pangea/enum/message_mode_enum.dart @@ -101,11 +101,7 @@ extension MessageModeExtension on MessageMode { } //unlocked and active - if (this == currentMode) { - return Theme.of(context).brightness == Brightness.dark - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.primary; - } + if (this == currentMode) return Theme.of(context).colorScheme.primary; //unlocked and inactive return Theme.of(context).colorScheme.primaryContainer; diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index 4c5cf86da..ea42c9854 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -281,9 +281,9 @@ class MessageOverlayController extends State return reactionsEvents.where((e) => !e.redacted).isNotEmpty; } - final double toolbarButtonsHeight = 50; double get reactionsHeight => hasReactions ? 28 : 0; - double get belowMessageHeight => toolbarButtonsHeight + reactionsHeight; + double get belowMessageHeight => + AppConfig.toolbarButtonsHeight + reactionsHeight; void setIsPlayingAudio(bool isPlaying) { if (mounted) { diff --git a/lib/pangea/widgets/chat/message_toolbar_buttons.dart b/lib/pangea/widgets/chat/message_toolbar_buttons.dart index 7cead7b5a..75cd4c5c3 100644 --- a/lib/pangea/widgets/chat/message_toolbar_buttons.dart +++ b/lib/pangea/widgets/chat/message_toolbar_buttons.dart @@ -6,6 +6,7 @@ import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pangea/enum/message_mode_enum.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart'; +import 'package:fluffychat/pangea/widgets/pressable_button.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; @@ -31,6 +32,7 @@ class ToolbarButtons extends StatelessWidget { MatrixState.pangeaController.languageController.userL2?.langCode; static const double iconWidth = 36.0; + static const buttonSize = 40.0; @override Widget build(BuildContext context) { @@ -44,7 +46,7 @@ class ToolbarButtons extends StatelessWidget { return SizedBox( width: width, - height: 50, + height: AppConfig.toolbarButtonsHeight, child: Stack( alignment: Alignment.center, children: [ @@ -75,37 +77,50 @@ class ToolbarButtons extends StatelessWidget { ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: modes - .mapIndexed( - (index, mode) => IconButton( - iconSize: 20, - icon: Icon(mode.icon), - tooltip: mode.tooltip(context), - color: mode == overlayController.toolbarMode - ? Colors.white - : null, - isSelected: mode == overlayController.toolbarMode, - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - mode.iconButtonColor( - context, - index, - overlayController.toolbarMode, - pangeaMessageEvent.numberOfActivitiesCompleted, - totallyDone, - ), - ), + children: modes.mapIndexed((index, mode) { + final enabled = mode.isUnlocked( + index, + pangeaMessageEvent.numberOfActivitiesCompleted, + totallyDone, + ); + final color = mode.iconButtonColor( + context, + index, + overlayController.toolbarMode, + pangeaMessageEvent.numberOfActivitiesCompleted, + totallyDone, + ); + return Tooltip( + message: mode.tooltip(context), + child: PressableButton( + width: buttonSize, + height: buttonSize, + borderRadius: BorderRadius.circular(20), + enabled: enabled, + depressed: !enabled || mode == overlayController.toolbarMode, + color: color, + onPressed: enabled + ? () => overlayController.updateToolbarMode(mode) + : null, + child: AnimatedContainer( + duration: FluffyThemes.animationDuration, + height: buttonSize, + width: buttonSize, + decoration: BoxDecoration( + color: color, + shape: BoxShape.circle, + ), + child: Icon( + mode.icon, + size: 20, + color: mode == overlayController.toolbarMode + ? Colors.white + : null, ), - onPressed: mode.isUnlocked( - index, - pangeaMessageEvent.numberOfActivitiesCompleted, - totallyDone, - ) - ? () => overlayController.updateToolbarMode(mode) - : null, ), - ) - .toList(), + ), + ); + }).toList(), ), ], ), diff --git a/lib/pangea/widgets/pressable_button.dart b/lib/pangea/widgets/pressable_button.dart new file mode 100644 index 000000000..b112dff0d --- /dev/null +++ b/lib/pangea/widgets/pressable_button.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class PressableButton extends StatefulWidget { + final double width; + final double height; + final BorderRadius borderRadius; + + final bool enabled; + final bool depressed; + + final Color color; + final Widget child; + final void Function()? onPressed; + + const PressableButton({ + required this.width, + required this.height, + required this.borderRadius, + required this.child, + required this.onPressed, + required this.color, + this.enabled = true, + this.depressed = false, + super.key, + }); + + @override + PressableButtonState createState() => PressableButtonState(); +} + +class PressableButtonState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _tweenAnimation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: const Duration(milliseconds: 100), + vsync: this, + ); + _tweenAnimation = Tween(begin: 5, end: 0).animate(_controller); + } + + void _onTapDown(TapDownDetails details) { + if (!widget.enabled) return; + _controller.forward(); + } + + void _onTapUp(TapUpDetails details) { + if (!widget.enabled) return; + _controller.reverse(); + widget.onPressed?.call(); + HapticFeedback.mediumImpact(); + } + + void _onTapCancel() { + if (!widget.enabled) return; + _controller.reverse(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTapDown: _onTapDown, + onTapUp: _onTapUp, + onTapCancel: _onTapCancel, + child: SizedBox( + height: 45, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + Container( + width: widget.width, + height: widget.height, + decoration: BoxDecoration( + color: Color.alphaBlend( + Colors.black.withOpacity(0.25), + widget.color, + ), + borderRadius: widget.borderRadius, + ), + ), + AnimatedBuilder( + animation: _tweenAnimation, + builder: (context, _) { + return Positioned( + bottom: widget.depressed ? 0 : _tweenAnimation.value, + child: widget.child, + ); + }, + ), + ], + ), + ), + ); + } +}