From 221e654dd669b24e5e4426131947098b47774b21 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 28 Nov 2022 13:50:55 -0600 Subject: [PATCH] animated main menu --- .../home/desktop_menu.dart | 92 +++++++++++--- .../home/desktop_menu_item.dart | 119 +++++++++++++++--- 2 files changed, 174 insertions(+), 37 deletions(-) diff --git a/lib/pages_desktop_specific/home/desktop_menu.dart b/lib/pages_desktop_specific/home/desktop_menu.dart index d82d62883..6ae4b91a1 100644 --- a/lib/pages_desktop_specific/home/desktop_menu.dart +++ b/lib/pages_desktop_specific/home/desktop_menu.dart @@ -38,6 +38,9 @@ class _DesktopMenuState extends ConsumerState { static const expandedWidth = 225.0; static const minimizedWidth = 72.0; + final Duration duration = const Duration(milliseconds: 250); + late final List controllers; + double _width = expandedWidth; void updateSelectedMenuItem(DesktopMenuItemId idKey) { @@ -49,26 +52,58 @@ class _DesktopMenuState extends ConsumerState { } void toggleMinimize() { + final expanded = _width == expandedWidth; + + for (var e in controllers) { + e.toggle?.call(); + } + setState(() { - _width = _width == expandedWidth ? minimizedWidth : expandedWidth; + _width = expanded ? minimizedWidth : expandedWidth; }); } + @override + void initState() { + controllers = [ + DMIController(), + DMIController(), + DMIController(), + DMIController(), + DMIController(), + DMIController(), + DMIController(), + DMIController(), + ]; + + super.initState(); + } + + @override + void dispose() { + for (var e in controllers) { + e.dispose(); + } + super.dispose(); + } + @override Widget build(BuildContext context) { return Material( color: Theme.of(context).extension()!.popupBG, - child: SizedBox( + child: AnimatedContainer( width: _width, + duration: duration, child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - SizedBox( - height: _width == expandedWidth ? 22 : 25, + const SizedBox( + height: 25, ), - SizedBox( + AnimatedContainer( + duration: duration, width: _width == expandedWidth ? 70 : 32, - height: _width == expandedWidth ? 70 : 32, + height: 70, //_width == expandedWidth ? 70 : 32, child: SvgPicture.asset( Assets.svg.stackIcon(context), ), @@ -76,18 +111,26 @@ class _DesktopMenuState extends ConsumerState { const SizedBox( height: 10, ), - Text( - _width == expandedWidth ? "Stack Wallet" : "", - style: STextStyles.desktopH2(context).copyWith( - fontSize: 18, - height: 23.4 / 18, + AnimatedOpacity( + duration: duration, + opacity: _width == expandedWidth ? 1 : 0, + child: SizedBox( + height: 28, + child: Text( + "Stack Wallet", + style: STextStyles.desktopH2(context).copyWith( + fontSize: 18, + height: 23.4 / 18, + ), + ), ), ), const SizedBox( height: 60, ), Expanded( - child: SizedBox( + child: AnimatedContainer( + duration: duration, width: _width == expandedWidth ? _width - 32 // 16 padding on either side : _width - 16, // 8 padding on either side @@ -95,6 +138,7 @@ class _DesktopMenuState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ DesktopMenuItem( + duration: duration, icon: SvgPicture.asset( Assets.svg.walletDesktop, width: 20, @@ -116,12 +160,13 @@ class _DesktopMenuState extends ConsumerState { group: ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: updateSelectedMenuItem, - iconOnly: _width == minimizedWidth, + controller: controllers[0], ), const SizedBox( height: 2, ), DesktopMenuItem( + duration: duration, icon: SvgPicture.asset( Assets.svg.exchangeDesktop, width: 20, @@ -143,12 +188,13 @@ class _DesktopMenuState extends ConsumerState { group: ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: updateSelectedMenuItem, - iconOnly: _width == minimizedWidth, + controller: controllers[1], ), const SizedBox( height: 2, ), DesktopMenuItem( + duration: duration, icon: SvgPicture.asset( ref.watch(notificationsProvider.select( (value) => value.hasUnreadNotifications)) @@ -177,12 +223,13 @@ class _DesktopMenuState extends ConsumerState { group: ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: updateSelectedMenuItem, - iconOnly: _width == minimizedWidth, + controller: controllers[2], ), const SizedBox( height: 2, ), DesktopMenuItem( + duration: duration, icon: SvgPicture.asset( Assets.svg.addressBookDesktop, width: 20, @@ -204,12 +251,13 @@ class _DesktopMenuState extends ConsumerState { group: ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: updateSelectedMenuItem, - iconOnly: _width == minimizedWidth, + controller: controllers[3], ), const SizedBox( height: 2, ), DesktopMenuItem( + duration: duration, icon: SvgPicture.asset( Assets.svg.gear, width: 20, @@ -231,12 +279,13 @@ class _DesktopMenuState extends ConsumerState { group: ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: updateSelectedMenuItem, - iconOnly: _width == minimizedWidth, + controller: controllers[4], ), const SizedBox( height: 2, ), DesktopMenuItem( + duration: duration, icon: SvgPicture.asset( Assets.svg.messageQuestion, width: 20, @@ -258,12 +307,13 @@ class _DesktopMenuState extends ConsumerState { group: ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: updateSelectedMenuItem, - iconOnly: _width == minimizedWidth, + controller: controllers[5], ), const SizedBox( height: 2, ), DesktopMenuItem( + duration: duration, icon: SvgPicture.asset( Assets.svg.aboutDesktop, width: 20, @@ -285,10 +335,12 @@ class _DesktopMenuState extends ConsumerState { group: ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: updateSelectedMenuItem, - iconOnly: _width == minimizedWidth, + controller: controllers[6], ), const Spacer(), DesktopMenuItem( + duration: duration, + labelLength: 123, icon: SvgPicture.asset( Assets.svg.exitDesktop, width: 20, @@ -306,7 +358,7 @@ class _DesktopMenuState extends ConsumerState { // todo: save stuff/ notify before exit? exit(0); }, - iconOnly: _width == minimizedWidth, + controller: controllers[7], ), ], ), diff --git a/lib/pages_desktop_specific/home/desktop_menu_item.dart b/lib/pages_desktop_specific/home/desktop_menu_item.dart index 76d945e2d..e73a4a477 100644 --- a/lib/pages_desktop_specific/home/desktop_menu_item.dart +++ b/lib/pages_desktop_specific/home/desktop_menu_item.dart @@ -2,7 +2,14 @@ import 'package:flutter/material.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; -class DesktopMenuItem extends StatelessWidget { +class DMIController { + VoidCallback? toggle; + void dispose() { + toggle = null; + } +} + +class DesktopMenuItem extends StatefulWidget { const DesktopMenuItem({ Key? key, required this.icon, @@ -10,7 +17,9 @@ class DesktopMenuItem extends StatelessWidget { required this.value, required this.group, required this.onChanged, - required this.iconOnly, + required this.duration, + this.labelLength = 125, + this.controller, }) : super(key: key); final Widget icon; @@ -18,7 +27,67 @@ class DesktopMenuItem extends StatelessWidget { final T value; final T group; final void Function(T) onChanged; - final bool iconOnly; + final Duration duration; + final double labelLength; + final DMIController? controller; + + @override + State> createState() => _DesktopMenuItemState(); +} + +class _DesktopMenuItemState extends State> + with SingleTickerProviderStateMixin { + late final Widget icon; + late final String label; + late final T value; + late final T group; + late final void Function(T) onChanged; + late final Duration duration; + late final double labelLength; + + late final DMIController? controller; + + late final AnimationController animationController; + + bool _iconOnly = false; + + void toggle() { + setState(() { + _iconOnly = !_iconOnly; + }); + if (_iconOnly) { + animationController.reverse(); + } else { + animationController.forward(); + } + } + + @override + void initState() { + icon = widget.icon; + label = widget.label; + value = widget.value; + group = widget.group; + onChanged = widget.onChanged; + duration = widget.duration; + labelLength = widget.labelLength; + controller = widget.controller; + + controller?.toggle = toggle; + animationController = AnimationController( + vsync: this, + duration: duration, + ); + + super.initState(); + } + + @override + void dispose() { + controller?.dispose(); + animationController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { @@ -34,26 +103,42 @@ class DesktopMenuItem extends StatelessWidget { onChanged(value); }, child: Padding( - padding: EdgeInsets.symmetric( + padding: const EdgeInsets.symmetric( vertical: 16, - horizontal: iconOnly ? 0 : 16, ), child: Row( - mainAxisAlignment: - iconOnly ? MainAxisAlignment.center : MainAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, children: [ + AnimatedContainer( + duration: duration, + width: _iconOnly ? 0 : 16, + ), icon, - if (!iconOnly) - const SizedBox( - width: 12, - ), - if (!iconOnly) - Text( - label, - style: value == group - ? STextStyles.desktopMenuItemSelected(context) - : STextStyles.desktopMenuItem(context), + AnimatedOpacity( + duration: duration, + opacity: _iconOnly ? 0 : 1.0, + child: SizeTransition( + sizeFactor: animationController, + axis: Axis.horizontal, + axisAlignment: -1, + child: SizedBox( + width: labelLength, + child: Row( + children: [ + const SizedBox( + width: 12, + ), + Text( + label, + style: value == group + ? STextStyles.desktopMenuItemSelected(context) + : STextStyles.desktopMenuItem(context), + ), + ], + ), + ), ), + ) ], ), ),