From 76e0616ade79a9ef56012e80a4f4e0ede7eb825d Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Mon, 28 Nov 2022 10:40:45 -0700 Subject: [PATCH 01/14] view backup keys text changed for wallet deletion --- .../wallet_view/sub_widgets/delete_wallet_keys_popup.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/delete_wallet_keys_popup.dart b/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/delete_wallet_keys_popup.dart index 70f4a3e13..a2d58465b 100644 --- a/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/delete_wallet_keys_popup.dart +++ b/lib/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/delete_wallet_keys_popup.dart @@ -95,7 +95,8 @@ class _DeleteWalletKeysPopup extends ConsumerState { horizontal: 32, ), child: Text( - "Please write down your recovery phrase in the correct order and save it to keep your funds secure. You will also be asked to verify the words on the next screen.", + "Please write down your recovery phrase in the correct order and " + "save it to keep your funds secure. You will be shown your recovery phrase on the next screen.", style: STextStyles.desktopTextExtraExtraSmall(context), textAlign: TextAlign.center, ), From 8960bb576464ecd5ac415cfa3494a53077d10677 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 28 Nov 2022 12:46:35 -0600 Subject: [PATCH 02/14] linux small screen width check --- lib/main.dart | 8 +++++++- lib/utilities/util.dart | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 2086d351e..a49bcab82 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -76,11 +76,17 @@ void main() async { Util.libraryPath = await getLibraryDirectory(); } + Screen? screen; + if (Platform.isLinux || Util.isDesktop) { + screen = await getCurrentScreen(); + Util.screenWidth = screen?.frame.width; + } + if (Util.isDesktop) { setWindowTitle('Stack Wallet'); setWindowMinSize(const Size(1220, 100)); setWindowMaxSize(Size.infinite); - final screen = await getCurrentScreen(); + final screenHeight = screen?.frame.height; if (screenHeight != null) { // starting to height be 3/4 screen height or 900, whichever is smaller diff --git a/lib/utilities/util.dart b/lib/utilities/util.dart index 5963bfee9..2940b6d40 100644 --- a/lib/utilities/util.dart +++ b/lib/utilities/util.dart @@ -1,14 +1,24 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:path_provider/path_provider.dart'; abstract class Util { static Directory? libraryPath; + static double? screenWidth; + static bool get isDesktop { - if(Platform.isIOS && libraryPath != null && !libraryPath!.path.contains("/var/mobile/")){ + // special check for running on linux based phones + if (Platform.isLinux && screenWidth != null && screenWidth! < 800) { + return false; + } + + // special check for running under ipad mode in macos + if (Platform.isIOS && + libraryPath != null && + !libraryPath!.path.contains("/var/mobile/")) { return true; } + return Platform.isLinux || Platform.isMacOS || Platform.isWindows; } From 66ff5a437dd8a00bb55687aacbfc4fc5139f91e5 Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Mon, 28 Nov 2022 11:51:13 -0700 Subject: [PATCH 03/14] reverted mobile restore calendar height --- .../restore_options_view/restore_options_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart index ac84964ca..1ce5d713a 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart @@ -155,7 +155,7 @@ class _RestoreOptionsViewState extends ConsumerState { final date = await showRoundedDatePicker( context: context, initialDate: DateTime.now(), - height: height / 3.2, + height: height * 0.5, theme: ThemeData( primarySwatch: Util.createMaterialColor(fetchedColor), ), From 221e654dd669b24e5e4426131947098b47774b21 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 28 Nov 2022 13:50:55 -0600 Subject: [PATCH 04/14] 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), + ), + ], + ), + ), ), + ) ], ), ), From 6bbabcd729aa50d74e9351a3bb2948d740437b3a Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Mon, 28 Nov 2022 13:06:02 -0700 Subject: [PATCH 05/14] MyStackView tab after a restore backup --- .../sub_views/stack_restore_progress_view.dart | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart index c7f53378d..92e7742e1 100644 --- a/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart +++ b/lib/pages/settings_views/global_settings_view/stack_backup_views/sub_views/stack_restore_progress_view.dart @@ -25,6 +25,9 @@ import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart'; import 'package:stackwallet/widgets/loading_indicator.dart'; import 'package:stackwallet/widgets/rounded_container.dart'; +import '../../../../../pages_desktop_specific/home/desktop_home_view.dart'; +import '../../../../../pages_desktop_specific/home/desktop_menu.dart'; +import '../../../../../providers/desktop/current_desktop_menu_item.dart'; import '../../../../../widgets/desktop/primary_button.dart'; class StackRestoreProgressView extends ConsumerStatefulWidget { @@ -685,7 +688,19 @@ class _StackRestoreProgressViewState enabled: true, label: "Done", onPressed: () async { - Navigator.of(context).pop(); + DesktopMenuItemId keyID = + DesktopMenuItemId.myStack; + + ref + .read(currentDesktopMenuItemProvider + .state) + .state = keyID; + + Navigator.of(context, rootNavigator: true) + .popUntil( + ModalRoute.withName( + DesktopHomeView.routeName), + ); }, ) : SecondaryButton( From c3921b01de78978a13874b8c3457a4837bc8846a Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 28 Nov 2022 14:25:49 -0600 Subject: [PATCH 06/14] animated desktop stack icon --- .../home/desktop_menu.dart | 6 +-- .../home/desktop_menu_item.dart | 2 +- lib/widgets/desktop/living_stack_icon.dart | 54 +++++++++++++++++++ 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 lib/widgets/desktop/living_stack_icon.dart diff --git a/lib/pages_desktop_specific/home/desktop_menu.dart b/lib/pages_desktop_specific/home/desktop_menu.dart index 6ae4b91a1..8af307e7b 100644 --- a/lib/pages_desktop_specific/home/desktop_menu.dart +++ b/lib/pages_desktop_specific/home/desktop_menu.dart @@ -9,6 +9,7 @@ import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/widgets/desktop/living_stack_icon.dart'; enum DesktopMenuItemId { myStack, @@ -103,9 +104,8 @@ class _DesktopMenuState extends ConsumerState { AnimatedContainer( duration: duration, width: _width == expandedWidth ? 70 : 32, - height: 70, //_width == expandedWidth ? 70 : 32, - child: SvgPicture.asset( - Assets.svg.stackIcon(context), + child: LivingStackIcon( + onPressed: toggleMinimize, ), ), const SizedBox( diff --git a/lib/pages_desktop_specific/home/desktop_menu_item.dart b/lib/pages_desktop_specific/home/desktop_menu_item.dart index e73a4a477..1fb39213b 100644 --- a/lib/pages_desktop_specific/home/desktop_menu_item.dart +++ b/lib/pages_desktop_specific/home/desktop_menu_item.dart @@ -77,7 +77,7 @@ class _DesktopMenuItemState extends State> animationController = AnimationController( vsync: this, duration: duration, - ); + )..forward(); super.initState(); } diff --git a/lib/widgets/desktop/living_stack_icon.dart b/lib/widgets/desktop/living_stack_icon.dart new file mode 100644 index 000000000..7afc8f8d2 --- /dev/null +++ b/lib/widgets/desktop/living_stack_icon.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/utilities/assets.dart'; + +class LivingStackIcon extends StatefulWidget { + const LivingStackIcon({Key? key, this.onPressed,}) : super(key: key); + + final VoidCallback? onPressed; + + @override + State createState() => _LivingStackIconState(); +} + +class _LivingStackIconState extends State { + bool _hovering = false; + + late final VoidCallback? onPressed; + + @override + void initState() { + onPressed = widget.onPressed; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 76, + child: MouseRegion( + cursor: SystemMouseCursors.click, + onEnter: (_) { + setState(() { + _hovering = true; + }); + }, + onExit: (_) { + setState(() { + _hovering = false; + }); + }, + child: GestureDetector( + onTap: () => onPressed?.call(), + child: AnimatedScale( + duration: const Duration(milliseconds: 200), + scale: _hovering ? 1.2 : 1, + child: SvgPicture.asset( + Assets.svg.stackIcon(context), + ), + ), + ), + ), + ); + } +} From b4cbf078c7a1a6105fc76bd92715b97447802d9e Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Mon, 28 Nov 2022 13:50:55 -0700 Subject: [PATCH 07/14] flutter_rounded_date_picker files from picker lib --- .../flutter_rounded_date_picker_dialog.dart | 332 ++++++++++++++++++ .../flutter_rounded_date_picker_widget.dart | 216 ++++++++++++ 2 files changed, 548 insertions(+) create mode 100644 lib/widgets/rounded_date_picker/flutter_rounded_date_picker_dialog.dart create mode 100644 lib/widgets/rounded_date_picker/flutter_rounded_date_picker_widget.dart diff --git a/lib/widgets/rounded_date_picker/flutter_rounded_date_picker_dialog.dart b/lib/widgets/rounded_date_picker/flutter_rounded_date_picker_dialog.dart new file mode 100644 index 000000000..6d7f775cd --- /dev/null +++ b/lib/widgets/rounded_date_picker/flutter_rounded_date_picker_dialog.dart @@ -0,0 +1,332 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/semantics.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_rounded_date_picker/flutter_rounded_date_picker.dart'; +import 'package:flutter_rounded_date_picker/src/flutter_rounded_button_action.dart'; +import 'package:flutter_rounded_date_picker/src/material_rounded_date_picker_style.dart'; +import 'package:flutter_rounded_date_picker/src/material_rounded_year_picker_style.dart'; +import 'package:flutter_rounded_date_picker/src/widgets/flutter_rounded_date_picker_header.dart'; +import 'package:flutter_rounded_date_picker/src/widgets/flutter_rounded_day_picker.dart'; +import 'package:flutter_rounded_date_picker/src/widgets/flutter_rounded_month_picker.dart'; +import 'package:flutter_rounded_date_picker/src/widgets/flutter_rounded_year_picker.dart'; +import 'package:stackwallet/utilities/util.dart'; + +/// +/// This file uses code taken from https://github.com/benznest/flutter_rounded_date_picker +/// + +class FlutterRoundedDatePickerDialog extends StatefulWidget { + const FlutterRoundedDatePickerDialog( + {Key? key, + this.height, + required this.initialDate, + required this.firstDate, + required this.lastDate, + this.selectableDayPredicate, + required this.initialDatePickerMode, + required this.era, + this.locale, + required this.borderRadius, + this.imageHeader, + this.description = "", + this.fontFamily, + this.textNegativeButton, + this.textPositiveButton, + this.textActionButton, + this.onTapActionButton, + this.styleDatePicker, + this.styleYearPicker, + this.customWeekDays, + this.builderDay, + this.listDateDisabled, + this.onTapDay, + this.onMonthChange}) + : super(key: key); + + final DateTime initialDate; + final DateTime firstDate; + final DateTime lastDate; + final SelectableDayPredicate? selectableDayPredicate; + final DatePickerMode initialDatePickerMode; + + /// double height. + final double? height; + + /// Custom era year. + final EraMode era; + final Locale? locale; + + /// Border + final double borderRadius; + + /// Header; + final ImageProvider? imageHeader; + final String description; + + /// Font + final String? fontFamily; + + /// Button + final String? textNegativeButton; + final String? textPositiveButton; + final String? textActionButton; + + final VoidCallback? onTapActionButton; + + /// Style + final MaterialRoundedDatePickerStyle? styleDatePicker; + final MaterialRoundedYearPickerStyle? styleYearPicker; + + /// Custom Weekday + final List? customWeekDays; + + final BuilderDayOfDatePicker? builderDay; + + final List? listDateDisabled; + final OnTapDay? onTapDay; + + final Function? onMonthChange; + + @override + _FlutterRoundedDatePickerDialogState createState() => + _FlutterRoundedDatePickerDialogState(); +} + +class _FlutterRoundedDatePickerDialogState + extends State { + @override + void initState() { + super.initState(); + _selectedDate = widget.initialDate; + _mode = widget.initialDatePickerMode; + } + + bool _announcedInitialDate = false; + + late MaterialLocalizations localizations; + late TextDirection textDirection; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + localizations = MaterialLocalizations.of(context); + textDirection = Directionality.of(context); + if (!_announcedInitialDate) { + _announcedInitialDate = true; + SemanticsService.announce( + localizations.formatFullDate(_selectedDate), + textDirection, + ); + } + } + + late DateTime _selectedDate; + late DatePickerMode _mode; + final GlobalKey _pickerKey = GlobalKey(); + + void _vibrate() { + switch (Theme.of(context).platform) { + case TargetPlatform.android: + case TargetPlatform.fuchsia: + HapticFeedback.vibrate(); + break; + case TargetPlatform.iOS: + default: + break; + } + } + + void _handleModeChanged(DatePickerMode mode) { + _vibrate(); + setState(() { + _mode = mode; + if (_mode == DatePickerMode.day) { + SemanticsService.announce( + localizations.formatMonthYear(_selectedDate), + textDirection, + ); + } else { + SemanticsService.announce( + localizations.formatYear(_selectedDate), + textDirection, + ); + } + }); + } + + Future _handleYearChanged(DateTime value) async { + if (value.isBefore(widget.firstDate)) { + value = widget.firstDate; + } else if (value.isAfter(widget.lastDate)) { + value = widget.lastDate; + } + if (value == _selectedDate) return; + + if (widget.onMonthChange != null) await widget.onMonthChange!(value); + + _vibrate(); + setState(() { + _mode = DatePickerMode.day; + _selectedDate = value; + }); + } + + void _handleDayChanged(DateTime value) { + _vibrate(); + setState(() { + _selectedDate = value; + }); + } + + void _handleCancel() { + Navigator.of(context).pop(); + } + + void _handleOk() { + Navigator.of(context).pop(_selectedDate); + } + + Widget _buildPicker() { + switch (_mode) { + case DatePickerMode.year: + return FlutterRoundedYearPicker( + key: _pickerKey, + selectedDate: _selectedDate, + onChanged: (DateTime date) async => await _handleYearChanged(date), + firstDate: widget.firstDate, + lastDate: widget.lastDate, + era: widget.era, + fontFamily: widget.fontFamily, + style: widget.styleYearPicker, + ); + case DatePickerMode.day: + default: + return FlutterRoundedMonthPicker( + key: _pickerKey, + selectedDate: _selectedDate, + onChanged: _handleDayChanged, + firstDate: widget.firstDate, + lastDate: widget.lastDate, + era: widget.era, + locale: widget.locale, + selectableDayPredicate: widget.selectableDayPredicate, + fontFamily: widget.fontFamily, + style: widget.styleDatePicker, + borderRadius: widget.borderRadius, + customWeekDays: widget.customWeekDays, + builderDay: widget.builderDay, + listDateDisabled: widget.listDateDisabled, + onTapDay: widget.onTapDay, + onMonthChange: widget.onMonthChange); + } + } + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + final Widget picker = _buildPicker(); + final isDesktop = Util.isDesktop; + + final Widget actions = FlutterRoundedButtonAction( + textButtonNegative: widget.textNegativeButton, + textButtonPositive: widget.textPositiveButton, + onTapButtonNegative: _handleCancel, + onTapButtonPositive: _handleOk, + textActionButton: widget.textActionButton, + onTapButtonAction: widget.onTapActionButton, + localizations: localizations, + textStyleButtonNegative: widget.styleDatePicker?.textStyleButtonNegative, + textStyleButtonPositive: widget.styleDatePicker?.textStyleButtonPositive, + textStyleButtonAction: widget.styleDatePicker?.textStyleButtonAction, + borderRadius: widget.borderRadius, + paddingActionBar: widget.styleDatePicker?.paddingActionBar, + background: widget.styleDatePicker?.backgroundActionBar, + ); + + Color backgroundPicker = theme.dialogBackgroundColor; + if (_mode == DatePickerMode.day) { + backgroundPicker = widget.styleDatePicker?.backgroundPicker ?? + theme.dialogBackgroundColor; + } else { + backgroundPicker = widget.styleYearPicker?.backgroundPicker ?? + theme.dialogBackgroundColor; + } + + final Dialog dialog = Dialog( + child: OrientationBuilder( + builder: (BuildContext context, Orientation orientation) { + final Widget header = FlutterRoundedDatePickerHeader( + selectedDate: _selectedDate, + mode: _mode, + onModeChanged: _handleModeChanged, + orientation: orientation, + era: widget.era, + borderRadius: widget.borderRadius, + imageHeader: widget.imageHeader, + description: widget.description, + fontFamily: widget.fontFamily, + style: widget.styleDatePicker); + switch (orientation) { + case Orientation.landscape: + return Container( + height: isDesktop ? 600 : null, + width: isDesktop ? 700 : null, + decoration: BoxDecoration( + color: backgroundPicker, + borderRadius: BorderRadius.circular(widget.borderRadius), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Flexible(flex: 1, child: header), + Flexible( + flex: 2, // have the picker take up 2/3 of the dialog width + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + SizedBox( + height: isDesktop ? 530 : null, + width: isDesktop ? 700 : null, + child: picker), + actions, + ], + ), + ), + ], + ), + ); + case Orientation.portrait: + default: + return Container( + decoration: BoxDecoration( + color: backgroundPicker, + borderRadius: BorderRadius.circular(widget.borderRadius), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + header, + if (widget.height == null) + Flexible(child: picker) + else + SizedBox( + height: widget.height, + child: picker, + ), + actions, + ], + ), + ); + } + }), + ); + + return Theme( + data: theme.copyWith(dialogBackgroundColor: Colors.transparent), + child: dialog, + ); + } +} diff --git a/lib/widgets/rounded_date_picker/flutter_rounded_date_picker_widget.dart b/lib/widgets/rounded_date_picker/flutter_rounded_date_picker_widget.dart new file mode 100644 index 000000000..5f576f480 --- /dev/null +++ b/lib/widgets/rounded_date_picker/flutter_rounded_date_picker_widget.dart @@ -0,0 +1,216 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +// import 'package:flutter_rounded_date_picker/src/dialogs/flutter_rounded_date_picker_dialog.dart'; +import 'package:flutter_rounded_date_picker/src/era_mode.dart'; +import 'package:flutter_rounded_date_picker/src/material_rounded_date_picker_style.dart'; +import 'package:flutter_rounded_date_picker/src/material_rounded_year_picker_style.dart'; +import 'package:flutter_rounded_date_picker/src/widgets/flutter_rounded_day_picker.dart'; +import 'package:stackwallet/widgets/rounded_date_picker/flutter_rounded_date_picker_dialog.dart'; + +/// +/// This file uses code taken from https://github.com/benznest/flutter_rounded_date_picker +/// + +// Examples can assume: +// BuildContext context; + +/// Initial display mode of the date picker dialog. +/// +/// Date picker UI mode for either showing a list of available years or a +/// monthly calendar initially in the dialog shown by calling [showDatePicker]. +/// + +// Shows the selected date in large font and toggles between year and day mode + +/// Signature for predicating dates for enabled date selections. +/// +/// See [showDatePicker]. +typedef SelectableDayPredicate = bool Function(DateTime day); + +/// Shows a dialog containing a material design date picker. +/// +/// The returned [Future] resolves to the date selected by the user when the +/// user closes the dialog. If the user cancels the dialog, null is returned. +/// +/// An optional [selectableDayPredicate] function can be passed in to customize +/// the days to enable for selection. If provided, only the days that +/// [selectableDayPredicate] returned true for will be selectable. +/// +/// An optional [initialDatePickerMode] argument can be used to display the +/// date picker initially in the year or month+day picker mode. It defaults +/// to month+day, and must not be null. +/// +/// An optional [locale] argument can be used to set the locale for the date +/// picker. It defaults to the ambient locale provided by [Localizations]. +/// +/// An optional [textDirection] argument can be used to set the text direction +/// (RTL or LTR) for the date picker. It defaults to the ambient text direction +/// provided by [Directionality]. If both [locale] and [textDirection] are not +/// null, [textDirection] overrides the direction chosen for the [locale]. +/// +/// The [context] argument is passed to [showDialog], the documentation for +/// which discusses how it is used. +/// +/// The [builder] parameter can be used to wrap the dialog widget +/// to add inherited widgets like [Theme]. +/// +/// {@tool sample} +/// Show a date picker with the dark theme. +/// +/// ```dart +/// Future selectedDate = showDatePicker( +/// context: context, +/// initialDate: DateTime.now(), +/// firstDate: DateTime(2018), +/// lastDate: DateTime(2030), +/// builder: (BuildContext context, Widget child) { +/// return Theme( +/// data: ThemeData.dark(), +/// child: child, +/// ); +/// }, +/// ); +/// ``` +/// {@end-tool} +/// +/// The [context], [initialDate], [firstDate], and [lastDate] parameters must +/// not be null. +/// +/// See also: +/// +/// * [showTimePicker], which shows a dialog that contains a material design +/// time picker. +/// * [DayPicker], which displays the days of a given month and allows +/// choosing a day. +/// * [MonthPicker], which displays a scrollable list of months to allow +/// picking a month. +/// * [YearPicker], which displays a scrollable list of years to allow picking +/// a year. +/// + +Future showRoundedDatePicker( + {required BuildContext context, + double? height, + DateTime? initialDate, + DateTime? firstDate, + DateTime? lastDate, + SelectableDayPredicate? selectableDayPredicate, + DatePickerMode initialDatePickerMode = DatePickerMode.day, + Locale? locale, + TextDirection? textDirection, + ThemeData? theme, + double borderRadius = 16, + EraMode era = EraMode.CHRIST_YEAR, + ImageProvider? imageHeader, + String description = "", + String? fontFamily, + bool barrierDismissible = false, + Color background = Colors.transparent, + String? textNegativeButton, + String? textPositiveButton, + String? textActionButton, + VoidCallback? onTapActionButton, + MaterialRoundedDatePickerStyle? styleDatePicker, + MaterialRoundedYearPickerStyle? styleYearPicker, + List? customWeekDays, + BuilderDayOfDatePicker? builderDay, + List? listDateDisabled, + OnTapDay? onTapDay, + Function? onMonthChange}) async { + initialDate ??= DateTime.now(); + firstDate ??= DateTime(initialDate.year - 1); + lastDate ??= DateTime(initialDate.year + 1); + theme ??= ThemeData(); + + assert( + !initialDate.isBefore(firstDate), + 'initialDate must be on or after firstDate', + ); + assert( + !initialDate.isAfter(lastDate), + 'initialDate must be on or before lastDate', + ); + assert( + !firstDate.isAfter(lastDate), + 'lastDate must be on or after firstDate', + ); + assert( + selectableDayPredicate == null || selectableDayPredicate(initialDate), + 'Provided initialDate must satisfy provided selectableDayPredicate', + ); + assert( + (onTapActionButton != null && textActionButton != null) || + onTapActionButton == null, + "If you provide onLeftBtn, you must provide leftBtn", + ); + assert(debugCheckHasMaterialLocalizations(context)); + + Widget child = GestureDetector( + onTap: () { + if (!barrierDismissible) { + Navigator.pop(context); + } + }, + child: Container( + color: background, + child: GestureDetector( + onTap: () { + // + }, + child: FlutterRoundedDatePickerDialog( + height: height, + initialDate: initialDate, + firstDate: firstDate, + lastDate: lastDate, + selectableDayPredicate: selectableDayPredicate, + initialDatePickerMode: initialDatePickerMode, + era: era, + locale: locale, + borderRadius: borderRadius, + imageHeader: imageHeader, + description: description, + fontFamily: fontFamily, + textNegativeButton: textNegativeButton, + textPositiveButton: textPositiveButton, + textActionButton: textActionButton, + onTapActionButton: onTapActionButton, + styleDatePicker: styleDatePicker, + styleYearPicker: styleYearPicker, + customWeekDays: customWeekDays, + builderDay: builderDay, + listDateDisabled: listDateDisabled, + onTapDay: onTapDay, + onMonthChange: onMonthChange, + ), + ), + ), + ); + + if (textDirection != null) { + child = Directionality( + textDirection: textDirection, + child: child, + ); + } + + if (locale != null) { + child = Localizations.override( + context: context, + locale: locale, + child: child, + ); + } + + return await showDialog( + context: context, + barrierDismissible: barrierDismissible, + builder: (_) => Theme(data: theme!, child: child), + ); +} From 3fef1ee67461e4ca3fd9078f3908679f9522953d Mon Sep 17 00:00:00 2001 From: ryleedavis Date: Mon, 28 Nov 2022 14:10:44 -0700 Subject: [PATCH 08/14] desktop restore calendar resize --- .../restore_options_view/restore_options_view.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart index 1ce5d713a..a66af63fc 100644 --- a/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart +++ b/lib/pages/add_wallet_views/restore_wallet_view/restore_options_view/restore_options_view.dart @@ -23,6 +23,8 @@ import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart'; import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; +import 'package:stackwallet/widgets/rounded_date_picker/flutter_rounded_date_picker_widget.dart' + as datePicker; import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:tuple/tuple.dart'; @@ -152,7 +154,7 @@ class _RestoreOptionsViewState extends ConsumerState { await Future.delayed(const Duration(milliseconds: 125)); } - final date = await showRoundedDatePicker( + final date = await datePicker.showRoundedDatePicker( context: context, initialDate: DateTime.now(), height: height * 0.5, From d7cd5cb8a9c556a29f6ef8e273a3f72503b523e3 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 28 Nov 2022 15:21:18 -0600 Subject: [PATCH 09/14] desktop wallets table hover effects --- .../my_stack_view/coin_wallets_table.dart | 87 ++++++++++++-- lib/widgets/table_view/table_view_row.dart | 106 ++++++++++++++---- .../wallet_info_row/wallet_info_row.dart | 95 ++++++++-------- 3 files changed, 209 insertions(+), 79 deletions(-) diff --git a/lib/pages_desktop_specific/home/my_stack_view/coin_wallets_table.dart b/lib/pages_desktop_specific/home/my_stack_view/coin_wallets_table.dart index b16a9bc58..4ed8765ae 100644 --- a/lib/pages_desktop_specific/home/my_stack_view/coin_wallets_table.dart +++ b/lib/pages_desktop_specific/home/my_stack_view/coin_wallets_table.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/widgets/rounded_container.dart'; import 'package:stackwallet/widgets/wallet_info_row/wallet_info_row.dart'; class CoinWalletsTable extends ConsumerWidget { @@ -24,8 +25,10 @@ class CoinWalletsTable extends ConsumerWidget { ), child: Padding( padding: const EdgeInsets.symmetric( - horizontal: 20, - vertical: 16, + // horizontal: 20, + // vertical: 16, + horizontal: 6, + vertical: 6, ), child: Column( children: [ @@ -36,14 +39,26 @@ class CoinWalletsTable extends ConsumerWidget { const SizedBox( height: 32, ), - WalletInfoRow( - walletId: walletIds[i], - onPressed: () async { - await Navigator.of(context).pushNamed( - DesktopWalletView.routeName, - arguments: walletIds[i], - ); - }, + Stack( + children: [ + WalletInfoRow( + padding: const EdgeInsets.symmetric( + horizontal: 14, + vertical: 10, + ), + walletId: walletIds[i], + ), + Positioned.fill( + child: WalletRowHoverOverlay( + onPressed: () async { + await Navigator.of(context).pushNamed( + DesktopWalletView.routeName, + arguments: walletIds[i], + ); + }, + ), + ), + ], ), ], ), @@ -53,3 +68,55 @@ class CoinWalletsTable extends ConsumerWidget { ); } } + +class WalletRowHoverOverlay extends StatefulWidget { + const WalletRowHoverOverlay({ + Key? key, + required this.onPressed, + }) : super(key: key); + + final VoidCallback onPressed; + + @override + State createState() => _WalletRowHoverOverlayState(); +} + +class _WalletRowHoverOverlayState extends State { + late final VoidCallback onPressed; + + bool _hovering = false; + + @override + void initState() { + onPressed = widget.onPressed; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return MouseRegion( + cursor: SystemMouseCursors.click, + onEnter: (_) { + setState(() { + _hovering = true; + }); + }, + onExit: (_) { + setState(() { + _hovering = false; + }); + }, + child: GestureDetector( + onTap: onPressed, + child: AnimatedOpacity( + duration: const Duration(milliseconds: 100), + opacity: _hovering ? 0.1 : 0, + child: RoundedContainer( + color: + Theme.of(context).extension()!.buttonBackSecondary, + ), + ), + ), + ); + } +} diff --git a/lib/widgets/table_view/table_view_row.dart b/lib/widgets/table_view/table_view_row.dart index e95eb68bd..9c3175efe 100644 --- a/lib/widgets/table_view/table_view_row.dart +++ b/lib/widgets/table_view/table_view_row.dart @@ -3,7 +3,7 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/widgets/expandable.dart'; import 'package:stackwallet/widgets/table_view/table_view_cell.dart'; -class TableViewRow extends StatelessWidget { +class TableViewRow extends StatefulWidget { const TableViewRow({ Key? key, required this.cells, @@ -17,40 +17,66 @@ class TableViewRow extends StatelessWidget { final List cells; final Widget? expandingChild; - final Decoration? decoration; + final BoxDecoration? decoration; final void Function(ExpandableState)? onExpandChanged; final EdgeInsetsGeometry padding; final double spacing; final CrossAxisAlignment crossAxisAlignment; + @override + State createState() => _TableViewRowState(); +} + +class _TableViewRowState extends State { + late final List cells; + late final Widget? expandingChild; + late final BoxDecoration? decoration; + late final void Function(ExpandableState)? onExpandChanged; + late final EdgeInsetsGeometry padding; + late final double spacing; + late final CrossAxisAlignment crossAxisAlignment; + + bool _hovering = false; + + @override + void initState() { + cells = widget.cells; + expandingChild = widget.expandingChild; + decoration = widget.decoration; + onExpandChanged = widget.onExpandChanged; + padding = widget.padding; + spacing = widget.spacing; + crossAxisAlignment = widget.crossAxisAlignment; + super.initState(); + } + @override Widget build(BuildContext context) { return Container( - decoration: decoration, + decoration: !_hovering + ? decoration + : decoration?.copyWith( + boxShadow: [ + Theme.of(context).extension()!.standardBoxShadow, + Theme.of(context).extension()!.standardBoxShadow, + ], + ), child: expandingChild == null - ? Padding( - padding: padding, - child: Row( - crossAxisAlignment: crossAxisAlignment, - children: [ - for (int i = 0; i < cells.length; i++) ...[ - if (i != 0 && i != cells.length) - SizedBox( - width: spacing, - ), - Expanded( - flex: cells[i].flex, - child: cells[i], - ), - ], - ], - ), - ) - : Expandable( - onExpandChanged: onExpandChanged, - header: Padding( + ? MouseRegion( + onEnter: (_) { + setState(() { + _hovering = true; + }); + }, + onExit: (_) { + setState(() { + _hovering = false; + }); + }, + child: Padding( padding: padding, child: Row( + crossAxisAlignment: crossAxisAlignment, children: [ for (int i = 0; i < cells.length; i++) ...[ if (i != 0 && i != cells.length) @@ -65,6 +91,38 @@ class TableViewRow extends StatelessWidget { ], ), ), + ) + : Expandable( + onExpandChanged: onExpandChanged, + header: MouseRegion( + onEnter: (_) { + setState(() { + _hovering = true; + }); + }, + onExit: (_) { + setState(() { + _hovering = false; + }); + }, + child: Padding( + padding: padding, + child: Row( + children: [ + for (int i = 0; i < cells.length; i++) ...[ + if (i != 0 && i != cells.length) + SizedBox( + width: spacing, + ), + Expanded( + flex: cells[i].flex, + child: cells[i], + ), + ], + ], + ), + ), + ), body: Column( children: [ Container( diff --git a/lib/widgets/wallet_info_row/wallet_info_row.dart b/lib/widgets/wallet_info_row/wallet_info_row.dart index fe006a67b..5bb51e2e6 100644 --- a/lib/widgets/wallet_info_row/wallet_info_row.dart +++ b/lib/widgets/wallet_info_row/wallet_info_row.dart @@ -14,10 +14,12 @@ class WalletInfoRow extends ConsumerWidget { Key? key, required this.walletId, this.onPressed, + this.padding = const EdgeInsets.all(0), }) : super(key: key); final String walletId; final VoidCallback? onPressed; + final EdgeInsets padding; @override Widget build(BuildContext context, WidgetRef ref) { @@ -30,53 +32,56 @@ class WalletInfoRow extends ConsumerWidget { cursor: SystemMouseCursors.click, child: GestureDetector( onTap: onPressed, - child: Container( - color: Colors.transparent, - child: Row( - children: [ - Expanded( - flex: 4, - child: Row( - children: [ - WalletInfoCoinIcon(coin: manager.coin), - const SizedBox( - width: 12, - ), - Text( - manager.walletName, - style: - STextStyles.desktopTextExtraSmall(context).copyWith( + child: Padding( + padding: padding, + child: Container( + color: Colors.transparent, + child: Row( + children: [ + Expanded( + flex: 4, + child: Row( + children: [ + WalletInfoCoinIcon(coin: manager.coin), + const SizedBox( + width: 12, + ), + Text( + manager.walletName, + style: STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ), + ), + ], + ), + ), + Expanded( + flex: 4, + child: WalletInfoRowBalanceFuture( + walletId: walletId, + ), + ), + Expanded( + flex: 6, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SvgPicture.asset( + Assets.svg.chevronRight, + width: 20, + height: 20, color: Theme.of(context) .extension()! - .textDark, - ), - ), - ], - ), - ), - Expanded( - flex: 4, - child: WalletInfoRowBalanceFuture( - walletId: walletId, - ), - ), - Expanded( - flex: 6, - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - SvgPicture.asset( - Assets.svg.chevronRight, - width: 20, - height: 20, - color: Theme.of(context) - .extension()! - .textSubtitle1, - ) - ], - ), - ) - ], + .textSubtitle1, + ) + ], + ), + ) + ], + ), ), ), ), From 345a077e0611d3e294325bfd74529938f0c93e14 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 28 Nov 2022 15:37:18 -0600 Subject: [PATCH 10/14] desktop fav card hover effect --- .../sub_widgets/favorite_card.dart | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/lib/pages/wallets_view/sub_widgets/favorite_card.dart b/lib/pages/wallets_view/sub_widgets/favorite_card.dart index 7749f264d..24b1e021a 100644 --- a/lib/pages/wallets_view/sub_widgets/favorite_card.dart +++ b/lib/pages/wallets_view/sub_widgets/favorite_card.dart @@ -49,6 +49,8 @@ class _FavoriteCardState extends ConsumerState { super.initState(); } + bool _hovering = false; + @override Widget build(BuildContext context) { final coin = ref.watch(managerProvider.select((value) => value.coin)); @@ -59,7 +61,48 @@ class _FavoriteCardState extends ConsumerState { condition: Util.isDesktop, builder: (child) => MouseRegion( cursor: SystemMouseCursors.click, - child: child, + onEnter: (_) { + setState(() { + _hovering = true; + }); + }, + onExit: (_) { + setState(() { + _hovering = false; + }); + }, + child: AnimatedScale( + duration: const Duration(milliseconds: 200), + scale: _hovering ? 1.05 : 1, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + decoration: _hovering + ? BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + boxShadow: [ + Theme.of(context) + .extension()! + .standardBoxShadow, + Theme.of(context) + .extension()! + .standardBoxShadow, + Theme.of(context) + .extension()! + .standardBoxShadow, + ], + ) + : BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + child: child, + ), + ), ), child: GestureDetector( onTap: () { From 178565a19025d0348e04beded423db91110ba5b4 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 28 Nov 2022 15:43:35 -0600 Subject: [PATCH 11/14] date picker file license added --- lib/widgets/rounded_date_picker/LICENSE | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 lib/widgets/rounded_date_picker/LICENSE diff --git a/lib/widgets/rounded_date_picker/LICENSE b/lib/widgets/rounded_date_picker/LICENSE new file mode 100644 index 000000000..58665fbd2 --- /dev/null +++ b/lib/widgets/rounded_date_picker/LICENSE @@ -0,0 +1,5 @@ +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License in the LICENSE file, or at: + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. \ No newline at end of file From 18da658a652863c7599e2602ba4b8472bfaab6f4 Mon Sep 17 00:00:00 2001 From: julian Date: Sat, 26 Nov 2022 13:39:52 -0600 Subject: [PATCH 12/14] persist active wallet on desktop --- .../home/desktop_home_view.dart | 71 +++++++++++++++++-- .../my_stack_view/coin_wallets_table.dart | 4 ++ .../wallet_view/desktop_wallet_view.dart | 2 +- 3 files changed, 70 insertions(+), 7 deletions(-) diff --git a/lib/pages_desktop_specific/home/desktop_home_view.dart b/lib/pages_desktop_specific/home/desktop_home_view.dart index 54c74fe88..3e0b9311b 100644 --- a/lib/pages_desktop_specific/home/desktop_home_view.dart +++ b/lib/pages_desktop_specific/home/desktop_home_view.dart @@ -9,12 +9,19 @@ import 'package:stackwallet/pages_desktop_specific/home/notifications/desktop_no import 'package:stackwallet/pages_desktop_specific/home/support_and_about_view/desktop_about_view.dart'; import 'package:stackwallet/pages_desktop_specific/home/support_and_about_view/desktop_support_view.dart'; import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart'; +import 'package:stackwallet/providers/global/auto_swb_service_provider.dart'; import 'package:stackwallet/providers/global/notifications_provider.dart'; +import 'package:stackwallet/providers/global/prefs_provider.dart'; +import 'package:stackwallet/providers/global/wallets_provider.dart'; +import 'package:stackwallet/providers/ui/transaction_filter_provider.dart'; import 'package:stackwallet/providers/ui/unread_notifications_provider.dart'; import 'package:stackwallet/route_generator.dart'; +import 'package:stackwallet/utilities/enums/backup_frequency_type.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/widgets/background.dart'; +final currentWalletIdProvider = StateProvider((_) => null); + class DesktopHomeView extends ConsumerStatefulWidget { const DesktopHomeView({Key? key}) : super(key: key); @@ -25,12 +32,25 @@ class DesktopHomeView extends ConsumerStatefulWidget { } class _DesktopHomeViewState extends ConsumerState { - final Map contentViews = { - DesktopMenuItemId.myStack: const Navigator( - key: Key("desktopStackHomeKey"), + final GlobalKey key = GlobalKey(); + late final Navigator myStackViewNav; + + @override + void initState() { + myStackViewNav = Navigator( + key: key, onGenerateRoute: RouteGenerator.generateRoute, initialRoute: MyStackView.routeName, - ), + ); + super.initState(); + } + + final Map contentViews = { + DesktopMenuItemId.myStack: Container( + // key: Key("desktopStackHomeKey"), + // onGenerateRoute: RouteGenerator.generateRoute, + // initialRoute: MyStackView.routeName, + ), DesktopMenuItemId.exchange: const Navigator( key: Key("desktopExchangeHomeKey"), onGenerateRoute: RouteGenerator.generateRoute, @@ -63,7 +83,30 @@ class _DesktopHomeViewState extends ConsumerState { ), }; + DesktopMenuItemId prev = DesktopMenuItemId.myStack; + void onMenuSelectionWillChange(DesktopMenuItemId newKey) { + if (prev == DesktopMenuItemId.myStack && prev == newKey) { + Navigator.of(key.currentContext!) + .popUntil(ModalRoute.withName(MyStackView.routeName)); + if (ref.read(currentWalletIdProvider.state).state != null) { + final managerProvider = ref + .read(walletsChangeNotifierProvider) + .getManagerProvider(ref.read(currentWalletIdProvider.state).state!); + if (ref.read(managerProvider).shouldAutoSync) { + ref.read(managerProvider).shouldAutoSync = false; + } + ref.read(transactionFilterProvider.state).state = null; + if (ref.read(prefsChangeNotifierProvider).isAutoBackupEnabled && + ref.read(prefsChangeNotifierProvider).backupFrequencyType == + BackupFrequencyType.afterClosingAWallet) { + ref.read(autoSWBServiceProvider).doBackup(); + } + ref.read(managerProvider.notifier).isActiveWallet = false; + } + } + prev = newKey; + // check for unread notifications and refresh provider before // showing notifications view if (newKey == DesktopMenuItemId.notifications) { @@ -111,9 +154,25 @@ class _DesktopHomeViewState extends ConsumerState { color: Theme.of(context).extension()!.background, ), Expanded( - child: contentViews[ - ref.watch(currentDesktopMenuItemProvider.state).state]!, + child: IndexedStack( + index: ref + .watch(currentDesktopMenuItemProvider.state) + .state + .index > + 0 + ? 1 + : 0, + children: [ + myStackViewNav, + contentViews[ + ref.watch(currentDesktopMenuItemProvider.state).state]!, + ], + ), ), + // Expanded( + // child: contentViews[ + // ref.watch(currentDesktopMenuItemProvider.state).state]!, + // ), ], ), ), diff --git a/lib/pages_desktop_specific/home/my_stack_view/coin_wallets_table.dart b/lib/pages_desktop_specific/home/my_stack_view/coin_wallets_table.dart index 4ed8765ae..1edb93e06 100644 --- a/lib/pages_desktop_specific/home/my_stack_view/coin_wallets_table.dart +++ b/lib/pages_desktop_specific/home/my_stack_view/coin_wallets_table.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart'; import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; @@ -51,6 +52,9 @@ class CoinWalletsTable extends ConsumerWidget { Positioned.fill( child: WalletRowHoverOverlay( onPressed: () async { + ref.read(currentWalletIdProvider.state).state = + walletIds[i]; + await Navigator.of(context).pushNamed( DesktopWalletView.routeName, arguments: walletIds[i], diff --git a/lib/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart b/lib/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart index 5996597b5..d870835f1 100644 --- a/lib/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart +++ b/lib/pages_desktop_specific/home/my_stack_view/wallet_view/desktop_wallet_view.dart @@ -81,13 +81,13 @@ class _DesktopWalletViewState extends ConsumerState { // disable auto sync if it was enabled only when loading wallet ref.read(managerProvider).shouldAutoSync = false; } - ref.read(managerProvider.notifier).isActiveWallet = false; ref.read(transactionFilterProvider.state).state = null; if (ref.read(prefsChangeNotifierProvider).isAutoBackupEnabled && ref.read(prefsChangeNotifierProvider).backupFrequencyType == BackupFrequencyType.afterClosingAWallet) { unawaited(ref.read(autoSWBServiceProvider).doBackup()); } + ref.read(managerProvider.notifier).isActiveWallet = false; } void _loadCNData() { From c9a91e10ac3d260a14714394c154bca7f7df591e Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 28 Nov 2022 16:11:02 -0600 Subject: [PATCH 13/14] clean up theme init --- lib/main.dart | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index a49bcab82..8136965db 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -318,17 +318,17 @@ class _MaterialAppWithThemeState extends ConsumerState final colorScheme = DB.instance .get(boxName: DB.boxNameTheme, key: "colorScheme") as String?; - ThemeType themeType; + StackColorTheme colorTheme; switch (colorScheme) { case "dark": - themeType = ThemeType.dark; + colorTheme = DarkColors(); break; case "oceanBreeze": - themeType = ThemeType.oceanBreeze; + colorTheme = OceanBreezeColors(); break; case "light": default: - themeType = ThemeType.light; + colorTheme = LightColors(); } loadingCompleter = Completer(); WidgetsBinding.instance.addObserver(this); @@ -339,11 +339,7 @@ class _MaterialAppWithThemeState extends ConsumerState WidgetsBinding.instance.addPostFrameCallback((_) async { ref.read(colorThemeProvider.state).state = - StackColors.fromStackColorTheme(themeType == ThemeType.dark - ? DarkColors() - : (themeType == ThemeType.light - ? LightColors() - : OceanBreezeColors())); + StackColors.fromStackColorTheme(colorTheme); if (Platform.isAndroid) { // fetch open file if it exists From 1aca715397a4146059c53ca446eea229a868d73e Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 28 Nov 2022 16:17:33 -0600 Subject: [PATCH 14/14] animated desktop menu fix --- .../home/desktop_menu.dart | 16 ---------------- .../home/desktop_menu_item.dart | 14 +++++++------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/lib/pages_desktop_specific/home/desktop_menu.dart b/lib/pages_desktop_specific/home/desktop_menu.dart index 8af307e7b..fd404db94 100644 --- a/lib/pages_desktop_specific/home/desktop_menu.dart +++ b/lib/pages_desktop_specific/home/desktop_menu.dart @@ -157,8 +157,6 @@ class _DesktopMenuState extends ConsumerState { ), label: "My Stack", value: DesktopMenuItemId.myStack, - group: - ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: updateSelectedMenuItem, controller: controllers[0], ), @@ -185,8 +183,6 @@ class _DesktopMenuState extends ConsumerState { ), label: "Exchange", value: DesktopMenuItemId.exchange, - group: - ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: updateSelectedMenuItem, controller: controllers[1], ), @@ -220,8 +216,6 @@ class _DesktopMenuState extends ConsumerState { ), label: "Notifications", value: DesktopMenuItemId.notifications, - group: - ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: updateSelectedMenuItem, controller: controllers[2], ), @@ -248,8 +242,6 @@ class _DesktopMenuState extends ConsumerState { ), label: "Address Book", value: DesktopMenuItemId.addressBook, - group: - ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: updateSelectedMenuItem, controller: controllers[3], ), @@ -276,8 +268,6 @@ class _DesktopMenuState extends ConsumerState { ), label: "Settings", value: DesktopMenuItemId.settings, - group: - ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: updateSelectedMenuItem, controller: controllers[4], ), @@ -304,8 +294,6 @@ class _DesktopMenuState extends ConsumerState { ), label: "Support", value: DesktopMenuItemId.support, - group: - ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: updateSelectedMenuItem, controller: controllers[5], ), @@ -332,8 +320,6 @@ class _DesktopMenuState extends ConsumerState { ), label: "About", value: DesktopMenuItemId.about, - group: - ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: updateSelectedMenuItem, controller: controllers[6], ), @@ -352,8 +338,6 @@ class _DesktopMenuState extends ConsumerState { ), label: "Exit", value: 7, - group: - ref.watch(currentDesktopMenuItemProvider.state).state, onChanged: (_) { // todo: save stuff/ notify before exit? exit(0); diff --git a/lib/pages_desktop_specific/home/desktop_menu_item.dart b/lib/pages_desktop_specific/home/desktop_menu_item.dart index 1fb39213b..78dcde79b 100644 --- a/lib/pages_desktop_specific/home/desktop_menu_item.dart +++ b/lib/pages_desktop_specific/home/desktop_menu_item.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; @@ -9,13 +11,12 @@ class DMIController { } } -class DesktopMenuItem extends StatefulWidget { +class DesktopMenuItem extends ConsumerStatefulWidget { const DesktopMenuItem({ Key? key, required this.icon, required this.label, required this.value, - required this.group, required this.onChanged, required this.duration, this.labelLength = 125, @@ -25,22 +26,20 @@ class DesktopMenuItem extends StatefulWidget { final Widget icon; final String label; final T value; - final T group; final void Function(T) onChanged; final Duration duration; final double labelLength; final DMIController? controller; @override - State> createState() => _DesktopMenuItemState(); + ConsumerState> createState() => _DesktopMenuItemState(); } -class _DesktopMenuItemState extends State> +class _DesktopMenuItemState extends ConsumerState> 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; @@ -67,7 +66,6 @@ class _DesktopMenuItemState extends State> icon = widget.icon; label = widget.label; value = widget.value; - group = widget.group; onChanged = widget.onChanged; duration = widget.duration; labelLength = widget.labelLength; @@ -91,6 +89,8 @@ class _DesktopMenuItemState extends State> @override Widget build(BuildContext context) { + final group = ref.watch(currentDesktopMenuItemProvider.state).state; + debugPrint("============ value:$value ============ group:$group"); return TextButton( style: value == group ? Theme.of(context)