mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-25 19:55:52 +00:00
animated main menu
This commit is contained in:
parent
66ff5a437d
commit
221e654dd6
2 changed files with 174 additions and 37 deletions
|
@ -38,6 +38,9 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
||||||
static const expandedWidth = 225.0;
|
static const expandedWidth = 225.0;
|
||||||
static const minimizedWidth = 72.0;
|
static const minimizedWidth = 72.0;
|
||||||
|
|
||||||
|
final Duration duration = const Duration(milliseconds: 250);
|
||||||
|
late final List<DMIController> controllers;
|
||||||
|
|
||||||
double _width = expandedWidth;
|
double _width = expandedWidth;
|
||||||
|
|
||||||
void updateSelectedMenuItem(DesktopMenuItemId idKey) {
|
void updateSelectedMenuItem(DesktopMenuItemId idKey) {
|
||||||
|
@ -49,26 +52,58 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void toggleMinimize() {
|
void toggleMinimize() {
|
||||||
|
final expanded = _width == expandedWidth;
|
||||||
|
|
||||||
|
for (var e in controllers) {
|
||||||
|
e.toggle?.call();
|
||||||
|
}
|
||||||
|
|
||||||
setState(() {
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Material(
|
return Material(
|
||||||
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
color: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||||
child: SizedBox(
|
child: AnimatedContainer(
|
||||||
width: _width,
|
width: _width,
|
||||||
|
duration: duration,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
const SizedBox(
|
||||||
height: _width == expandedWidth ? 22 : 25,
|
height: 25,
|
||||||
),
|
),
|
||||||
SizedBox(
|
AnimatedContainer(
|
||||||
|
duration: duration,
|
||||||
width: _width == expandedWidth ? 70 : 32,
|
width: _width == expandedWidth ? 70 : 32,
|
||||||
height: _width == expandedWidth ? 70 : 32,
|
height: 70, //_width == expandedWidth ? 70 : 32,
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
Assets.svg.stackIcon(context),
|
Assets.svg.stackIcon(context),
|
||||||
),
|
),
|
||||||
|
@ -76,18 +111,26 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
Text(
|
AnimatedOpacity(
|
||||||
_width == expandedWidth ? "Stack Wallet" : "",
|
duration: duration,
|
||||||
|
opacity: _width == expandedWidth ? 1 : 0,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 28,
|
||||||
|
child: Text(
|
||||||
|
"Stack Wallet",
|
||||||
style: STextStyles.desktopH2(context).copyWith(
|
style: STextStyles.desktopH2(context).copyWith(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
height: 23.4 / 18,
|
height: 23.4 / 18,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 60,
|
height: 60,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SizedBox(
|
child: AnimatedContainer(
|
||||||
|
duration: duration,
|
||||||
width: _width == expandedWidth
|
width: _width == expandedWidth
|
||||||
? _width - 32 // 16 padding on either side
|
? _width - 32 // 16 padding on either side
|
||||||
: _width - 16, // 8 padding on either side
|
: _width - 16, // 8 padding on either side
|
||||||
|
@ -95,6 +138,7 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
DesktopMenuItem(
|
DesktopMenuItem(
|
||||||
|
duration: duration,
|
||||||
icon: SvgPicture.asset(
|
icon: SvgPicture.asset(
|
||||||
Assets.svg.walletDesktop,
|
Assets.svg.walletDesktop,
|
||||||
width: 20,
|
width: 20,
|
||||||
|
@ -116,12 +160,13 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
||||||
group:
|
group:
|
||||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||||
onChanged: updateSelectedMenuItem,
|
onChanged: updateSelectedMenuItem,
|
||||||
iconOnly: _width == minimizedWidth,
|
controller: controllers[0],
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 2,
|
height: 2,
|
||||||
),
|
),
|
||||||
DesktopMenuItem(
|
DesktopMenuItem(
|
||||||
|
duration: duration,
|
||||||
icon: SvgPicture.asset(
|
icon: SvgPicture.asset(
|
||||||
Assets.svg.exchangeDesktop,
|
Assets.svg.exchangeDesktop,
|
||||||
width: 20,
|
width: 20,
|
||||||
|
@ -143,12 +188,13 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
||||||
group:
|
group:
|
||||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||||
onChanged: updateSelectedMenuItem,
|
onChanged: updateSelectedMenuItem,
|
||||||
iconOnly: _width == minimizedWidth,
|
controller: controllers[1],
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 2,
|
height: 2,
|
||||||
),
|
),
|
||||||
DesktopMenuItem(
|
DesktopMenuItem(
|
||||||
|
duration: duration,
|
||||||
icon: SvgPicture.asset(
|
icon: SvgPicture.asset(
|
||||||
ref.watch(notificationsProvider.select(
|
ref.watch(notificationsProvider.select(
|
||||||
(value) => value.hasUnreadNotifications))
|
(value) => value.hasUnreadNotifications))
|
||||||
|
@ -177,12 +223,13 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
||||||
group:
|
group:
|
||||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||||
onChanged: updateSelectedMenuItem,
|
onChanged: updateSelectedMenuItem,
|
||||||
iconOnly: _width == minimizedWidth,
|
controller: controllers[2],
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 2,
|
height: 2,
|
||||||
),
|
),
|
||||||
DesktopMenuItem(
|
DesktopMenuItem(
|
||||||
|
duration: duration,
|
||||||
icon: SvgPicture.asset(
|
icon: SvgPicture.asset(
|
||||||
Assets.svg.addressBookDesktop,
|
Assets.svg.addressBookDesktop,
|
||||||
width: 20,
|
width: 20,
|
||||||
|
@ -204,12 +251,13 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
||||||
group:
|
group:
|
||||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||||
onChanged: updateSelectedMenuItem,
|
onChanged: updateSelectedMenuItem,
|
||||||
iconOnly: _width == minimizedWidth,
|
controller: controllers[3],
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 2,
|
height: 2,
|
||||||
),
|
),
|
||||||
DesktopMenuItem(
|
DesktopMenuItem(
|
||||||
|
duration: duration,
|
||||||
icon: SvgPicture.asset(
|
icon: SvgPicture.asset(
|
||||||
Assets.svg.gear,
|
Assets.svg.gear,
|
||||||
width: 20,
|
width: 20,
|
||||||
|
@ -231,12 +279,13 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
||||||
group:
|
group:
|
||||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||||
onChanged: updateSelectedMenuItem,
|
onChanged: updateSelectedMenuItem,
|
||||||
iconOnly: _width == minimizedWidth,
|
controller: controllers[4],
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 2,
|
height: 2,
|
||||||
),
|
),
|
||||||
DesktopMenuItem(
|
DesktopMenuItem(
|
||||||
|
duration: duration,
|
||||||
icon: SvgPicture.asset(
|
icon: SvgPicture.asset(
|
||||||
Assets.svg.messageQuestion,
|
Assets.svg.messageQuestion,
|
||||||
width: 20,
|
width: 20,
|
||||||
|
@ -258,12 +307,13 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
||||||
group:
|
group:
|
||||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||||
onChanged: updateSelectedMenuItem,
|
onChanged: updateSelectedMenuItem,
|
||||||
iconOnly: _width == minimizedWidth,
|
controller: controllers[5],
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 2,
|
height: 2,
|
||||||
),
|
),
|
||||||
DesktopMenuItem(
|
DesktopMenuItem(
|
||||||
|
duration: duration,
|
||||||
icon: SvgPicture.asset(
|
icon: SvgPicture.asset(
|
||||||
Assets.svg.aboutDesktop,
|
Assets.svg.aboutDesktop,
|
||||||
width: 20,
|
width: 20,
|
||||||
|
@ -285,10 +335,12 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
||||||
group:
|
group:
|
||||||
ref.watch(currentDesktopMenuItemProvider.state).state,
|
ref.watch(currentDesktopMenuItemProvider.state).state,
|
||||||
onChanged: updateSelectedMenuItem,
|
onChanged: updateSelectedMenuItem,
|
||||||
iconOnly: _width == minimizedWidth,
|
controller: controllers[6],
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
DesktopMenuItem(
|
DesktopMenuItem(
|
||||||
|
duration: duration,
|
||||||
|
labelLength: 123,
|
||||||
icon: SvgPicture.asset(
|
icon: SvgPicture.asset(
|
||||||
Assets.svg.exitDesktop,
|
Assets.svg.exitDesktop,
|
||||||
width: 20,
|
width: 20,
|
||||||
|
@ -306,7 +358,7 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
|
||||||
// todo: save stuff/ notify before exit?
|
// todo: save stuff/ notify before exit?
|
||||||
exit(0);
|
exit(0);
|
||||||
},
|
},
|
||||||
iconOnly: _width == minimizedWidth,
|
controller: controllers[7],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,7 +2,14 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:stackwallet/utilities/text_styles.dart';
|
import 'package:stackwallet/utilities/text_styles.dart';
|
||||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||||
|
|
||||||
class DesktopMenuItem<T> extends StatelessWidget {
|
class DMIController {
|
||||||
|
VoidCallback? toggle;
|
||||||
|
void dispose() {
|
||||||
|
toggle = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DesktopMenuItem<T> extends StatefulWidget {
|
||||||
const DesktopMenuItem({
|
const DesktopMenuItem({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
|
@ -10,7 +17,9 @@ class DesktopMenuItem<T> extends StatelessWidget {
|
||||||
required this.value,
|
required this.value,
|
||||||
required this.group,
|
required this.group,
|
||||||
required this.onChanged,
|
required this.onChanged,
|
||||||
required this.iconOnly,
|
required this.duration,
|
||||||
|
this.labelLength = 125,
|
||||||
|
this.controller,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final Widget icon;
|
final Widget icon;
|
||||||
|
@ -18,7 +27,67 @@ class DesktopMenuItem<T> extends StatelessWidget {
|
||||||
final T value;
|
final T value;
|
||||||
final T group;
|
final T group;
|
||||||
final void Function(T) onChanged;
|
final void Function(T) onChanged;
|
||||||
final bool iconOnly;
|
final Duration duration;
|
||||||
|
final double labelLength;
|
||||||
|
final DMIController? controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DesktopMenuItem<T>> createState() => _DesktopMenuItemState<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DesktopMenuItemState<T> extends State<DesktopMenuItem<T>>
|
||||||
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -34,20 +103,31 @@ class DesktopMenuItem<T> extends StatelessWidget {
|
||||||
onChanged(value);
|
onChanged(value);
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 16,
|
vertical: 16,
|
||||||
horizontal: iconOnly ? 0 : 16,
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
iconOnly ? MainAxisAlignment.center : MainAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
|
AnimatedContainer(
|
||||||
|
duration: duration,
|
||||||
|
width: _iconOnly ? 0 : 16,
|
||||||
|
),
|
||||||
icon,
|
icon,
|
||||||
if (!iconOnly)
|
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(
|
const SizedBox(
|
||||||
width: 12,
|
width: 12,
|
||||||
),
|
),
|
||||||
if (!iconOnly)
|
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: value == group
|
style: value == group
|
||||||
|
@ -57,6 +137,11 @@ class DesktopMenuItem<T> extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue