From 244b4992ed66cd9ba976a722eaecc2b1cf89f341 Mon Sep 17 00:00:00 2001 From: julian Date: Fri, 10 Mar 2023 12:42:45 -0600 Subject: [PATCH] wallet navigation bar widget redesign --- .../sub_widgets/wallet_navigation_bar.dart | 592 ------------- lib/pages/wallet_view/wallet_view.dart | 820 ++++++++++-------- .../components/icons/buy_nav_icon.dart | 16 + .../icons/coin_control_nav_icon.dart | 18 + .../components/icons/exchange_nav_icon.dart | 16 + .../components/icons/paynym_nav_icon.dart | 18 + .../components/icons/receive_nav_icon.dart | 32 + .../components/icons/send_nav_icon.dart | 32 + .../components/icons/whirlpool_nav_icon.dart | 18 + .../wallet_navigation_bar_item.dart | 125 +++ .../wallet_navigation_bar.dart | 237 +++++ 11 files changed, 965 insertions(+), 959 deletions(-) delete mode 100644 lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart create mode 100644 lib/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart create mode 100644 lib/widgets/wallet_navigation_bar/components/icons/coin_control_nav_icon.dart create mode 100644 lib/widgets/wallet_navigation_bar/components/icons/exchange_nav_icon.dart create mode 100644 lib/widgets/wallet_navigation_bar/components/icons/paynym_nav_icon.dart create mode 100644 lib/widgets/wallet_navigation_bar/components/icons/receive_nav_icon.dart create mode 100644 lib/widgets/wallet_navigation_bar/components/icons/send_nav_icon.dart create mode 100644 lib/widgets/wallet_navigation_bar/components/icons/whirlpool_nav_icon.dart create mode 100644 lib/widgets/wallet_navigation_bar/components/wallet_navigation_bar_item.dart create mode 100644 lib/widgets/wallet_navigation_bar/wallet_navigation_bar.dart diff --git a/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart b/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart deleted file mode 100644 index 18a1dfb34..000000000 --- a/lib/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart +++ /dev/null @@ -1,592 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:stackwallet/pages/coin_control/coin_control_view.dart'; -import 'package:stackwallet/pages/paynym/paynym_claim_view.dart'; -import 'package:stackwallet/pages/paynym/paynym_home_view.dart'; -import 'package:stackwallet/providers/global/paynym_api_provider.dart'; -import 'package:stackwallet/providers/global/prefs_provider.dart'; -import 'package:stackwallet/providers/global/wallets_provider.dart'; -import 'package:stackwallet/providers/wallet/my_paynym_account_state_provider.dart'; -import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart'; -import 'package:stackwallet/utilities/assets.dart'; -import 'package:stackwallet/utilities/enums/coin_enum.dart'; -import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart'; -import 'package:stackwallet/utilities/logger.dart'; -import 'package:stackwallet/utilities/text_styles.dart'; -import 'package:stackwallet/utilities/theme/stack_colors.dart'; -import 'package:stackwallet/widgets/loading_indicator.dart'; -import 'package:tuple/tuple.dart'; - -class WalletNavigationBar extends ConsumerStatefulWidget { - const WalletNavigationBar({ - Key? key, - required this.onReceivePressed, - required this.onSendPressed, - required this.onExchangePressed, - required this.onBuyPressed, - required this.height, - required this.enableExchange, - required this.coin, - required this.walletId, - }) : super(key: key); - - final VoidCallback onReceivePressed; - final VoidCallback onSendPressed; - final VoidCallback onExchangePressed; - final VoidCallback onBuyPressed; - final double height; - final bool enableExchange; - final Coin coin; - final String walletId; - - @override - ConsumerState createState() => - _WalletNavigationBarState(); -} - -class _WalletNavigationBarState extends ConsumerState { - double scale = 0; - final duration = const Duration(milliseconds: 200); - - @override - Widget build(BuildContext context) { - final showMore = ref.watch( - walletsChangeNotifierProvider.select( - (value) => value.getManager(widget.walletId).hasPaynymSupport, - ), - ) || - (ref.watch( - walletsChangeNotifierProvider.select( - (value) => - value.getManager(widget.walletId).hasCoinControlSupport, - ), - ) && - ref.watch( - prefsChangeNotifierProvider.select( - (value) => value.enableCoinControl, - ), - )); - - return Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - // const Spacer(), - - AnimatedScale( - scale: scale, - duration: duration, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // AnimatedOpacity( - // opacity: scale, - // duration: duration, - // child: GestureDetector( - // onTap: () {}, - // child: Container( - // padding: const EdgeInsets.all(16), - // width: 146, - // decoration: BoxDecoration( - // color: - // Theme.of(context).extension()!.popupBG, - // boxShadow: [ - // Theme.of(context) - // .extension()! - // .standardBoxShadow - // ], - // borderRadius: BorderRadius.circular( - // widget.height / 2.0, - // ), - // ), - // child: Row( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // Text( - // "Whirlpool", - // style: STextStyles.w600_12(context), - // ), - // ], - // ), - // ), - // ), - // ), - // const SizedBox( - // height: 8, - // ), - if (ref.watch( - walletsChangeNotifierProvider.select( - (value) => value - .getManager(widget.walletId) - .hasCoinControlSupport, - ), - ) && - ref.watch( - prefsChangeNotifierProvider.select( - (value) => value.enableCoinControl, - ), - )) - AnimatedOpacity( - opacity: scale, - duration: duration, - child: GestureDetector( - onTap: () { - if (mounted) { - // hide more context menu - setState(() { - scale = 0; - }); - - Navigator.of(context).pushNamed( - CoinControlView.routeName, - arguments: Tuple2( - widget.walletId, - CoinControlViewType.manage, - ), - ); - } - }, - child: Container( - padding: const EdgeInsets.all(16), - width: 146, - decoration: BoxDecoration( - color: - Theme.of(context).extension()!.popupBG, - boxShadow: [ - Theme.of(context) - .extension()! - .standardBoxShadow - ], - borderRadius: BorderRadius.circular( - widget.height / 2.0, - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - "Coin control", - style: STextStyles.buttonSmall(context), - ), - const SizedBox( - width: 16, - ), - SvgPicture.asset( - Assets.svg.coinControl.gamePad, - height: 20, - width: 20, - color: Theme.of(context) - .extension()! - .bottomNavIconIcon, - ), - ], - ), - ), - ), - ), - if (ref.watch( - walletsChangeNotifierProvider.select( - (value) => value - .getManager(widget.walletId) - .hasCoinControlSupport, - ), - ) && - ref.watch( - prefsChangeNotifierProvider.select( - (value) => value.enableCoinControl, - ), - ) && - ref.watch( - walletsChangeNotifierProvider.select( - (value) => - value.getManager(widget.walletId).hasPaynymSupport, - ), - )) - const SizedBox( - height: 8, - ), - if (ref.watch(walletsChangeNotifierProvider.select((value) => - value.getManager(widget.walletId).hasPaynymSupport))) - AnimatedOpacity( - opacity: scale, - duration: duration, - child: Consumer(builder: (context, ref, __) { - return GestureDetector( - onTap: () async { - setState(() { - scale = 0; - }); - unawaited( - showDialog( - context: context, - builder: (context) => const LoadingIndicator( - width: 100, - ), - ), - ); - - final manager = ref - .read(walletsChangeNotifierProvider) - .getManager(widget.walletId); - - final paynymInterface = - manager.wallet as PaynymWalletInterface; - - final code = await paynymInterface.getPaymentCode( - DerivePathTypeExt.primaryFor(manager.coin)); - - final account = await ref - .read(paynymAPIProvider) - .nym(code.toString()); - - Logging.instance.log( - "my nym account: $account", - level: LogLevel.Info, - ); - - if (mounted) { - Navigator.of(context).pop(); - - // check if account exists and for matching code to see if claimed - if (account.value != null && - account.value!.codes.first.claimed) { - ref.read(myPaynymAccountStateProvider.state).state = - account.value!; - - await Navigator.of(context).pushNamed( - PaynymHomeView.routeName, - arguments: widget.walletId, - ); - } else { - await Navigator.of(context).pushNamed( - PaynymClaimView.routeName, - arguments: widget.walletId, - ); - } - } - }, - child: Container( - padding: const EdgeInsets.all(16), - width: 146, - decoration: BoxDecoration( - color: Theme.of(context) - .extension()! - .popupBG, - boxShadow: [ - Theme.of(context) - .extension()! - .standardBoxShadow - ], - borderRadius: BorderRadius.circular( - widget.height / 2.0, - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - "Paynym", - style: STextStyles.buttonSmall(context), - ), - const SizedBox( - width: 16, - ), - SvgPicture.asset( - Assets.svg.robotHead, - height: 20, - width: 20, - color: Theme.of(context) - .extension()! - .bottomNavIconIcon, - ), - ], - ), - ), - ); - }), - ), - const SizedBox( - height: 8, - ), - ], - ), - ), - Container( - height: widget.height, - decoration: BoxDecoration( - color: Theme.of(context).extension()!.bottomNavBack, - boxShadow: [ - Theme.of(context).extension()!.standardBoxShadow - ], - borderRadius: BorderRadius.circular( - widget.height / 2.0, - ), - ), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 6, - vertical: 4, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - const SizedBox( - width: 12, - ), - RawMaterialButton( - constraints: const BoxConstraints( - minWidth: 66, - ), - onPressed: widget.onReceivePressed, - splashColor: - Theme.of(context).extension()!.highlight, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - widget.height / 2.0, - ), - ), - child: Container( - color: Colors.transparent, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Spacer(), - Container( - decoration: BoxDecoration( - color: Theme.of(context) - .extension()! - .accentColorDark - .withOpacity(0.4), - borderRadius: BorderRadius.circular( - 24, - ), - ), - child: Padding( - padding: const EdgeInsets.all(6.0), - child: SvgPicture.asset( - Assets.svg.arrowDownLeft, - width: 12, - height: 12, - color: Theme.of(context) - .extension()! - .accentColorDark, - ), - ), - ), - const SizedBox( - height: 4, - ), - Text( - "Receive", - style: STextStyles.buttonSmall(context), - ), - const Spacer(), - ], - ), - ), - ), - ), - RawMaterialButton( - constraints: const BoxConstraints( - minWidth: 66, - ), - onPressed: widget.onSendPressed, - splashColor: - Theme.of(context).extension()!.highlight, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - widget.height / 2.0, - ), - ), - child: Container( - color: Colors.transparent, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Spacer(), - Container( - decoration: BoxDecoration( - color: Theme.of(context) - .extension()! - .accentColorDark - .withOpacity(0.4), - borderRadius: BorderRadius.circular( - 24, - ), - ), - child: Padding( - padding: const EdgeInsets.all(6.0), - child: SvgPicture.asset( - Assets.svg.arrowUpRight, - width: 12, - height: 12, - color: Theme.of(context) - .extension()! - .accentColorDark, - ), - ), - ), - const SizedBox( - height: 4, - ), - Text( - "Send", - style: STextStyles.buttonSmall(context), - ), - const Spacer(), - ], - ), - ), - ), - ), - if (widget.enableExchange) - RawMaterialButton( - constraints: const BoxConstraints( - minWidth: 66, - ), - onPressed: widget.onExchangePressed, - splashColor: - Theme.of(context).extension()!.highlight, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - widget.height / 2.0, - ), - ), - child: Container( - color: Colors.transparent, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Spacer(), - SvgPicture.asset( - Assets.svg.exchange(context), - width: 24, - height: 24, - ), - const SizedBox( - height: 4, - ), - Text( - "Exchange", - style: STextStyles.buttonSmall(context), - ), - const Spacer(), - ], - ), - ), - ), - ), - if (widget.coin.hasBuySupport) - RawMaterialButton( - constraints: const BoxConstraints( - minWidth: 66, - ), - onPressed: widget.onBuyPressed, - splashColor: - Theme.of(context).extension()!.highlight, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - widget.height / 2.0, - ), - ), - child: Container( - color: Colors.transparent, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Spacer(), - SvgPicture.asset( - Assets.svg.buy(context), - width: 24, - height: 24, - ), - const SizedBox( - height: 4, - ), - Text( - "Buy", - style: STextStyles.buttonSmall(context), - ), - const Spacer(), - ], - ), - ), - ), - ), - if (showMore) - RawMaterialButton( - constraints: const BoxConstraints( - minWidth: 66, - ), - onPressed: () { - if (scale == 0) { - setState(() { - scale = 1; - }); - } else if (scale == 1) { - setState(() { - scale = 0; - }); - } - }, - splashColor: - Theme.of(context).extension()!.highlight, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - widget.height / 2.0, - ), - ), - child: Container( - color: Colors.transparent, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Spacer(), - const SizedBox( - height: 2, - ), - SvgPicture.asset( - Assets.svg.bars, - width: 20, - height: 20, - color: Theme.of(context) - .extension()! - .bottomNavIconIcon, - ), - const SizedBox( - height: 6, - ), - Text( - "More", - style: STextStyles.buttonSmall(context), - ), - const Spacer(), - ], - ), - ), - ), - ), - const SizedBox( - width: 12, - ), - ], - ), - ), - ), - ], - ); - } -} diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 28c77f30f..e6a676bf3 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -7,21 +7,25 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/pages/buy_view/buy_in_wallet_view.dart'; +import 'package:stackwallet/pages/coin_control/coin_control_view.dart'; import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart'; import 'package:stackwallet/pages/home_view/home_view.dart'; import 'package:stackwallet/pages/notification_views/notifications_view.dart'; +import 'package:stackwallet/pages/paynym/paynym_claim_view.dart'; +import 'package:stackwallet/pages/paynym/paynym_home_view.dart'; import 'package:stackwallet/pages/receive_view/receive_view.dart'; import 'package:stackwallet/pages/send_view/send_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_view.dart'; import 'package:stackwallet/pages/wallet_view/sub_widgets/transactions_list.dart'; -import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_navigation_bar.dart'; import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_summary.dart'; import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.dart'; import 'package:stackwallet/providers/global/auto_swb_service_provider.dart'; +import 'package:stackwallet/providers/global/paynym_api_provider.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/ui/transaction_filter_provider.dart'; import 'package:stackwallet/providers/ui/unread_notifications_provider.dart'; +import 'package:stackwallet/providers/wallet/my_paynym_account_state_provider.dart'; import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart'; import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart'; import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; @@ -29,11 +33,14 @@ import 'package:stackwallet/services/coins/manager.dart'; import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/global_event_bus.dart'; +import 'package:stackwallet/services/mixins/paynym_wallet_interface.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/backup_frequency_type.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; +import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart'; import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart'; +import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/widgets/background.dart'; @@ -41,7 +48,16 @@ import 'package:stackwallet/widgets/conditional_parent.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart'; import 'package:stackwallet/widgets/custom_loading_overlay.dart'; +import 'package:stackwallet/widgets/loading_indicator.dart'; import 'package:stackwallet/widgets/stack_dialog.dart'; +import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart'; +import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/coin_control_nav_icon.dart'; +import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/exchange_nav_icon.dart'; +import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/paynym_nav_icon.dart'; +import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/receive_nav_icon.dart'; +import 'package:stackwallet/widgets/wallet_navigation_bar/components/icons/send_nav_icon.dart'; +import 'package:stackwallet/widgets/wallet_navigation_bar/components/wallet_navigation_bar_item.dart'; +import 'package:stackwallet/widgets/wallet_navigation_bar/wallet_navigation_bar.dart'; import 'package:tuple/tuple.dart'; /// [eventBus] should only be set during testing @@ -398,297 +414,303 @@ class _WalletViewState extends ConsumerState { child: WillPopScope( onWillPop: _onWillPop, child: Background( - child: Scaffold( - backgroundColor: - Theme.of(context).extension()!.background, - appBar: AppBar( - leading: AppBarBackButton( - onPressed: () { - _logout(); - Navigator.of(context).pop(); - }, - ), - titleSpacing: 0, - title: Row( - children: [ - SvgPicture.asset( - Assets.svg.iconFor(coin: coin), - // color: Theme.of(context).extension()!.accentColorDark - width: 24, - height: 24, + child: Stack( + children: [ + Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + leading: AppBarBackButton( + onPressed: () { + _logout(); + Navigator.of(context).pop(); + }, ), - const SizedBox( - width: 16, - ), - Expanded( - child: Text( - ref.watch( - managerProvider.select((value) => value.walletName)), - style: STextStyles.navBarTitle(context), - overflow: TextOverflow.ellipsis, - ), - ) - ], - ), - actions: [ - Padding( - padding: const EdgeInsets.only( - top: 10, - bottom: 10, - right: 10, - ), - child: AspectRatio( - aspectRatio: 1, - child: AppBarIconButton( - key: const Key("walletViewRadioButton"), - size: 36, - shadows: const [], - color: Theme.of(context) - .extension()! - .background, - icon: _buildNetworkIcon(_currentSyncStatus), - onPressed: () { - Navigator.of(context).pushNamed( - WalletNetworkSettingsView.routeName, - arguments: Tuple3( - walletId, - _currentSyncStatus, - _currentNodeStatus, - ), - ); - }, - ), - ), - ), - Padding( - padding: const EdgeInsets.only( - top: 10, - bottom: 10, - right: 10, - ), - child: AspectRatio( - aspectRatio: 1, - child: AppBarIconButton( - key: const Key("walletViewAlertsButton"), - size: 36, - shadows: const [], - color: Theme.of(context) - .extension()! - .background, - icon: SvgPicture.asset( - ref.watch(notificationsProvider.select((value) => - value.hasUnreadNotificationsFor(walletId))) - ? Assets.svg.bellNew(context) - : Assets.svg.bell, - width: 20, - height: 20, - color: ref.watch(notificationsProvider.select((value) => - value.hasUnreadNotificationsFor(walletId))) - ? null - : Theme.of(context) - .extension()! - .topNavIconPrimary, + titleSpacing: 0, + title: Row( + children: [ + SvgPicture.asset( + Assets.svg.iconFor(coin: coin), + // color: Theme.of(context).extension()!.accentColorDark + width: 24, + height: 24, ), - onPressed: () { - // reset unread state - ref.refresh(unreadNotificationsStateProvider); - - Navigator.of(context) - .pushNamed( - NotificationsView.routeName, - arguments: walletId, - ) - .then((_) { - final Set unreadNotificationIds = ref - .read(unreadNotificationsStateProvider.state) - .state; - if (unreadNotificationIds.isEmpty) return; - - List> futures = []; - for (int i = 0; - i < unreadNotificationIds.length - 1; - i++) { - futures.add(ref - .read(notificationsProvider) - .markAsRead( - unreadNotificationIds.elementAt(i), false)); - } - - // wait for multiple to update if any - Future.wait(futures).then((_) { - // only notify listeners once - ref - .read(notificationsProvider) - .markAsRead(unreadNotificationIds.last, true); - }); - }); - }, - ), - ), - ), - Padding( - padding: const EdgeInsets.only( - top: 10, - bottom: 10, - right: 10, - ), - child: AspectRatio( - aspectRatio: 1, - child: AppBarIconButton( - key: const Key("walletViewSettingsButton"), - size: 36, - shadows: const [], - color: Theme.of(context) - .extension()! - .background, - icon: SvgPicture.asset( - Assets.svg.bars, - color: Theme.of(context) - .extension()! - .accentColorDark, - width: 20, - height: 20, - ), - onPressed: () { - //todo: check if print needed - // debugPrint("wallet view settings tapped"); - Navigator.of(context).pushNamed( - WalletSettingsView.routeName, - arguments: Tuple4( - walletId, - ref.read(managerProvider).coin, - _currentSyncStatus, - _currentNodeStatus, - ), - ); - }, - ), - ), - ), - ], - ), - body: SafeArea( - child: Container( - color: Theme.of(context).extension()!.background, - child: Column( - children: [ - const SizedBox( - height: 10, - ), - Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: WalletSummary( - walletId: walletId, - managerProvider: managerProvider, - initialSyncStatus: ref.watch(managerProvider - .select((value) => value.isRefreshing)) - ? WalletSyncStatus.syncing - : WalletSyncStatus.synced, - ), - ), - ), - if (coin == Coin.firo) const SizedBox( - height: 10, + width: 16, ), - if (coin == Coin.firo) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Row( - children: [ - Expanded( - child: TextButton( - style: Theme.of(context) - .extension()! - .getSecondaryEnabledButtonStyle(context), - onPressed: () async { - await showDialog( - context: context, - builder: (context) => StackDialog( - title: "Attention!", - message: - "You're about to anonymize all of your public funds.", - leftButton: TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: Text( - "Cancel", - style: STextStyles.button(context) - .copyWith( - color: Theme.of(context) - .extension()! - .accentColorDark, - ), - ), - ), - rightButton: TextButton( - onPressed: () async { - Navigator.of(context).pop(); - - unawaited(attemptAnonymize()); - }, - style: Theme.of(context) - .extension()! - .getPrimaryEnabledButtonStyle( - context), - child: Text( - "Continue", - style: STextStyles.button(context), - ), - ), - ), - ); - }, - child: Text( - "Anonymize funds", - style: STextStyles.button(context).copyWith( - color: Theme.of(context) - .extension()! - .buttonTextSecondary, - ), - ), + Expanded( + child: Text( + ref.watch(managerProvider + .select((value) => value.walletName)), + style: STextStyles.navBarTitle(context), + overflow: TextOverflow.ellipsis, + ), + ) + ], + ), + actions: [ + Padding( + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + right: 10, + ), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + key: const Key("walletViewRadioButton"), + size: 36, + shadows: const [], + color: Theme.of(context) + .extension()! + .background, + icon: _buildNetworkIcon(_currentSyncStatus), + onPressed: () { + Navigator.of(context).pushNamed( + WalletNetworkSettingsView.routeName, + arguments: Tuple3( + walletId, + _currentSyncStatus, + _currentNodeStatus, ), - ), - ], + ); + }, ), ), - const SizedBox( - height: 20, ), Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Transactions", - style: STextStyles.itemSubtitle(context).copyWith( - color: Theme.of(context) - .extension()! - .textDark3, - ), + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + right: 10, + ), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + key: const Key("walletViewAlertsButton"), + size: 36, + shadows: const [], + color: Theme.of(context) + .extension()! + .background, + icon: SvgPicture.asset( + ref.watch(notificationsProvider.select((value) => + value.hasUnreadNotificationsFor(walletId))) + ? Assets.svg.bellNew(context) + : Assets.svg.bell, + width: 20, + height: 20, + color: ref.watch(notificationsProvider.select( + (value) => value + .hasUnreadNotificationsFor(walletId))) + ? null + : Theme.of(context) + .extension()! + .topNavIconPrimary, ), - CustomTextButton( - text: "See all", - onTap: () { - Navigator.of(context).pushNamed( - AllTransactionsView.routeName, - arguments: walletId, - ); - }, - ), - ], + onPressed: () { + // reset unread state + ref.refresh(unreadNotificationsStateProvider); + + Navigator.of(context) + .pushNamed( + NotificationsView.routeName, + arguments: walletId, + ) + .then((_) { + final Set unreadNotificationIds = ref + .read(unreadNotificationsStateProvider.state) + .state; + if (unreadNotificationIds.isEmpty) return; + + List> futures = []; + for (int i = 0; + i < unreadNotificationIds.length - 1; + i++) { + futures.add(ref + .read(notificationsProvider) + .markAsRead( + unreadNotificationIds.elementAt(i), + false)); + } + + // wait for multiple to update if any + Future.wait(futures).then((_) { + // only notify listeners once + ref.read(notificationsProvider).markAsRead( + unreadNotificationIds.last, true); + }); + }); + }, + ), ), ), - const SizedBox( - height: 12, + Padding( + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + right: 10, + ), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + key: const Key("walletViewSettingsButton"), + size: 36, + shadows: const [], + color: Theme.of(context) + .extension()! + .background, + icon: SvgPicture.asset( + Assets.svg.bars, + color: Theme.of(context) + .extension()! + .accentColorDark, + width: 20, + height: 20, + ), + onPressed: () { + //todo: check if print needed + // debugPrint("wallet view settings tapped"); + Navigator.of(context).pushNamed( + WalletSettingsView.routeName, + arguments: Tuple4( + walletId, + ref.read(managerProvider).coin, + _currentSyncStatus, + _currentNodeStatus, + ), + ); + }, + ), + ), ), - Expanded( - child: Stack( - children: [ + ], + ), + body: SafeArea( + child: Container( + color: + Theme.of(context).extension()!.background, + child: Column( + children: [ + const SizedBox( + height: 10, + ), + Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: WalletSummary( + walletId: walletId, + managerProvider: managerProvider, + initialSyncStatus: ref.watch(managerProvider + .select((value) => value.isRefreshing)) + ? WalletSyncStatus.syncing + : WalletSyncStatus.synced, + ), + ), + ), + if (coin == Coin.firo) + const SizedBox( + height: 10, + ), + if (coin == Coin.firo) Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + children: [ + Expanded( + child: TextButton( + style: Theme.of(context) + .extension()! + .getSecondaryEnabledButtonStyle( + context), + onPressed: () async { + await showDialog( + context: context, + builder: (context) => StackDialog( + title: "Attention!", + message: + "You're about to anonymize all of your public funds.", + leftButton: TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text( + "Cancel", + style: STextStyles.button(context) + .copyWith( + color: Theme.of(context) + .extension()! + .accentColorDark, + ), + ), + ), + rightButton: TextButton( + onPressed: () async { + Navigator.of(context).pop(); + + unawaited(attemptAnonymize()); + }, + style: Theme.of(context) + .extension()! + .getPrimaryEnabledButtonStyle( + context), + child: Text( + "Continue", + style: + STextStyles.button(context), + ), + ), + ), + ); + }, + child: Text( + "Anonymize funds", + style: + STextStyles.button(context).copyWith( + color: Theme.of(context) + .extension()! + .buttonTextSecondary, + ), + ), + ), + ), + ], + ), + ), + const SizedBox( + height: 20, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Transactions", + style: + STextStyles.itemSubtitle(context).copyWith( + color: Theme.of(context) + .extension()! + .textDark3, + ), + ), + CustomTextButton( + text: "See all", + onTap: () { + Navigator.of(context).pushNamed( + AllTransactionsView.routeName, + arguments: walletId, + ); + }, + ), + ], + ), + ), + const SizedBox( + height: 12, + ), + Expanded( + child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: ClipRRect( borderRadius: BorderRadius.vertical( @@ -741,98 +763,162 @@ class _WalletViewState extends ConsumerState { ), ), ), - Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.only( - bottom: 14, - left: 16, - right: 16, - ), - child: WalletNavigationBar( - walletId: widget.walletId, - coin: ref.watch(managerProvider - .select((value) => value.coin)), - enableExchange: - Constants.enableExchange && - ref.watch(managerProvider.select( - (value) => value.coin)) != - Coin.epicCash, - height: WalletView.navBarHeight, - onExchangePressed: () => - _onExchangePressed(context), - onReceivePressed: () async { - final coin = - ref.read(managerProvider).coin; - if (mounted) { - unawaited( - Navigator.of(context).pushNamed( - ReceiveView.routeName, - arguments: Tuple2( - walletId, - coin, - ), - )); - } - }, - onSendPressed: () { - final walletId = - ref.read(managerProvider).walletId; - final coin = - ref.read(managerProvider).coin; - switch (ref - .read( - walletBalanceToggleStateProvider - .state) - .state) { - case WalletBalanceToggleState.full: - ref - .read( - publicPrivateBalanceStateProvider - .state) - .state = "Public"; - break; - case WalletBalanceToggleState - .available: - ref - .read( - publicPrivateBalanceStateProvider - .state) - .state = "Private"; - break; - } - Navigator.of(context).pushNamed( - SendView.routeName, - arguments: Tuple2( - walletId, - coin, - ), - ); - }, - onBuyPressed: () { - unawaited( - Navigator.of(context).pushNamed( - BuyInWalletView.routeName, - arguments: coin, - )); - }, - ), - ), - ], - ), - ], - ) - ], - ), + ), + ], ), - ], + ), ), ), - ), + WalletNavigationBar( + items: [ + WalletNavigationBarItemData( + label: "Receive", + icon: const ReceiveNavIcon(), + onTap: () { + final coin = ref.read(managerProvider).coin; + if (mounted) { + unawaited( + Navigator.of(context).pushNamed( + ReceiveView.routeName, + arguments: Tuple2( + walletId, + coin, + ), + ), + ); + } + }, + ), + WalletNavigationBarItemData( + label: "Send", + icon: const SendNavIcon(), + onTap: () { + final walletId = ref.read(managerProvider).walletId; + final coin = ref.read(managerProvider).coin; + switch (ref + .read(walletBalanceToggleStateProvider.state) + .state) { + case WalletBalanceToggleState.full: + ref + .read(publicPrivateBalanceStateProvider.state) + .state = "Public"; + break; + case WalletBalanceToggleState.available: + ref + .read(publicPrivateBalanceStateProvider.state) + .state = "Private"; + break; + } + Navigator.of(context).pushNamed( + SendView.routeName, + arguments: Tuple2( + walletId, + coin, + ), + ); + }, + ), + WalletNavigationBarItemData( + label: "Exchange", + icon: const ExchangeNavIcon(), + onTap: () => _onExchangePressed(context), + ), + WalletNavigationBarItemData( + label: "Buy", + icon: const BuyNavIcon(), + onTap: () { + unawaited( + Navigator.of(context).pushNamed( + BuyInWalletView.routeName, + arguments: coin, + ), + ); + }, + ), + if (ref.watch( + walletsChangeNotifierProvider.select( + (value) => value + .getManager(widget.walletId) + .hasCoinControlSupport, + ), + ) && + ref.watch( + prefsChangeNotifierProvider.select( + (value) => value.enableCoinControl, + ), + )) + WalletNavigationBarItemData( + label: "Coin control", + icon: const CoinControlNavIcon(), + onTap: () { + Navigator.of(context).pushNamed( + CoinControlView.routeName, + arguments: Tuple2( + widget.walletId, + CoinControlViewType.manage, + ), + ); + }, + ), + if (ref.watch(walletsChangeNotifierProvider.select((value) => + value.getManager(widget.walletId).hasPaynymSupport))) + WalletNavigationBarItemData( + label: "PayNym", + icon: const PaynymNavIcon(), + onTap: () async { + unawaited( + showDialog( + context: context, + builder: (context) => const LoadingIndicator( + width: 100, + ), + ), + ); + + final manager = ref + .read(walletsChangeNotifierProvider) + .getManager(widget.walletId); + + final paynymInterface = + manager.wallet as PaynymWalletInterface; + + final code = await paynymInterface.getPaymentCode( + DerivePathTypeExt.primaryFor(manager.coin)); + + final account = await ref + .read(paynymAPIProvider) + .nym(code.toString()); + + Logging.instance.log( + "my nym account: $account", + level: LogLevel.Info, + ); + + if (mounted) { + Navigator.of(context).pop(); + + // check if account exists and for matching code to see if claimed + if (account.value != null && + account.value!.codes.first.claimed) { + ref.read(myPaynymAccountStateProvider.state).state = + account.value!; + + await Navigator.of(context).pushNamed( + PaynymHomeView.routeName, + arguments: widget.walletId, + ); + } else { + await Navigator.of(context).pushNamed( + PaynymClaimView.routeName, + arguments: widget.walletId, + ); + } + } + }, + ), + ], + ), + ], ), ), ), diff --git a/lib/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart new file mode 100644 index 000000000..fa2975f1a --- /dev/null +++ b/lib/widgets/wallet_navigation_bar/components/icons/buy_nav_icon.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/utilities/assets.dart'; + +class BuyNavIcon extends StatelessWidget { + const BuyNavIcon({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + Assets.svg.buy(context), + width: 24, + height: 24, + ); + } +} diff --git a/lib/widgets/wallet_navigation_bar/components/icons/coin_control_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/coin_control_nav_icon.dart new file mode 100644 index 000000000..d55cd463f --- /dev/null +++ b/lib/widgets/wallet_navigation_bar/components/icons/coin_control_nav_icon.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; + +class CoinControlNavIcon extends StatelessWidget { + const CoinControlNavIcon({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + Assets.svg.coinControl.gamePad, + height: 20, + width: 20, + color: Theme.of(context).extension()!.bottomNavIconIcon, + ); + } +} diff --git a/lib/widgets/wallet_navigation_bar/components/icons/exchange_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/exchange_nav_icon.dart new file mode 100644 index 000000000..9735fa989 --- /dev/null +++ b/lib/widgets/wallet_navigation_bar/components/icons/exchange_nav_icon.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/utilities/assets.dart'; + +class ExchangeNavIcon extends StatelessWidget { + const ExchangeNavIcon({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + Assets.svg.exchange(context), + width: 24, + height: 24, + ); + } +} diff --git a/lib/widgets/wallet_navigation_bar/components/icons/paynym_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/paynym_nav_icon.dart new file mode 100644 index 000000000..26a2fd96d --- /dev/null +++ b/lib/widgets/wallet_navigation_bar/components/icons/paynym_nav_icon.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; + +class PaynymNavIcon extends StatelessWidget { + const PaynymNavIcon({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + Assets.svg.robotHead, + height: 20, + width: 20, + color: Theme.of(context).extension()!.bottomNavIconIcon, + ); + } +} diff --git a/lib/widgets/wallet_navigation_bar/components/icons/receive_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/receive_nav_icon.dart new file mode 100644 index 000000000..54420543f --- /dev/null +++ b/lib/widgets/wallet_navigation_bar/components/icons/receive_nav_icon.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; + +class ReceiveNavIcon extends StatelessWidget { + const ReceiveNavIcon({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .accentColorDark + .withOpacity(0.4), + borderRadius: BorderRadius.circular( + 24, + ), + ), + child: Padding( + padding: const EdgeInsets.all(6.0), + child: SvgPicture.asset( + Assets.svg.arrowDownLeft, + width: 12, + height: 12, + color: Theme.of(context).extension()!.accentColorDark, + ), + ), + ); + } +} diff --git a/lib/widgets/wallet_navigation_bar/components/icons/send_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/send_nav_icon.dart new file mode 100644 index 000000000..7b7da5799 --- /dev/null +++ b/lib/widgets/wallet_navigation_bar/components/icons/send_nav_icon.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; + +class SendNavIcon extends StatelessWidget { + const SendNavIcon({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .accentColorDark + .withOpacity(0.4), + borderRadius: BorderRadius.circular( + 24, + ), + ), + child: Padding( + padding: const EdgeInsets.all(6.0), + child: SvgPicture.asset( + Assets.svg.arrowUpRight, + width: 12, + height: 12, + color: Theme.of(context).extension()!.accentColorDark, + ), + ), + ); + } +} diff --git a/lib/widgets/wallet_navigation_bar/components/icons/whirlpool_nav_icon.dart b/lib/widgets/wallet_navigation_bar/components/icons/whirlpool_nav_icon.dart new file mode 100644 index 000000000..bc7b0f399 --- /dev/null +++ b/lib/widgets/wallet_navigation_bar/components/icons/whirlpool_nav_icon.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; + +class WhirlpoolNavIcon extends StatelessWidget { + const WhirlpoolNavIcon({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SvgPicture.asset( + Assets.svg.whirlPool, + height: 20, + width: 20, + color: Theme.of(context).extension()!.bottomNavIconIcon, + ); + } +} diff --git a/lib/widgets/wallet_navigation_bar/components/wallet_navigation_bar_item.dart b/lib/widgets/wallet_navigation_bar/components/wallet_navigation_bar_item.dart new file mode 100644 index 000000000..8f1cade50 --- /dev/null +++ b/lib/widgets/wallet_navigation_bar/components/wallet_navigation_bar_item.dart @@ -0,0 +1,125 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/widgets/rounded_container.dart'; +import 'package:stackwallet/widgets/wallet_navigation_bar/wallet_navigation_bar.dart'; + +class WalletNavigationBarItemData { + WalletNavigationBarItemData({ + required this.icon, + required this.label, + required this.onTap, + this.isMore = false, + this.overrideText, + }); + + final Widget icon; + final String? label; + final VoidCallback? onTap; + final bool isMore; + final Widget? overrideText; +} + +class WalletNavigationBarItem extends ConsumerWidget { + const WalletNavigationBarItem({ + Key? key, + required this.data, + required this.disableDuration, + }) : super(key: key); + + final WalletNavigationBarItemData data; + final Duration disableDuration; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return GestureDetector( + onTap: data.isMore || !ref.watch(walletNavBarMore.state).state + ? data.onTap + : null, + child: RoundedContainer( + color: Colors.transparent, + padding: const EdgeInsets.all(0), + radiusMultiplier: 2, + child: AnimatedOpacity( + opacity: + data.isMore || !ref.watch(walletNavBarMore.state).state ? 1 : 0.2, + duration: disableDuration, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: ConstrainedBox( + constraints: const BoxConstraints( + maxHeight: 45, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox( + width: 24, + height: 24, + child: Center( + child: data.icon, + ), + ), + const Spacer(), + data.overrideText ?? + Text( + data.label ?? "", + style: STextStyles.buttonSmall(context), + ), + ], + ), + ), + ), + ), + ), + ); + } +} + +class WalletNavigationBarMoreItem extends ConsumerWidget { + const WalletNavigationBarMoreItem({ + Key? key, + required this.data, + }) : super(key: key); + + final WalletNavigationBarItemData data; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return GestureDetector( + onTap: () { + data.onTap?.call(); + ref.read(walletNavBarMore.state).state = false; + }, + child: Material( + color: Colors.transparent, + child: RoundedContainer( + color: Theme.of(context).extension()!.bottomNavBack, + radiusMultiplier: 100, + padding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 30, + ), + child: Row( + children: [ + Expanded( + child: Text( + data.label ?? "", + textAlign: TextAlign.center, + style: STextStyles.buttonSmall(context), + ), + ), + const SizedBox( + width: 10, + ), + data.icon, + ], + ), + ), + ), + ); + } +} diff --git a/lib/widgets/wallet_navigation_bar/wallet_navigation_bar.dart b/lib/widgets/wallet_navigation_bar/wallet_navigation_bar.dart new file mode 100644 index 000000000..0fca4d4d5 --- /dev/null +++ b/lib/widgets/wallet_navigation_bar/wallet_navigation_bar.dart @@ -0,0 +1,237 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.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/wallet_navigation_bar/components/wallet_navigation_bar_item.dart'; + +const _kMaxItems = 5; + +final walletNavBarMore = StateProvider.autoDispose((ref) => false); + +class WalletNavigationBar extends ConsumerStatefulWidget { + const WalletNavigationBar({ + Key? key, + required this.items, + }) : super(key: key); + + final List items; + + @override + ConsumerState createState() => + _WalletNavigationBarState(); +} + +class _WalletNavigationBarState extends ConsumerState { + static const double horizontalPadding = 16; + + final _moreDuration = const Duration(milliseconds: 200); + + late final bool hasMore; + + double _moreScale = 0; + + void _onMorePressed() { + ref.read(walletNavBarMore.state).state = + !ref.read(walletNavBarMore.state).state; + } + + @override + void initState() { + hasMore = widget.items.length > _kMaxItems; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.bottomCenter, + children: [ + IgnorePointer( + ignoring: !ref.read(walletNavBarMore.state).state, + child: GestureDetector( + onTap: () { + if (ref.read(walletNavBarMore.state).state) { + ref.read(walletNavBarMore.state).state = false; + } + }, + child: AnimatedOpacity( + opacity: ref.watch(walletNavBarMore.state).state ? 1 : 0, + duration: _moreDuration, + child: Container( + color: Colors.black.withOpacity(0.7), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.only( + left: horizontalPadding, + right: horizontalPadding, + bottom: horizontalPadding, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + AnimatedScale( + scale: ref.watch(walletNavBarMore.state).state ? 1 : 0, + duration: _moreDuration, + alignment: const Alignment( + 0.5, + 1.0, + ), + child: AnimatedOpacity( + opacity: ref.watch(walletNavBarMore.state).state ? 1 : 0, + duration: _moreDuration, + child: IntrinsicWidth( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + ...widget.items.sublist(_kMaxItems - 1).map( + (e) { + return Column( + children: [ + WalletNavigationBarMoreItem(data: e), + const SizedBox( + height: 8, + ), + ], + ); + }, + ), + ], + ), + ), + ), + ), + Material( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 1000, + ), + ), + child: Container( + decoration: BoxDecoration( + color: Theme.of(context) + .extension()! + .bottomNavBack, + boxShadow: [ + Theme.of(context) + .extension()! + .standardBoxShadow + ], + borderRadius: BorderRadius.circular( + 1000, + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 6, + horizontal: 12, + ), + child: IntrinsicWidth( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Spacer(), + if (!hasMore) + ...widget.items.map( + (e) => Flexible( + flex: 10000, + child: WalletNavigationBarItem( + data: e, + disableDuration: _moreDuration, + ), + ), + ), + if (hasMore) + ...widget.items.sublist(0, _kMaxItems - 1).map( + (e) => Flexible( + flex: 10000, + child: WalletNavigationBarItem( + data: e, + disableDuration: _moreDuration, + ), + ), + ), + if (hasMore) + Flexible( + flex: 10000, + child: WalletNavigationBarItem( + data: WalletNavigationBarItemData( + icon: AnimatedCrossFade( + firstChild: SvgPicture.asset( + Assets.svg.bars, + width: 20, + height: 20, + color: Theme.of(context) + .extension()! + .bottomNavIconIcon, + ), + secondChild: SvgPicture.asset( + Assets.svg.bars, + width: 20, + height: 20, + color: Theme.of(context) + .extension()! + .infoItemIcons, + ), + crossFadeState: ref + .watch(walletNavBarMore.state) + .state + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, + duration: _moreDuration, + ), + overrideText: AnimatedCrossFade( + firstChild: Text( + "More", + style: + STextStyles.buttonSmall(context), + ), + secondChild: Text( + "More", + style: + STextStyles.buttonSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .infoItemIcons, + ), + ), + crossFadeState: ref + .watch(walletNavBarMore.state) + .state + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, + duration: _moreDuration, + ), + label: null, + isMore: true, + onTap: _onMorePressed, + ), + disableDuration: _moreDuration, + ), + ), + const Spacer(), + ], + ), + ), + ), + ), + ), + ], + ), + ], + ), + ), + ], + ); + } +}