extract desktop tor status button into its own widget and animate it when collapsing/expanding the desktop menu

This commit is contained in:
julian 2023-09-08 13:29:42 -06:00
parent dea3be904e
commit 596f917a0f
2 changed files with 213 additions and 134 deletions

View file

@ -8,10 +8,8 @@
*
*/
import 'dart:async';
import 'dart:io';
import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -19,15 +17,12 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_menu_item.dart';
import 'package:stackwallet/pages_desktop_specific/settings/settings_menu.dart';
import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart';
import 'package:stackwallet/services/event_bus/events/global/tor_connection_status_changed_event.dart';
import 'package:stackwallet/services/tor_service.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/desktop/desktop_tor_status_button.dart';
import 'package:stackwallet/widgets/desktop/living_stack_icon.dart';
import '../services/event_bus/global_event_bus.dart';
enum DesktopMenuItemId {
myStack,
exchange,
@ -59,86 +54,10 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
final Duration duration = const Duration(milliseconds: 250);
late final List<DMIController> controllers;
late final DMIController torButtonController;
double _width = expandedWidth;
// final _buyDataLoadingService = BuyDataLoadingService();
/// The global event bus.
late final EventBus eventBus;
/// The subscription to the TorConnectionStatusChangedEvent.
late StreamSubscription<TorConnectionStatusChangedEvent>
_torConnectionStatusSubscription;
/// The current status of the Tor connection.
late TorConnectionStatus _torConnectionStatus;
/// Builds the tor icon based on the current status.
Widget _buildTorIcon(TorConnectionStatus status) {
switch (status) {
case TorConnectionStatus.disconnected:
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.svg.tor,
color: Theme.of(context).extension<StackColors>()!.textSubtitle3,
width: 20,
height: 20,
),
Text(
"\tDisconnected",
style: STextStyles.smallMed12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle3),
)
],
);
case TorConnectionStatus.connecting:
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.svg.tor,
color:
Theme.of(context).extension<StackColors>()!.accentColorYellow,
width: 20,
height: 20,
),
Text(
"\tConnecting",
style: STextStyles.smallMed12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorYellow),
)
],
);
case TorConnectionStatus.connected:
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.svg.tor,
color:
Theme.of(context).extension<StackColors>()!.accentColorGreen,
width: 20,
height: 20,
),
Text(
"\tConnected",
style: STextStyles.smallMed12(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.accentColorGreen),
)
],
);
}
}
void updateSelectedMenuItem(DesktopMenuItemId idKey) {
widget.onSelectionWillChange?.call(idKey);
@ -154,6 +73,8 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
e.toggle?.call();
}
torButtonController.toggle?.call();
setState(() {
_width = expanded ? minimizedWidth : expandedWidth;
});
@ -173,43 +94,7 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
DMIController(),
];
// Initialize the global event bus.
eventBus = GlobalEventBus.instance;
// Initialize the TorConnectionStatus.
_torConnectionStatus = ref.read(pTorService).enabled
? TorConnectionStatus.connected
: TorConnectionStatus.disconnected;
// Subscribe to the TorConnectionStatusChangedEvent.
_torConnectionStatusSubscription =
eventBus.on<TorConnectionStatusChangedEvent>().listen(
(event) async {
// Rebuild the widget.
setState(() {
_torConnectionStatus = event.newStatus;
});
// TODO implement spinner or animations and control from here
// switch (event.newStatus) {
// case TorConnectionStatus.disconnected:
// // if (_spinController.hasLoadedAnimation) {
// // _spinController.stop?.call();
// // }
// break;
// case TorConnectionStatus.connecting:
// // if (_spinController.hasLoadedAnimation) {
// // _spinController.repeat?.call();
// // }
// break;
// case TorConnectionStatus.connected:
// // if (_spinController.hasLoadedAnimation) {
// // _spinController.stop?.call();
// // }
// break;
// }
},
);
torButtonController = DMIController();
super.initState();
}
@ -219,9 +104,7 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
for (var e in controllers) {
e.dispose();
}
// Clean up the subscription to the TorConnectionStatusChangedEvent.
_torConnectionStatusSubscription.cancel();
torButtonController.dispose();
super.dispose();
}
@ -266,17 +149,21 @@ class _DesktopMenuState extends ConsumerState<DesktopMenu> {
const SizedBox(
height: 5,
),
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {
ref.read(currentDesktopMenuItemProvider.state).state =
DesktopMenuItemId.settings;
ref
.watch(selectedSettingsMenuItemStateProvider.state)
.state = 4;
},
child: _buildTorIcon(_torConnectionStatus)),
AnimatedContainer(
duration: duration,
width: _width == expandedWidth
? _width - 32 // 16 padding on either side
: _width - 16, // 8 padding on either side
child: DesktopTorStatusButton(
transitionDuration: duration,
controller: torButtonController,
onPressed: () {
ref.read(currentDesktopMenuItemProvider.state).state =
DesktopMenuItemId.settings;
ref.watch(selectedSettingsMenuItemStateProvider.state).state =
4;
},
),
),
const SizedBox(
height: 40,

View file

@ -0,0 +1,192 @@
import 'dart:async';
import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/cli_commands.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_menu_item.dart';
import 'package:stackwallet/services/event_bus/events/global/tor_connection_status_changed_event.dart';
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
import 'package:stackwallet/services/tor_service.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart';
class DesktopTorStatusButton extends ConsumerStatefulWidget {
const DesktopTorStatusButton({
super.key,
this.onPressed,
required this.transitionDuration,
this.controller,
this.labelLength = 125,
});
final VoidCallback? onPressed;
final Duration transitionDuration;
final DMIController? controller;
final double labelLength;
@override
ConsumerState<DesktopTorStatusButton> createState() =>
_DesktopTorStatusButtonState();
}
class _DesktopTorStatusButtonState extends ConsumerState<DesktopTorStatusButton>
with SingleTickerProviderStateMixin {
late final AnimationController animationController;
late final DMIController? controller;
late final double labelLength;
/// The global event bus.
late final EventBus eventBus;
/// The subscription to the TorConnectionStatusChangedEvent.
late final StreamSubscription<TorConnectionStatusChangedEvent>
_torConnectionStatusSubscription;
/// The current status of the Tor connection.
late TorConnectionStatus _torConnectionStatus;
Color _color(TorConnectionStatus status) {
switch (status) {
case TorConnectionStatus.disconnected:
return Theme.of(context).extension<StackColors>()!.textSubtitle3;
case TorConnectionStatus.connecting:
return Theme.of(context).extension<StackColors>()!.accentColorYellow;
case TorConnectionStatus.connected:
return Theme.of(context).extension<StackColors>()!.accentColorGreen;
}
}
bool _iconOnly = false;
void toggle() {
setState(() {
_iconOnly = !_iconOnly;
});
if (_iconOnly) {
animationController.reverse();
} else {
animationController.forward();
}
}
@override
void initState() {
labelLength = widget.labelLength;
controller = widget.controller;
// Initialize the global event bus.
eventBus = GlobalEventBus.instance;
// Initialize the TorConnectionStatus.
_torConnectionStatus = ref.read(pTorService).enabled
? TorConnectionStatus.connected
: TorConnectionStatus.disconnected;
// Subscribe to the TorConnectionStatusChangedEvent.
_torConnectionStatusSubscription =
eventBus.on<TorConnectionStatusChangedEvent>().listen(
(event) async {
// Rebuild the widget.
setState(() {
_torConnectionStatus = event.newStatus;
});
// TODO implement spinner or animations and control from here
// switch (event.newStatus) {
// case TorConnectionStatus.disconnected:
// // if (_spinController.hasLoadedAnimation) {
// // _spinController.stop?.call();
// // }
// break;
// case TorConnectionStatus.connecting:
// // if (_spinController.hasLoadedAnimation) {
// // _spinController.repeat?.call();
// // }
// break;
// case TorConnectionStatus.connected:
// // if (_spinController.hasLoadedAnimation) {
// // _spinController.stop?.call();
// // }
// break;
// }
},
);
controller?.toggle = toggle;
animationController = AnimationController(
vsync: this,
duration: widget.transitionDuration,
)..forward();
super.initState();
}
@override
void dispose() {
// Clean up the subscription to the TorConnectionStatusChangedEvent.
_torConnectionStatusSubscription.cancel();
controller?.dispose();
animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return TextButton(
style: Theme.of(context)
.extension<StackColors>()!
.getDesktopMenuButtonStyle(context),
onPressed: widget.onPressed,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 16,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedContainer(
duration: widget.transitionDuration,
width: _iconOnly ? 0 : 16,
),
SvgPicture.asset(
Assets.svg.tor,
color: _color(_torConnectionStatus),
width: 20,
height: 20,
),
AnimatedOpacity(
duration: widget.transitionDuration,
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(
_torConnectionStatus.name.capitalize(),
style: STextStyles.smallMed12(context).copyWith(
color: _color(_torConnectionStatus),
),
),
],
),
),
),
),
],
),
),
);
}
}