From 301734bbf5c5ab69a24fdfdd55d3a1e8a3930e28 Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Fri, 13 Feb 2026 01:26:53 +0100 Subject: [PATCH 1/6] new home page compact header --- ios/Podfile.lock | 6 +- lib/new-ui/pages/home_page.dart | 363 +++++++++++++----- .../action_row/coin_action_row.dart | 295 +++++++------- .../coins_page/cards/balance_card.dart | 273 ++++++------- .../widgets/coins_page/cards/cards_view.dart | 51 ++- .../coins_page/compact_wallet_header.dart | 66 ++++ res/values/strings_ar.arb | 1 + res/values/strings_bg.arb | 1 + res/values/strings_cs.arb | 1 + res/values/strings_de.arb | 1 + res/values/strings_en.arb | 1 + res/values/strings_es.arb | 1 + res/values/strings_fa.arb | 1 + res/values/strings_fr.arb | 1 + res/values/strings_gn.arb | 1 + res/values/strings_ha.arb | 1 + res/values/strings_hi.arb | 1 + res/values/strings_hr.arb | 1 + res/values/strings_hy.arb | 1 + res/values/strings_id.arb | 1 + res/values/strings_it.arb | 1 + res/values/strings_ja.arb | 7 +- res/values/strings_ko.arb | 1 + res/values/strings_my.arb | 1 + res/values/strings_nl.arb | 1 + res/values/strings_pl.arb | 1 + res/values/strings_pt.arb | 1 + res/values/strings_ru.arb | 1 + res/values/strings_th.arb | 1 + res/values/strings_tl.arb | 1 + res/values/strings_tr.arb | 1 + res/values/strings_uk.arb | 1 + res/values/strings_ur.arb | 1 + res/values/strings_vi.arb | 1 + res/values/strings_yo.arb | 1 + res/values/strings_zh.arb | 1 + 36 files changed, 689 insertions(+), 401 deletions(-) create mode 100644 lib/new-ui/widgets/coins_page/compact_wallet_header.dart diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 9340e9957f..51f6ffba44 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,7 +1,7 @@ PODS: - bitbox_flutter (0.0.1): - Flutter - - breez_sdk_spark_flutter (0.7.14): + - breez_sdk_spark_flutter (0.7.19): - Flutter - connectivity_plus (0.0.1): - Flutter @@ -303,7 +303,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: bitbox_flutter: 506f80b961ddf646b0d80cef9f6eadaab96d91b0 - breez_sdk_spark_flutter: fa928c35bb2097bddf7cfff98edb2fc6cd48afba + breez_sdk_spark_flutter: 84c583d8804e6694c8f231168cae448204a31e53 connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d CryptoSwift: e64e11850ede528a02a0f3e768cec8e9d92ecb90 cw_decred: 9c0e1df74745b51a1289ec5e91fb9e24b68fa14a @@ -356,4 +356,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 5296465b1c6d14d506230356756826012f65d97a -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/lib/new-ui/pages/home_page.dart b/lib/new-ui/pages/home_page.dart index 825a05ac03..c984a6d94d 100644 --- a/lib/new-ui/pages/home_page.dart +++ b/lib/new-ui/pages/home_page.dart @@ -8,6 +8,7 @@ import 'package:cake_wallet/new-ui/viewmodels/card_customizer/card_customizer_bl import 'package:cake_wallet/new-ui/widgets/coins_page/action_row/coin_action_row.dart'; import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/assets_history_section.dart'; import 'package:cake_wallet/new-ui/widgets/coins_page/cards/cards_view.dart'; +import 'package:cake_wallet/new-ui/widgets/coins_page/compact_wallet_header.dart'; import 'package:cake_wallet/new-ui/widgets/coins_page/top_bar_widget/top_bar.dart'; import 'package:cake_wallet/new-ui/widgets/coins_page/unconfirmed_balance_widget.dart'; import 'package:cake_wallet/new-ui/widgets/coins_page/wallet_info.dart'; @@ -59,106 +60,141 @@ class _NewHomePageState extends State { @override Widget build(BuildContext context) { return Container( - height: MediaQuery.of(context).size.height, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - Theme.of(context).colorScheme.surface, - Theme.of(context).colorScheme.surfaceDim, - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, + height: MediaQuery.of(context).size.height, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Theme.of(context).colorScheme.surface, + Theme.of(context).colorScheme.surfaceDim, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), ), - ), - child: Stack( - children: [ - CustomScrollView( - physics: BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), - slivers:[ - SliverPadding( - padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), - sliver: CupertinoSliverRefreshControl( - onRefresh: () => widget.dashboardViewModel.refreshDashboard(), + child: Stack( + children: [ + CustomScrollView( + physics: BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), + slivers: [ + SliverPadding( + padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), + sliver: CupertinoSliverRefreshControl( + onRefresh: () => widget.dashboardViewModel.refreshDashboard(), + ), ), - ), - SliverToBoxAdapter( - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.start, - spacing: 24.0, - children: [ - TopBar( - dashboardViewModel: widget.dashboardViewModel, - lightningMode: _lightningMode, - onLightningSwitchPress: () { - setState(() { - _lightningMode = !_lightningMode; - }); - }, - onSettingsButtonPress: () { - CupertinoScaffold.showCupertinoModalBottomSheet( - context: context, - barrierColor: Colors.black.withAlpha(85), - builder: (context) => FractionallySizedBox( - child: Material( + SliverToBoxAdapter( + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + TopBar( + dashboardViewModel: widget.dashboardViewModel, + lightningMode: _lightningMode, + onLightningSwitchPress: () { + setState(() { + _lightningMode = !_lightningMode; + }); + }, + onSettingsButtonPress: () { + CupertinoScaffold.showCupertinoModalBottomSheet( + context: context, + barrierColor: Colors.black.withAlpha(85), + builder: (context) => FractionallySizedBox( + child: Material( child: NewSettingsPage( dashboardViewModel: widget.dashboardViewModel, - authService: getIt.get(), + authService: getIt.get(), ))), - ); - }, + ); + }, + ), + SizedBox(height: 24), + WalletInfo( + lightningMode: _lightningMode, + hardwareWalletType: widget.dashboardViewModel.wallet.hardwareWalletType, + name: widget.dashboardViewModel.wallet.name, + onCustomizeButtonTap: openCustomizer, + ), + SizedBox(height: 24), + ], ), - WalletInfo( - lightningMode: _lightningMode, - hardwareWalletType: widget.dashboardViewModel.wallet.hardwareWalletType, - name: widget.dashboardViewModel.wallet.name, - onCustomizeButtonTap: openCustomizer + ), + Observer( + builder: (_) => SliverPersistentHeader( + pinned: true, + delegate: CardsViewHeaderDelegate( + maxHeight: getCardBoxHeight(), + sideWidget: CompactWalletHeader(dashboardViewModel: widget.dashboardViewModel,accountListViewModel: accountListViewModel,), + bottomWidget: CompactCoinActionRow(lightningMode: _lightningMode), + minHeight: 100.0, + maxWidth: MediaQuery.of(context).size.width * 0.878, + minWidth: 80, + topPadding: MediaQuery.of(context).padding.top, + cardsViewBuilder: (context, dynamicWidth, showText) { + return CardsView( + cardWidth: dynamicWidth, + showContent: showText, + key: ValueKey(widget.dashboardViewModel.wallet.name), + dashboardViewModel: widget.dashboardViewModel, + accountListViewModel: accountListViewModel, + onCompactModeBackgroundCardsTapped: openCustomizer, + lightningMode: _lightningMode, + ); + }, + ), ), - Column( + ), + SliverToBoxAdapter( + child: Column( children: [ - CardsView( - key: ValueKey(widget.dashboardViewModel.wallet.name), + SizedBox(height: 10), + UnconfirmedBalanceWidget( dashboardViewModel: widget.dashboardViewModel, - accountListViewModel: accountListViewModel, - onCompactModeBackgroundCardsTapped: openCustomizer, - lightningMode: _lightningMode, ), - UnconfirmedBalanceWidget(dashboardViewModel: widget.dashboardViewModel,), + SizedBox(height: 24), + CoinActionRow(lightningMode: _lightningMode), + SizedBox(height: 24), + Observer( + builder: (_) => AssetsHistorySection( + nftViewModel: widget.nftViewModel, + dashboardViewModel: widget.dashboardViewModel, + ), + ), + SizedBox(height: 80.0) ], ), - CoinActionRow(lightningMode: _lightningMode), - Observer( - builder: (_)=>AssetsHistorySection( - nftViewModel: widget.nftViewModel, - dashboardViewModel: widget.dashboardViewModel, - ), - ), - SizedBox(height: 80.0) + ), + ], + ), + Container( + height: (MediaQuery.of(context).padding.top), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Theme.of(context).colorScheme.surface.withAlpha(5), + Theme.of(context).colorScheme.surface.withAlpha(25), + Theme.of(context).colorScheme.surface.withAlpha(50), + Theme.of(context).colorScheme.surface.withAlpha(100), + Theme.of(context).colorScheme.surface.withAlpha(150), + Theme.of(context).colorScheme.surface.withAlpha(175), + Theme.of(context).colorScheme.surface.withAlpha(200), ], - ), - ),] - ), - Container( - height: (MediaQuery.of(context).padding.top), - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - colors: [ - Theme.of(context).colorScheme.surface.withAlpha(5), - Theme.of(context).colorScheme.surface.withAlpha(25), - Theme.of(context).colorScheme.surface.withAlpha(50), - Theme.of(context).colorScheme.surface.withAlpha(100), - Theme.of(context).colorScheme.surface.withAlpha(150), - Theme.of(context).colorScheme.surface.withAlpha(175), - Theme.of(context).colorScheme.surface.withAlpha(200), - ], + ), ), ), - ), - ], - ), - ); + ], + ), + ); + } + + double getCardBoxHeight() { + final numCards = widget.dashboardViewModel.cardDesigns.length; + final maxCardHeight = MediaQuery.of(context).size.width * 0.878 * (2/3.2); + final overlapAmount = numCards > 3 ? 5.0 : 60.0; + + return maxCardHeight + (numCards-1)*overlapAmount; } void openCustomizer() { @@ -182,16 +218,16 @@ class _NewHomePageState extends State { }, child: accountListViewModel == null ? CardCustomizer( - cryptoTitle: widget.dashboardViewModel.wallet.currency.fullName ?? - widget.dashboardViewModel.wallet.currency.name, - cryptoName: widget.dashboardViewModel.wallet.currency.name, - ) + cryptoTitle: widget.dashboardViewModel.wallet.currency.fullName ?? + widget.dashboardViewModel.wallet.currency.name, + cryptoName: widget.dashboardViewModel.wallet.currency.name, + ) : AccountCustomizer( - accountListViewModel: accountListViewModel!, - accountEditOrCreateViewModel: - getIt.get(), - dashboardViewModel: widget.dashboardViewModel, - ), + accountListViewModel: accountListViewModel!, + accountEditOrCreateViewModel: + getIt.get(), + dashboardViewModel: widget.dashboardViewModel, + ), ), ), ), @@ -200,3 +236,142 @@ class _NewHomePageState extends State { ); } } + +class CardsViewHeaderDelegate extends SliverPersistentHeaderDelegate { + CardsViewHeaderDelegate({ + required this.minHeight, + required this.maxHeight, + required this.minWidth, + required this.maxWidth, + required this.topPadding, + required this.sideWidget, + required this.bottomWidget, + required this.cardsViewBuilder, + }); + + final double minHeight; + final double maxHeight; + final double minWidth; + final double maxWidth; + final double topPadding; + final Widget sideWidget; + final Widget bottomWidget; + final Widget Function(BuildContext, double, bool) cardsViewBuilder; + + @override + Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { + final double scrollRange = maxExtent - minExtent; + final double progress = (shrinkOffset / scrollRange).clamp(0.0, 1.0); + + final double currentCardWidth = maxWidth - (progress * (maxWidth - minWidth)); + + final double fadeThreshold = 0.6; + final double elementsOpacity = + ((progress - fadeThreshold) / (1.0 - fadeThreshold)).clamp(0.0, 1.0); + + return Stack( + children: [ + Positioned( + child: Stack( + children: [ + Positioned( + top:0,bottom:0,left:0,right:0, + child: Opacity( + opacity: progress, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Theme.of(context).colorScheme.surfaceDim.withAlpha(5), + Theme.of(context).colorScheme.surfaceDim.withAlpha(25), + Theme.of(context).colorScheme.surfaceDim.withAlpha(50), + Theme.of(context).colorScheme.surfaceDim.withAlpha(100), + Theme.of(context).colorScheme.surfaceDim.withAlpha(150), + Theme.of(context).colorScheme.surfaceDim.withAlpha(175), + Theme.of(context).colorScheme.surfaceDim.withAlpha(200), + ], + ), + ), + ), + ), + ), + Positioned( + top:topPadding+18, + bottom: 48, + left: 18, + right:18, + child: Opacity( + opacity: progress, + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainer, + borderRadius: BorderRadius.circular(18)), + ), + ), + ), + Positioned( + top: 36*progress, + bottom: 0, + left: minWidth+42, + right: 36, + child: Opacity( + opacity: elementsOpacity, + child: Align( + alignment: Alignment.centerLeft, + child: sideWidget, + ), + ), + ), + Align( + alignment: Alignment(-progress, 0.0), + child: Padding( + padding: EdgeInsets.only( + top: 36*progress, + left: 30 * progress, + ), + child: SizedBox( + width: currentCardWidth, + child: cardsViewBuilder(context, currentCardWidth, progress == 0), + ), + ), + ), + + + ], + ), + ), + + Positioned( + left: 0, + right: 0, + bottom: 0, + height: 36, + child: Opacity( + opacity: elementsOpacity, + child: Center( + child: bottomWidget, + ), + ), + ), + ], + ); + } + + @override + double get maxExtent => maxHeight > minExtent ? maxHeight : minExtent; + + @override + double get minExtent => minHeight + topPadding + 40; + + @override + bool shouldRebuild(covariant CardsViewHeaderDelegate oldDelegate) { + return oldDelegate.maxHeight != maxHeight || + oldDelegate.minHeight != minHeight || + oldDelegate.topPadding != topPadding || + oldDelegate.sideWidget != sideWidget || + oldDelegate.bottomWidget != bottomWidget || + oldDelegate.cardsViewBuilder != cardsViewBuilder; + } +} \ No newline at end of file diff --git a/lib/new-ui/widgets/coins_page/action_row/coin_action_row.dart b/lib/new-ui/widgets/coins_page/action_row/coin_action_row.dart index 9d7b8a0bfd..ce04a0cde6 100644 --- a/lib/new-ui/widgets/coins_page/action_row/coin_action_row.dart +++ b/lib/new-ui/widgets/coins_page/action_row/coin_action_row.dart @@ -7,6 +7,7 @@ import 'package:cake_wallet/main.dart'; import 'package:cake_wallet/new-ui/modal_navigator.dart'; import 'package:cake_wallet/new-ui/pages/send_page.dart'; import 'package:cake_wallet/new-ui/pages/swap_page.dart'; +import 'package:cake_wallet/new-ui/widgets/modern_button.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/utils/feature_flag.dart'; import 'package:cake_wallet/utils/payment_request.dart'; @@ -21,6 +22,126 @@ import '../../../pages/receive_page.dart'; import '../../../pages/scan_page.dart'; import 'coin_action_button.dart'; +class CoinAction { + final String name; + final String iconPath; + final Function(BuildContext, bool) action; + + CoinAction({required this.name, required this.iconPath, required this.action}); + + static final send = CoinAction( + name: S.current.send, + iconPath: "assets/new-ui/send.svg", + action: (context, lightningMode) { + if (FeatureFlag.hasNewUiExtraPages) { + final sendPage = getIt.get( + param1: SendPageParams( + unspentCoinType: lightningMode ? UnspentCoinType.lightning : UnspentCoinType.any, + ), + ); + + CupertinoScaffold.showCupertinoModalBottomSheet( + context: context, + barrierColor: Colors.black.withAlpha(60), + builder: (context) { + return Material( + child: ModalNavigator( + rootPage: sendPage, + parentContext: context, + ), + ); + }, + ); + } else { + Map? args; + if (lightningMode) args = {'coinTypeToSpendFrom': UnspentCoinType.lightning}; + Navigator.of(context).pushNamed(Routes.send, arguments: args); + } + }); + + static final receive = CoinAction( + name: S.current.receive, + iconPath: "assets/new-ui/receive.svg", + action: (context, lightningMode) async { + if (FeatureFlag.hasNewUiExtraPages) { + final page = getIt.get(param1: lightningMode); + CupertinoScaffold.showCupertinoModalBottomSheet( + context: context, + barrierColor: Colors.black.withAlpha(60), + builder: (context) { + return Material(child: ModalNavigator(parentContext: context, rootPage: page)); + }, + ); + } else { + // ToDo: (Konsti) refactor as part of the derivation PR (I hate myself for it) + if (lightningMode) { + await getIt().setAddressType( + bitcoin!.getOptionToType(bitcoin!.getBitcoinLightningReceivePageOption())); + } else { + await getIt() + .setAddressType(bitcoin!.getOptionToType(bitcoin!.getBitcoinSegwitPageOption())); + } + Navigator.of(context).pushNamed(Routes.addressPage); + } + }); + + static final swap = CoinAction( + name: S.current.swap, + iconPath: "assets/new-ui/exchange.svg", + action: (context, lightningMode) { + final page = getIt.get(); + if (FeatureFlag.hasNewUiExtraPages) { + CupertinoScaffold.showCupertinoModalBottomSheet( + context: context, + barrierColor: Colors.black.withAlpha(85), + builder: (context) => FractionallySizedBox( + heightFactor: 0.97, + child: Material( + child: ModalNavigator( + rootPage: page, + parentContext: context, + ))), + ); + } else { + Navigator.of(context).pushNamed(Routes.exchange); + } + }); + + static final scan = CoinAction( + name: S.current.scan, + iconPath: "assets/new-ui/scan.svg", + action: (context, lightningMode) async { + if (FeatureFlag.hasNewUiExtraPages) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) => FractionallySizedBox( + heightFactor: 0.9, + child: ScanPage(), + ), + ); + } else { + final code = await presentQRScanner(context); + + if (code == null || code.isEmpty) return; + + if (SendViewModelBase.isNonZeroAmountLightningInvoice(code) || + OpenCryptoPayService.isOpenCryptoPayQR(code)) { + Navigator.of(context).pushNamed(Routes.send, + arguments: {"paymentRequest": PaymentRequest(code, "", "", "", "")}); + return; + } + + final uri = Uri.tryParse(code); + if (uri == null) return; + rootKey.currentState?.handleDeepLinking(uri); + } + ; + }); + + static final all = [send, receive, swap, scan]; +} + class CoinActionRow extends StatelessWidget { const CoinActionRow({super.key, this.lightningMode = false}); @@ -31,148 +152,44 @@ class CoinActionRow extends StatelessWidget { return Padding( padding: const EdgeInsets.symmetric(horizontal: 18.0), child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - spacing: MediaQuery.of(context).size.width * 0.05, - children: [ - CoinActionButton( - icon: SvgPicture.asset( - "assets/new-ui/send.svg", - colorFilter: ColorFilter.mode( - Theme.of(context).colorScheme.primary, - BlendMode.srcIn, - ), - ), - label: S.of(context).send, - action: () { - if (FeatureFlag.hasNewUiExtraPages) { - final sendPage = getIt.get( - param1: SendPageParams( - unspentCoinType: - lightningMode ? UnspentCoinType.lightning : UnspentCoinType.any, + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + spacing: MediaQuery.of(context).size.width * 0.05, + children: CoinAction.all + .map((item) => CoinActionButton( + icon: SvgPicture.asset( + item.iconPath, + colorFilter: + ColorFilter.mode(Theme.of(context).colorScheme.primary, BlendMode.srcIn), ), - ); - - CupertinoScaffold.showCupertinoModalBottomSheet( - context: context, - barrierColor: Colors.black.withAlpha(60), - builder: (context) { - return Material( - child: ModalNavigator( - rootPage: sendPage, - parentContext: context, - ), - ); - }, - ); - } else { - Map? args; - if (lightningMode) args = {'coinTypeToSpendFrom' : UnspentCoinType.lightning}; - Navigator.of(context).pushNamed(Routes.send, arguments: args); - } - }, - ), - CoinActionButton( - icon: SvgPicture.asset( - "assets/new-ui/receive.svg", - colorFilter: ColorFilter.mode( - Theme.of(context).colorScheme.primary, - BlendMode.srcIn, - ), - ), - label: S.of(context).receive, - action: () async { - if (FeatureFlag.hasNewUiExtraPages) { - final page = getIt.get(param1: lightningMode); - CupertinoScaffold.showCupertinoModalBottomSheet( - context: context, - barrierColor: Colors.black.withAlpha(60), - builder: (context) { - return Material(child: ModalNavigator(parentContext:context,rootPage: page)); - }, - ); - } else { - // ToDo: (Konsti) refactor as part of the derivation PR (I hate myself for it) - if (lightningMode) { - await getIt().setAddressType( - bitcoin!.getOptionToType(bitcoin!.getBitcoinLightningReceivePageOption())); - } else { - await getIt().setAddressType( - bitcoin!.getOptionToType(bitcoin!.getBitcoinSegwitPageOption())); - } - Navigator.of(context).pushNamed(Routes.addressPage); - } - }, - ), - CoinActionButton( - icon: SvgPicture.asset( - "assets/new-ui/exchange.svg", - colorFilter: ColorFilter.mode( - Theme.of(context).colorScheme.primary, - BlendMode.srcIn, - ), - ), - label: S.of(context).swap, - action: () { - final page = getIt.get(); - if (FeatureFlag.hasNewUiExtraPages) { - CupertinoScaffold.showCupertinoModalBottomSheet( - context: context, - barrierColor: Colors.black.withAlpha(85), - builder: (context) => FractionallySizedBox( - heightFactor: 0.97, - child: Material( - child: ModalNavigator( - rootPage: page, - parentContext: context, - ))), - ); - } else { - Navigator.of(context).pushNamed(Routes.exchange); - } - }, - ), - CoinActionButton( - icon: SvgPicture.asset( - "assets/new-ui/scan.svg", - colorFilter: ColorFilter.mode( - Theme.of(context).colorScheme.primary, - BlendMode.srcIn, - ), - ), - label: "Scan", - action: () => _onPressedScan(context), - ), - ], - ), + label: item.name, + action: () => item.action(context, lightningMode))) + .toList()), ); } +} + +class CompactCoinActionRow extends StatelessWidget { + const CompactCoinActionRow({super.key, required this.lightningMode}); + + final bool lightningMode; - Future _onPressedScan(BuildContext context) async { - if (FeatureFlag.hasNewUiExtraPages) { - showModalBottomSheet( - context: context, - isScrollControlled: true, - builder: (context) => FractionallySizedBox( - heightFactor: 0.9, - child: ScanPage(), - ), - ); - } else { - final code = await presentQRScanner(context); - - if (code == null || code.isEmpty) return; - - if (SendViewModelBase.isNonZeroAmountLightningInvoice(code) || - OpenCryptoPayService.isOpenCryptoPayQR(code)) { - Navigator.of(context).pushNamed(Routes.send, - arguments: {"paymentRequest": PaymentRequest(code, "", "", "", "")}); - return; - } - - final uri = Uri.tryParse(code); - if (uri == null) return; - rootKey.currentState?.handleDeepLinking(uri); - }; + @override + Widget build(BuildContext context) { + return Row( + spacing: 20, + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: CoinAction.all + .map((item) => ModernButton.svg( + svgPath: item.iconPath, + size: 36, + iconSize: 20, + onPressed: () => item.action(context, lightningMode), + backgroundColor: Theme.of(context).colorScheme.primary, + iconColor: Theme.of(context).colorScheme.onPrimary, + )) + .toList(), + ); } } diff --git a/lib/new-ui/widgets/coins_page/cards/balance_card.dart b/lib/new-ui/widgets/coins_page/cards/balance_card.dart index 2c97ff5178..1f94c4631f 100644 --- a/lib/new-ui/widgets/coins_page/cards/balance_card.dart +++ b/lib/new-ui/widgets/coins_page/cards/balance_card.dart @@ -24,7 +24,7 @@ class BalanceCard extends StatelessWidget { this.fiatBalance = "", this.assetName = "", this.designSwitchDuration = const Duration(), - this.actions = const [], + this.actions = const [], this.showForeground = true, }); final double width; @@ -36,6 +36,7 @@ class BalanceCard extends StatelessWidget { final String fiatBalance; final String assetName; final bool selected; + final bool showForeground; final CardDesign design; final List actions; final Duration designSwitchDuration; @@ -85,150 +86,154 @@ class BalanceCard extends StatelessWidget { key: ValueKey('svgFullOff'), ), ), - Padding( - padding: EdgeInsets.all(width * 0.05), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - mainAxisSize: MainAxisSize.max, - children: [ - if (showText) - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: [ - if(accountName.isNotEmpty || accountBalance.isNotEmpty) - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - AnimatedDefaultTextStyle( + AnimatedOpacity( + duration: designSwitchDuration, + opacity: showForeground ? 1 : 0, + child: Padding( + padding: EdgeInsets.all(width * 0.05), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, + children: [ + if (showText) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + if(accountName.isNotEmpty || accountBalance.isNotEmpty) + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AnimatedDefaultTextStyle( + duration: designSwitchDuration, + style: Theme.of(context).textTheme.titleMedium!.copyWith( + fontWeight: FontWeight.w500, + color: design.colors.textColor), + child: Text(accountName), + ), + AnimatedOpacity( + opacity: selected ? 0 : 1, + duration: textFadeDuration, + child: Text( + accountBalance, + style: TextStyle(color: design.colors.textColor, fontSize: 14), + ), + ), + ], + ), + AnimatedOpacity( + opacity: selected ? 1 : 0, + duration: textFadeDuration, + child: AnimatedSwitcher( duration: designSwitchDuration, - style: Theme.of(context).textTheme.titleMedium!.copyWith( - fontWeight: FontWeight.w500, - color: design.colors.textColor), - child: Text(accountName), + layoutBuilder: (currentChild, previousChildren) { + return Stack( + alignment: Alignment.centerLeft, + children: [ + ...previousChildren, + if (currentChild != null) currentChild, + ], + ); + }, + child: Row( + key: ValueKey("$balance ${assetName.toUpperCase()}"), + spacing: 8.0, + children: [ + AnimatedDefaultTextStyle( + duration: designSwitchDuration, + style: DefaultTextStyle.of(context) + .style + .copyWith(color: design.colors.textColor, fontSize: 28, fontWeight: FontWeight.w500, letterSpacing: -0.4), + child: Text(balance), + ), + AnimatedDefaultTextStyle( + duration: designSwitchDuration, + style: DefaultTextStyle.of(context) + .style + .copyWith(color: design.colors.textColorSecondary, fontSize: 28, fontWeight: FontWeight.w400, letterSpacing: -0.4), + child: Text(assetName.toUpperCase()), + ), + ], + ), ), - AnimatedOpacity( - opacity: selected ? 0 : 1, - duration: textFadeDuration, + ), + AnimatedDefaultTextStyle( + duration: designSwitchDuration, + style: DefaultTextStyle.of(context).style.copyWith( + fontSize: 15, + fontWeight: FontWeight.w400, + color: design.colors.textColorSecondary), + child: AnimatedSwitcher( + duration: designSwitchDuration, + layoutBuilder: (currentChild, previousChildren) { + return Stack( + alignment: Alignment.centerLeft, + children: [ + ...previousChildren, + if (currentChild != null) currentChild, + ], + ); + }, child: Text( - accountBalance, - style: TextStyle(color: design.colors.textColor, fontSize: 14), + key: ValueKey(fiatBalance), + fiatBalance, ), ), - ], - ), - AnimatedOpacity( - opacity: selected ? 1 : 0, - duration: textFadeDuration, - child: AnimatedSwitcher( - duration: designSwitchDuration, - layoutBuilder: (currentChild, previousChildren) { - return Stack( - alignment: Alignment.centerLeft, - children: [ - ...previousChildren, - if (currentChild != null) currentChild, - ], - ); - }, - child: Row( - key: ValueKey("$balance ${assetName.toUpperCase()}"), - spacing: 8.0, - children: [ - AnimatedDefaultTextStyle( - duration: designSwitchDuration, - style: DefaultTextStyle.of(context) - .style - .copyWith(color: design.colors.textColor, fontSize: 28, fontWeight: FontWeight.w500, letterSpacing: -0.4), - child: Text(balance), - ), - AnimatedDefaultTextStyle( - duration: designSwitchDuration, - style: DefaultTextStyle.of(context) - .style - .copyWith(color: design.colors.textColorSecondary, fontSize: 28, fontWeight: FontWeight.w400, letterSpacing: -0.4), - child: Text(assetName.toUpperCase()), - ), + ), + ], + ) + else + Container(), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + AnimatedSwitcher( + duration: designSwitchDuration, + switchInCurve: Curves.easeInOut, + switchOutCurve: Curves.easeInOut, + layoutBuilder: (currentChild, previousChildren) { + return Stack( + alignment: Alignment.centerLeft, + children: [ + ...previousChildren, + if (currentChild != null) currentChild, ], - ), + ); + }, + child: Row( + key: ValueKey(actions.toString()), + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.start, + children: actions.map(getBalanceCardActionButton).toList(), ), ), - AnimatedDefaultTextStyle( + AnimatedSwitcher( duration: designSwitchDuration, - style: DefaultTextStyle.of(context).style.copyWith( - fontSize: 15, - fontWeight: FontWeight.w400, - color: design.colors.textColorSecondary), - child: AnimatedSwitcher( - duration: designSwitchDuration, - layoutBuilder: (currentChild, previousChildren) { - return Stack( - alignment: Alignment.centerLeft, - children: [ - ...previousChildren, - if (currentChild != null) currentChild, - ], - ); - }, - child: Text( - key: ValueKey(fiatBalance), - fiatBalance, - ), - ), + switchInCurve: Curves.easeInOut, + switchOutCurve: Curves.easeInOut, + child: design.backgroundType == CardDesignBackgroundTypes.svgIcon + ? SvgPicture.asset( + design.imagePath, + key: const ValueKey('svgIcon'), + height: iconWidth, + width: iconWidth, + colorFilter: ColorFilter.mode( + design.colors.backgroundImageColor.withAlpha(80), + BlendMode.srcIn, + ), + ) + : const SizedBox.shrink( + key: ValueKey('svgIconOff'), + ), ), ], - ) - else - Container(), - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - AnimatedSwitcher( - duration: designSwitchDuration, - switchInCurve: Curves.easeInOut, - switchOutCurve: Curves.easeInOut, - layoutBuilder: (currentChild, previousChildren) { - return Stack( - alignment: Alignment.centerLeft, - children: [ - ...previousChildren, - if (currentChild != null) currentChild, - ], - ); - }, - child: Row( - key: ValueKey(actions.toString()), - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.start, - children: actions.map(getBalanceCardActionButton).toList(), - ), - ), - AnimatedSwitcher( - duration: designSwitchDuration, - switchInCurve: Curves.easeInOut, - switchOutCurve: Curves.easeInOut, - child: design.backgroundType == CardDesignBackgroundTypes.svgIcon - ? SvgPicture.asset( - design.imagePath, - key: const ValueKey('svgIcon'), - height: iconWidth, - width: iconWidth, - colorFilter: ColorFilter.mode( - design.colors.backgroundImageColor.withAlpha(80), - BlendMode.srcIn, - ), - ) - : const SizedBox.shrink( - key: ValueKey('svgIconOff'), - ), - ), - ], - ), - ], + ), + ], + ), ), ), ], diff --git a/lib/new-ui/widgets/coins_page/cards/cards_view.dart b/lib/new-ui/widgets/coins_page/cards/cards_view.dart index 0379d55a1a..bb29f4c0dd 100644 --- a/lib/new-ui/widgets/coins_page/cards/cards_view.dart +++ b/lib/new-ui/widgets/coins_page/cards/cards_view.dart @@ -13,7 +13,6 @@ import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_v import 'package:cw_core/card_design.dart'; import 'package:cw_core/unspent_coin_type.dart'; import 'package:cw_core/wallet_type.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -28,12 +27,15 @@ class CardsView extends StatefulWidget { required this.dashboardViewModel, required this.accountListViewModel, required this.lightningMode, - required this.onCompactModeBackgroundCardsTapped}); + required this.onCompactModeBackgroundCardsTapped, + required this.cardWidth, required this.showContent}); final DashboardViewModel dashboardViewModel; final MoneroAccountListViewModel? accountListViewModel; final VoidCallback onCompactModeBackgroundCardsTapped; final bool lightningMode; + final bool showContent; + final double cardWidth; @override _CardsViewState createState() => _CardsViewState(); @@ -56,7 +58,6 @@ class _CardsViewState extends State { static const Duration animDuration = Duration(milliseconds: 200); static const int compactModeTreshold = 4; static const int maxCards = 5; - late final double cardWidth = MediaQuery.of(context).size.width * 0.878; Widget _buildCard(int visualIndex, int realIndex, int numCards, double parentWidth, Map order, bool compactMode, double overlapAmount) { @@ -68,14 +69,12 @@ class _CardsViewState extends State { final top = baseTop - (howFarBehind * overlapAmount); - final left = (parentWidth - cardWidth) / 2.0; return AnimatedPositioned( key: ValueKey("$visualIndex $realIndex"), duration: animDuration, curve: Curves.easeOut, top: top, - left: left, child: AnimatedScale( duration: animDuration, curve: Curves.easeOut, @@ -105,15 +104,12 @@ class _CardsViewState extends State { } final account = widget.accountListViewModel?.accounts[realIndex]; - // The second balance should always be the lightning balance final walletBalanceRecord = widget.dashboardViewModel.balanceViewModel.formattedBalances .elementAtOrNull(widget.lightningMode ? 1 : 0); final walletBalance = walletBalanceRecord?.availableBalance ?? "0"; final walletFiatBalance = walletBalanceRecord?.fiatAvailableBalance ?? "0.00"; - // the card designs is empty if widget gets built before it loads. - // should get populated before user sees anything final CardDesign cardDesign; if (widget.dashboardViewModel.cardDesigns.isEmpty) cardDesign = CardDesign.genericDefault; @@ -153,13 +149,19 @@ class _CardsViewState extends State { ) ]; + final double maxWidth = MediaQuery.of(context).size.width * 0.878; + final double widthFactor = (widget.cardWidth / maxWidth).clamp(0.0, 1.0); + final double radius = 10.0 + (10.0 * widthFactor); + return BalanceCard( - width: cardWidth, + width: widget.cardWidth, accountName: accountName, accountBalance: accountBalance, designSwitchDuration: Duration(milliseconds: 150), assetName: walletBalanceRecord?.formattedAssetTitle ?? "", balance: walletBalance, + borderRadius: radius, + showForeground: widget.showContent, fiatBalance: walletFiatBalance, selected: _selectedIndex == visualIndex, design: cardDesign, @@ -174,7 +176,7 @@ class _CardsViewState extends State { double _getBoxHeight(int numCards, double overlapAmount) { return /* height of initial card */ - (2 / 3.2) * (cardWidth) + + (2 / 3.2) * (widget.cardWidth) + /* height of bg card * amount of bg cards */ overlapAmount * ((numCards) - 1); } @@ -182,10 +184,11 @@ class _CardsViewState extends State { @override Widget build(BuildContext context) { return Observer(builder: (_) { - final parentWidth = MediaQuery.of(context).size.width; + final parentWidth = widget.cardWidth; + final children = []; - int numCards = widget.dashboardViewModel.wallet.type == WalletType.bitcoin + int numCards = widget.dashboardViewModel.wallet.type == WalletType.bitcoin || !widget.showContent ? 1 : widget.dashboardViewModel.cardDesigns.length; if(numCards == 0) numCards = 1; @@ -205,27 +208,19 @@ class _CardsViewState extends State { for (int i = min(numCards - 1, maxCards); i >= 0; i--) { int visualIndex = (_selectedIndex - i + numCards) % numCards; - int realIndex = order[visualIndex]!; - children.add(_buildCard( visualIndex, realIndex, numCards, parentWidth, order, compactMode, overlapAmount)); } - return AnimatedContainer( - duration: Duration(milliseconds: 200), - curve: Curves.easeOut, - width: double.infinity, + return Container( height: _getBoxHeight(numCards, overlapAmount), - child: AnimatedSwitcher( - duration: Duration(milliseconds: 200), - transitionBuilder: (child, animation) => FadeTransition(opacity: animation, child: child), - child: SizedBox( - key: ValueKey(_getBoxHeight(numCards, overlapAmount)), - width: double.infinity, - height: _getBoxHeight(numCards, overlapAmount), - child: Stack(alignment: Alignment.center, children: children), - ), + width: widget.cardWidth, + child: SizedBox( + key: ValueKey(_getBoxHeight(numCards, overlapAmount)), + // height: _getBoxHeight(numCards, overlapAmount), + width: widget.cardWidth, + child: Stack(alignment: Alignment.centerLeft, children: children), ), ); }); @@ -313,4 +308,4 @@ class _CardsViewState extends State { ); } } -} +} \ No newline at end of file diff --git a/lib/new-ui/widgets/coins_page/compact_wallet_header.dart b/lib/new-ui/widgets/coins_page/compact_wallet_header.dart new file mode 100644 index 0000000000..293d7657d9 --- /dev/null +++ b/lib/new-ui/widgets/coins_page/compact_wallet_header.dart @@ -0,0 +1,66 @@ +import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart'; +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart'; +import 'package:flutter/material.dart'; + +class CompactWalletHeader extends StatelessWidget { + const CompactWalletHeader( + {super.key, required this.dashboardViewModel, this.accountListViewModel}); + + final DashboardViewModel dashboardViewModel; + final MoneroAccountListViewModel? accountListViewModel; + + @override + Widget build(BuildContext context) { + final account = accountListViewModel?.accounts.where((item) => item.isSelected).firstOrNull; + + late final String accountName; + if (account == null) { + accountName = ""; + } else { + accountName = account.label; + } + + final walletName = dashboardViewModel.wallet.name; + + final BalanceRecord? record = dashboardViewModel.balanceViewModel.formattedBalances.firstOrNull; + final String balance = record?.availableBalance ?? "0.00"; + final String currency = record?.asset.title ?? ""; + + return Stack( + alignment: Alignment.centerLeft, + children: [ + Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (accountName.isNotEmpty) Text(accountName,style: TextStyle(fontWeight: FontWeight.w500,color: Theme.of(context).colorScheme.primary),), + Text( + walletName, + style: TextStyle( + fontWeight: accountName.isEmpty ? FontWeight.w500 : FontWeight.w400, + color: accountName.isEmpty + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.onSurfaceVariant), + ) + ], + ), + Row( + spacing: 12, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + width: 1, + height: 36, + color: Theme.of(context).colorScheme.surfaceContainerHigh, + ), + Text("$balance $currency") + ], + ), + ], + ); + } +} diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 503251a6d4..be203d8e6d 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -839,6 +839,7 @@ "save_to_downloads": "ﺕﻼﻳﺰﻨﺘﻟﺍ ﻲﻓ ﻆﻔﺣ", "saved_the_trade_id": "لقد تم حفظ معرف العملية", "savings": "الادخار", + "scan": "مسح", "scan_one_block": "مسح كتلة واحدة", "scan_qr_code": "امسح رمز QR ضوئيًا", "scan_qr_code_to_get_address": "امسح ال QR للحصول على العنوان", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index e711beacc7..0964f0b17f 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -839,6 +839,7 @@ "save_to_downloads": "Запазване в Изтегляния", "saved_the_trade_id": "Запазих trade ID-то", "savings": "Спестявания", + "scan": "Сканиране", "scan_one_block": "Сканирайте един блок", "scan_qr_code": "Сканирайте QR кода, за да получите адреса", "scan_qr_code_to_get_address": "Сканирайте QR кода, за да получите адреса", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 3da6624789..85201f0a1f 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -839,6 +839,7 @@ "save_to_downloads": "Uložit do Stažených souborů", "saved_the_trade_id": "Uložil jsem si ID transakce (trade ID)", "savings": "Úspory", + "scan": "Skenovat", "scan_one_block": "Prohledejte jeden blok", "scan_qr_code": "Naskenujte QR kód pro získání adresy", "scan_qr_code_to_get_address": "Prohledejte QR kód a získejte adresu", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 6426dc8134..1d82676e20 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -840,6 +840,7 @@ "save_to_downloads": "Unter „Downloads“ speichern", "saved_the_trade_id": "Ich habe die Handels-ID gespeichert", "savings": "Ersparnisse", + "scan": "Scan", "scan_one_block": "Einen Block scannen", "scan_qr_code": "QR-Code scannen", "scan_qr_code_to_get_address": "Scannen Sie den QR-Code, um die Adresse zu erhalten", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 880e4155fd..cf4f2dc9c7 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -840,6 +840,7 @@ "save_to_downloads": "Save to Downloads", "saved_the_trade_id": "I've saved the trade ID", "savings": "Savings", + "scan": "Scan", "scan_one_block": "Scan one block", "scan_qr_code": "Scan QR code", "scan_qr_code_to_get_address": "Scan the QR code to get the address", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 1924e54faf..be8ba2d38c 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -840,6 +840,7 @@ "save_to_downloads": "Guardar en Descargas", "saved_the_trade_id": "He guardado el ID de la operación", "savings": "Ahorros", + "scan": "Escanear", "scan_one_block": "Escanear un bloque", "scan_qr_code": "Escanear código QR", "scan_qr_code_to_get_address": "Escanea el código QR para obtener la dirección", diff --git a/res/values/strings_fa.arb b/res/values/strings_fa.arb index 2ba07179f4..7688a353c5 100644 --- a/res/values/strings_fa.arb +++ b/res/values/strings_fa.arb @@ -838,6 +838,7 @@ "save_to_downloads": "ذخیره در دانلودها", "saved_the_trade_id": "من شناسه معامله را ذخیره کردم", "savings": "پس انداز", + "scan": "اسکن کنید", "scan_one_block": "اسکن یک بلاک", "scan_qr_code": "اسکن کد QR", "scan_qr_code_to_get_address": "کد QR را برای دریافت آدرس اسکن کنید", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index f428bd4c27..e0757dc551 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -839,6 +839,7 @@ "save_to_downloads": "Enregistrer dans les téléchargements", "saved_the_trade_id": "J'ai sauvegardé l'ID d'échange", "savings": "Économies", + "scan": "Balayage", "scan_one_block": "Scanner un bloc", "scan_qr_code": "Scannez le QR code", "scan_qr_code_to_get_address": "Scannez le QR code pour obtenir l'adresse", diff --git a/res/values/strings_gn.arb b/res/values/strings_gn.arb index 56dbf1ee76..fc62b8158d 100644 --- a/res/values/strings_gn.arb +++ b/res/values/strings_gn.arb @@ -658,6 +658,7 @@ "save_to_downloads": "Ñongatu mboguejýpyre-pe", "saved_the_trade_id": "Añongatu ID ñemuha rehegua", "savings": "Ahorro rehegua .", + "scan": "Escaneo rehegua", "scan_one_block": "escanea peteĩ bloque", "scan_qr_code": "Re-escanea papapy QR", "scan_qr_code_to_get_address": "Escanea pe código QR reikuaa hag̃ua pe dirección", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index c02c097169..e6234abfe1 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -841,6 +841,7 @@ "save_to_downloads": "Ajiye zuwa Zazzagewa", "saved_the_trade_id": "Na ajiye ID na ciniki", "savings": "Adana", + "scan": "Duba", "scan_one_block": "Duba toshe daya", "scan_qr_code": "Gani QR kodin", "scan_qr_code_to_get_address": "Duba lambar QR don samun adireshin", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 28ba7be7ab..e7a160b078 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -841,6 +841,7 @@ "save_to_downloads": "डाउनलोड में सहेजें", "saved_the_trade_id": "मैंने व्यापार बचा लिया है ID", "savings": "बचत", + "scan": "स्कैन", "scan_one_block": "एक ब्लॉक को स्कैन करना", "scan_qr_code": "स्कैन क्यू आर कोड", "scan_qr_code_to_get_address": "पता प्राप्त करने के लिए QR कोड स्कैन करें", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index eb0201dc80..3bf109a576 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -839,6 +839,7 @@ "save_to_downloads": "Spremi u Preuzimanja", "saved_the_trade_id": "Spremio/la sam transakcijski ID", "savings": "Štednja", + "scan": "Skenirati", "scan_one_block": "Skenirajte jedan blok", "scan_qr_code": "Skenirajte QR kod", "scan_qr_code_to_get_address": "Skeniraj QR kod za dobivanje adrese", diff --git a/res/values/strings_hy.arb b/res/values/strings_hy.arb index d9d4c57074..fec6dfdd5f 100644 --- a/res/values/strings_hy.arb +++ b/res/values/strings_hy.arb @@ -838,6 +838,7 @@ "save_to_downloads": "Պահպանել ներբեռնումներում", "saved_the_trade_id": "Ես պահպանել եմ առևտրի ID-ն", "savings": "Խնայողություններ", + "scan": "Սկանավորել", "scan_one_block": "Սկանավորել մեկ բլոկ", "scan_qr_code": "Սկանավորել QR կոդ", "scan_qr_code_to_get_address": "Սկանավորել QR կոդը հասցեն ստանալու համար", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 068ef3545f..9b10afdc97 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -842,6 +842,7 @@ "save_to_downloads": "Simpan ke Unduhan", "saved_the_trade_id": "Saya telah menyimpan ID perdagangan", "savings": "Tabungan", + "scan": "Pindai", "scan_one_block": "Pindai satu blok", "scan_qr_code": "Scan kode QR untuk mendapatkan alamat", "scan_qr_code_to_get_address": "Pindai kode QR untuk mendapatkan alamat", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index fa21c49b89..a47be0afac 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -840,6 +840,7 @@ "save_to_downloads": "Salva in Download", "saved_the_trade_id": "Ho salvato l'ID dello scambio", "savings": "Risparmio", + "scan": "Scansione", "scan_one_block": "Scansionare un blocco", "scan_qr_code": "Scansiona il codice QR", "scan_qr_code_to_get_address": "Scansiona il codice QR per ottenere l'indirizzo", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 0db753094b..b56246384d 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -455,8 +455,8 @@ "field_required": "この項目は必須です", "file_saved": "保存されたファイル", "fill_code": "メールアドレスに記載されている確認コードを入力してください", - "finding_provider": "プロバイダーを探す", "filter_by": "絞り込み", + "finding_provider": "プロバイダーを探す", "first_wallet_text": "Monero、Bitcoin、Ethereum、Litecoin、Haven用の素晴らしいウォレット", "fixed_pair_not_supported": "この固定ペアは、選択したスワップサービスではサポートされていません", "fixed_rate": "固定金利", @@ -840,6 +840,7 @@ "save_to_downloads": "ダウンロードに保存", "saved_the_trade_id": "取引IDを保存しました", "savings": "貯蓄", + "scan": "スキャン", "scan_one_block": "1つのブロックをスキャンします", "scan_qr_code": "QRコードをスキャン", "scan_qr_code_to_get_address": "QRコードをスキャンして住所を取得します", @@ -1246,8 +1247,8 @@ "view_transaction_on": "View Transaction on ", "voting_weight": "投票重み", "waitFewSecondForTxUpdate": "取引履歴に取引が反映されるまで数秒お待ちください。", - "wallet_accounts": "ウォレットアカウント", "wallet": "ウォレット", + "wallet_accounts": "ウォレットアカウント", "wallet_group": "ウォレットグループ", "wallet_group_description_existing_seed": "この財布に既存の種子を使用することを選択しました。確認または書き留める必要がある場合は、シードをもう一度確認できます。", "wallet_group_description_four": "を選択して、全く新しいシードを持つウォレットを作成することもできます。", @@ -1327,4 +1328,4 @@ "zcash_card_missing_funds": "資金が不足していますか?", "zcash_card_scan": "スキャン", "zcash_card_warning": "手順が完了するまでアプリを閉じないでください。閉じた場合、このプロセスを最初からやり直す必要があります。" -} +} \ No newline at end of file diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 9193579747..ef9270188f 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -840,6 +840,7 @@ "save_to_downloads": "다운로드에 저장", "saved_the_trade_id": "거래 ID를 저장했습니다", "savings": "저금", + "scan": "주사", "scan_one_block": "블록 하나 스캔", "scan_qr_code": "QR 코드 스캔", "scan_qr_code_to_get_address": "주소를 얻으려면 QR 코드를 스캔하세요", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 2814786913..ffcb50b016 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -839,6 +839,7 @@ "save_to_downloads": "ဒေါင်းလုဒ်များထံ သိမ်းဆည်းပါ။", "saved_the_trade_id": "ကုန်သွယ်မှု ID ကို သိမ်းဆည်းပြီးပါပြီ။", "savings": "စုဆောင်းငွေ", + "scan": "စကင်န်", "scan_one_block": "တစ်ကွက်ကိုစကင်ဖတ်စစ်ဆေးပါ", "scan_qr_code": "QR ကုဒ်ကို စကင်န်ဖတ်ပါ။", "scan_qr_code_to_get_address": "လိပ်စာရယူရန် QR ကုဒ်ကို စကင်န်ဖတ်ပါ။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index cf3d101ebd..91de075149 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -839,6 +839,7 @@ "save_to_downloads": "Opslaan in downloads", "saved_the_trade_id": "Ik heb de ruil-ID opgeslagen", "savings": "Besparingen", + "scan": "Scannen", "scan_one_block": "Scan een blok", "scan_qr_code": "Scan QR-code", "scan_qr_code_to_get_address": "Scan de QR-code om het adres te krijgen", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 43a94e8534..0b5dc7a920 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -838,6 +838,7 @@ "save_to_downloads": "Zapisz w folderze Pobrane", "saved_the_trade_id": "Zapisałem ID transakcji", "savings": "Oszczędności", + "scan": "Skandować", "scan_one_block": "Zeskanuj jeden blok", "scan_qr_code": "Zeskanuj kod QR", "scan_qr_code_to_get_address": "Zeskanuj kod QR, aby uzyskać adres", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 412f98f856..881b839f2f 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -841,6 +841,7 @@ "save_to_downloads": "Salvar em Downloads", "saved_the_trade_id": "ID da troca salvo", "savings": "Poupança", + "scan": "Digitalizar", "scan_one_block": "Escanear um bloco", "scan_qr_code": "Escanear código QR", "scan_qr_code_to_get_address": "Digitalize o código QR para obter o endereço", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index d5a3e4e939..f16aa03f8c 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -840,6 +840,7 @@ "save_to_downloads": "Сохранить в загрузках", "saved_the_trade_id": "Я сохранил ID сделки", "savings": "Экономия", + "scan": "Сканировать", "scan_one_block": "Сканируйте один блок", "scan_qr_code": "Сканировать QR-код", "scan_qr_code_to_get_address": "Отсканируйте QR-код для получения адреса", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index f907dc19ef..45d3aa28e8 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -839,6 +839,7 @@ "save_to_downloads": "บันทึกลงดาวน์โหลด", "saved_the_trade_id": "ฉันได้บันทึก ID ของการซื้อขายแล้ว", "savings": "ออมทรัพย์", + "scan": "สแกน", "scan_one_block": "สแกนหนึ่งบล็อก", "scan_qr_code": "สแกนรหัส QR", "scan_qr_code_to_get_address": "สแกน QR code เพื่อรับที่อยู่", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 5e1084e088..6cf7debec1 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -839,6 +839,7 @@ "save_to_downloads": "I-save sa mga Pag-download", "saved_the_trade_id": "Nai-save ko na ang trade ID", "savings": "Savings", + "scan": "I-scan", "scan_one_block": "I-scan ang isang bloke", "scan_qr_code": "I-scan ang QR code", "scan_qr_code_to_get_address": "I-scan ang QR code upang makuha ang address", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 2f4f2e0901..ab20b0c22e 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -839,6 +839,7 @@ "save_to_downloads": "İndirilenlere Kaydet", "saved_the_trade_id": "Takas ID'imi kaydettim", "savings": "Tasarruf", + "scan": "Tara", "scan_one_block": "Bir bloğu tara", "scan_qr_code": "QR kodunu tarayın", "scan_qr_code_to_get_address": "Adresi getirmek için QR kodunu tara", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 5a208e9fbe..00438819a6 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -840,6 +840,7 @@ "save_to_downloads": "Зберегти до завантажень", "saved_the_trade_id": "Я зберіг ID операції", "savings": "Економія", + "scan": "Сканувати", "scan_one_block": "Сканувати один блок", "scan_qr_code": "Відскануйте QR-код", "scan_qr_code_to_get_address": "Скануйте QR-код для одержання адреси", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index eb7c8cd834..2621431a40 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -841,6 +841,7 @@ "save_to_downloads": "۔ﮟﯾﺮﮐ ﻅﻮﻔﺤﻣ ﮟﯿﻣ ﺯﮈﻮﻟ ﻥﺅﺍﮈ", "saved_the_trade_id": "میں نے تجارتی ID محفوظ کر لی ہے۔", "savings": "بچت", + "scan": "اسکین کریں۔", "scan_one_block": "ایک بلاک اسکین کریں", "scan_qr_code": "پتہ حاصل کرنے کے لیے QR کوڈ اسکین کریں۔", "scan_qr_code_to_get_address": "پتہ حاصل کرنے کے لئے QR کوڈ کو اسکین کریں", diff --git a/res/values/strings_vi.arb b/res/values/strings_vi.arb index e95dbe90f8..c2ad80b5a8 100644 --- a/res/values/strings_vi.arb +++ b/res/values/strings_vi.arb @@ -837,6 +837,7 @@ "save_to_downloads": "Lưu vào Tải xuống", "saved_the_trade_id": "Tôi đã lưu ID giao dịch", "savings": "Tiết kiệm", + "scan": "Quét", "scan_one_block": "Quét một khối", "scan_qr_code": "Quét mã QR", "scan_qr_code_to_get_address": "Quét mã QR để nhận địa chỉ", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index d7738a67de..563079cb21 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -840,6 +840,7 @@ "save_to_downloads": "Fipamọ si Awọn igbasilẹ", "saved_the_trade_id": "Mo ti pamọ́ àmì ìdánimọ̀ pàṣípààrọ̀", "savings": "Ifowopamọ", + "scan": "Ṣayẹwo", "scan_one_block": "Ọlọjẹ ọkan bulọki", "scan_qr_code": "Yan QR koodu", "scan_qr_code_to_get_address": "Ṣayẹwo koodu QR naa lati gba adirẹsi naa", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 52ec13e6dd..2754eb7277 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -839,6 +839,7 @@ "save_to_downloads": "保存到下载", "saved_the_trade_id": "我已经保存了交易编号", "savings": "储蓄", + "scan": "扫描", "scan_one_block": "扫描一个街区", "scan_qr_code": "扫描二维码", "scan_qr_code_to_get_address": "扫描二维码获取地址", From cf4ea266fa06396e3764e567eb8bc4fa1b70af2e Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Thu, 19 Feb 2026 16:29:30 +0100 Subject: [PATCH 2/6] merge --- .../widgets/coins_page/action_row/coin_action_row.dart | 2 +- lib/new-ui/widgets/coins_page/cards/cards_view.dart | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/new-ui/widgets/coins_page/action_row/coin_action_row.dart b/lib/new-ui/widgets/coins_page/action_row/coin_action_row.dart index d56ad57d2e..9275861899 100644 --- a/lib/new-ui/widgets/coins_page/action_row/coin_action_row.dart +++ b/lib/new-ui/widgets/coins_page/action_row/coin_action_row.dart @@ -155,7 +155,7 @@ class CoinAction { } ; } - }); + ); static final all = [send, receive, swap, scan]; } diff --git a/lib/new-ui/widgets/coins_page/cards/cards_view.dart b/lib/new-ui/widgets/coins_page/cards/cards_view.dart index e3773242ed..8b242e2ec2 100644 --- a/lib/new-ui/widgets/coins_page/cards/cards_view.dart +++ b/lib/new-ui/widgets/coins_page/cards/cards_view.dart @@ -107,6 +107,7 @@ class _CardsViewState extends State { } final account = widget.accountListViewModel?.accounts[realIndex]; + // The second balance should always be the lightning balance final walletBalanceRecord = widget.dashboardViewModel.balanceViewModel.formattedBalances .elementAtOrNull(widget.lightningMode ? 1 : 0); @@ -125,6 +126,8 @@ class _CardsViewState extends State { walletFiatBalance = walletBalanceRecord?.fiatAvailableBalance ?? "${widget.dashboardViewModel.appStore.settingsStore.fiatCurrency.title} 0.00"; } + // the card designs is empty if widget gets built before it loads. + // should get populated before user sees anything final CardDesign cardDesign; if (widget.dashboardViewModel.cardDesigns.isEmpty || realIndex >= widget.dashboardViewModel.cardDesigns.length) @@ -358,4 +361,4 @@ class _CardsViewState extends State { ); } } -} \ No newline at end of file +} From fef5489dfe565e610a258720e1ddbf3088e8c0ac Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Thu, 19 Feb 2026 16:52:51 +0100 Subject: [PATCH 3/6] fix container sizing --- lib/new-ui/pages/home_page.dart | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/new-ui/pages/home_page.dart b/lib/new-ui/pages/home_page.dart index 1736022699..0d8a289ca7 100644 --- a/lib/new-ui/pages/home_page.dart +++ b/lib/new-ui/pages/home_page.dart @@ -300,16 +300,21 @@ class CardsViewHeaderDelegate extends SliverPersistentHeaderDelegate { ), ), Positioned( - top:topPadding+18, - bottom: 48, + top: 36 * progress, + bottom: 0, left: 18, - right:18, + right: 18, child: Opacity( opacity: progress, - child: Container( - decoration: BoxDecoration( + child: Align( + alignment: Alignment.center, + child: Container( + height: 74, + decoration: BoxDecoration( color: Theme.of(context).colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(18)), + borderRadius: BorderRadius.circular(18), + ), + ), ), ), ), From 999964082dbd8c6882272531bd15e0b8a501c0fa Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Thu, 19 Feb 2026 17:00:50 +0100 Subject: [PATCH 4/6] fix showSwap --- lib/new-ui/pages/home_page.dart | 2 +- .../widgets/coins_page/action_row/coin_action_row.dart | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/new-ui/pages/home_page.dart b/lib/new-ui/pages/home_page.dart index 0d8a289ca7..3c48a282c8 100644 --- a/lib/new-ui/pages/home_page.dart +++ b/lib/new-ui/pages/home_page.dart @@ -125,7 +125,7 @@ class _NewHomePageState extends State { delegate: CardsViewHeaderDelegate( maxHeight: getCardBoxHeight(), sideWidget: CompactWalletHeader(dashboardViewModel: widget.dashboardViewModel,accountListViewModel: accountListViewModel,), - bottomWidget: CompactCoinActionRow(lightningMode: _lightningMode), + bottomWidget: CompactCoinActionRow(lightningMode: _lightningMode,showSwap: widget.dashboardViewModel.isEnabledSwapAction), minHeight: 100.0, maxWidth: MediaQuery.of(context).size.width * 0.878, minWidth: 80, diff --git a/lib/new-ui/widgets/coins_page/action_row/coin_action_row.dart b/lib/new-ui/widgets/coins_page/action_row/coin_action_row.dart index 9275861899..ed40244fb5 100644 --- a/lib/new-ui/widgets/coins_page/action_row/coin_action_row.dart +++ b/lib/new-ui/widgets/coins_page/action_row/coin_action_row.dart @@ -174,7 +174,7 @@ class CoinActionRow extends StatelessWidget { mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, spacing: MediaQuery.of(context).size.width * 0.05, - children: CoinAction.all + children: CoinAction.all.where((item) => item != CoinAction.swap || showSwap) .map((item) => CoinActionButton( icon: SvgPicture.asset( item.iconPath, @@ -189,9 +189,11 @@ class CoinActionRow extends StatelessWidget { } class CompactCoinActionRow extends StatelessWidget { - const CompactCoinActionRow({super.key, required this.lightningMode}); + const CompactCoinActionRow({super.key, this.lightningMode = false, this.showSwap = true}); final bool lightningMode; + final bool showSwap; + @override Widget build(BuildContext context) { @@ -199,7 +201,7 @@ class CompactCoinActionRow extends StatelessWidget { spacing: 20, mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, - children: CoinAction.all + children: CoinAction.all.where((item) => item != CoinAction.swap || showSwap) .map((item) => ModernButton.svg( svgPath: item.iconPath, size: 36, From 5129eeb2e2d8160e90471311e1046a26d543bade Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Thu, 19 Feb 2026 17:02:08 +0100 Subject: [PATCH 5/6] fix numCards for btc/ln --- lib/new-ui/pages/home_page.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/new-ui/pages/home_page.dart b/lib/new-ui/pages/home_page.dart index 3c48a282c8..b98a8e60c2 100644 --- a/lib/new-ui/pages/home_page.dart +++ b/lib/new-ui/pages/home_page.dart @@ -16,6 +16,7 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -192,7 +193,9 @@ class _NewHomePageState extends State { } double getCardBoxHeight() { - final numCards = widget.dashboardViewModel.cardDesigns.length; + final numCards = widget.dashboardViewModel.wallet.type == WalletType.bitcoin + ? 1 + : widget.dashboardViewModel.cardDesigns.length; final maxCardHeight = MediaQuery.of(context).size.width * 0.878 * (2/3.2); final overlapAmount = numCards > 3 ? 5.0 : 60.0; From a647532deb0b362aed0c31e35faf824e1f3ced3f Mon Sep 17 00:00:00 2001 From: Robert Malikowski Date: Mon, 2 Mar 2026 13:44:31 +0100 Subject: [PATCH 6/6] simplify design --- lib/new-ui/pages/home_page.dart | 234 +++++------------- .../widgets/coins_page/cards/cards_view.dart | 20 +- .../coins_page/compact_wallet_header.dart | 176 +++++++++---- 3 files changed, 203 insertions(+), 227 deletions(-) diff --git a/lib/new-ui/pages/home_page.dart b/lib/new-ui/pages/home_page.dart index 6af28bbcc2..f201cb2f0a 100644 --- a/lib/new-ui/pages/home_page.dart +++ b/lib/new-ui/pages/home_page.dart @@ -38,17 +38,43 @@ class NewHomePage extends StatefulWidget { class _NewHomePageState extends State { MoneroAccountListViewModel? accountListViewModel; bool _lightningMode = false; + final GlobalKey _cardsViewKey = GlobalKey(); + final ScrollController _scrollController = ScrollController(); + double _triggerOffset = double.infinity; + bool _showHeader = false; @override void initState() { super.initState(); _setAccountViewModel(); + _scrollController.addListener(_onScroll); reaction((_)=>widget.dashboardViewModel.wallet, (_) { _setAccountViewModel(); setState(() { _lightningMode = false; }); }); + + WidgetsBinding.instance.addPostFrameCallback((_) { + _calculateTriggerOffset(); + }); + } + + void _calculateTriggerOffset() { + final RenderBox? renderBox = _cardsViewKey.currentContext?.findRenderObject() as RenderBox?; + if (renderBox != null) { + setState(() { + _triggerOffset = renderBox.size.height; + }); + } + } + + void _onScroll() { + if (_scrollController.offset >= _triggerOffset && !_showHeader) { + setState(() => _showHeader = true); + } else if (_scrollController.offset < _triggerOffset && _showHeader) { + setState(() => _showHeader = false); + } } void _setAccountViewModel() { @@ -74,6 +100,7 @@ class _NewHomePageState extends State { child: Stack( children: [ CustomScrollView( + controller: _scrollController, physics: BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), slivers: [ SliverPadding( @@ -112,40 +139,24 @@ class _NewHomePageState extends State { ), SizedBox(height: 24), Observer( - builder: (_)=>WalletInfoBar( + builder: (_) => WalletInfoBar( lightningMode: _lightningMode, hardwareWalletType: widget.dashboardViewModel.wallet.hardwareWalletType, name: widget.dashboardViewModel.wallet.name, - onCustomizeButtonTap: openCustomizer - ), + onCustomizeButtonTap: openCustomizer), ), SizedBox(height: 24), ], ), ), - Observer( - builder: (_) => SliverPersistentHeader( - pinned: true, - delegate: CardsViewHeaderDelegate( - maxHeight: getCardBoxHeight(), - sideWidget: CompactWalletHeader(dashboardViewModel: widget.dashboardViewModel,accountListViewModel: accountListViewModel,), - bottomWidget: CompactCoinActionRow(lightningMode: _lightningMode,showSwap: widget.dashboardViewModel.isEnabledSwapAction), - minHeight: 100.0, - maxWidth: MediaQuery.of(context).size.width * 0.878, - minWidth: 80, - topPadding: MediaQuery.of(context).padding.top, - cardsViewBuilder: (context, dynamicWidth, showText) { - return CardsView( - cardWidth: dynamicWidth, - showContent: showText, - key: ValueKey(widget.dashboardViewModel.wallet.name), - dashboardViewModel: widget.dashboardViewModel, - accountListViewModel: accountListViewModel, - onCompactModeBackgroundCardsTapped: openCustomizer, - lightningMode: _lightningMode, - ); - }, - ), + SliverToBoxAdapter( + child: CardsView( + key: _cardsViewKey, + showContent: true, + dashboardViewModel: widget.dashboardViewModel, + accountListViewModel: accountListViewModel, + onCompactModeBackgroundCardsTapped: openCustomizer, + lightningMode: _lightningMode, ), ), SliverToBoxAdapter( @@ -162,8 +173,11 @@ class _NewHomePageState extends State { children: [ CoinActionRow( lightningMode: _lightningMode, - showSwap: widget.dashboardViewModel.isEnabledSwapAction,), - MwebAd(dashboardViewModel: widget.dashboardViewModel,), + showSwap: widget.dashboardViewModel.isEnabledSwapAction, + ), + MwebAd( + dashboardViewModel: widget.dashboardViewModel, + ), ], ); }, @@ -199,6 +213,26 @@ class _NewHomePageState extends State { ), ), ), + Align( + alignment: Alignment.topCenter, + child: IgnorePointer( + ignoring: !_showHeader, + child: AnimatedOpacity( + duration: const Duration(milliseconds: 200), + opacity: _showHeader ? 1 : 0, + child: Observer( + builder: (_) => CompactWalletHeader( + onHeaderTapped: () => _scrollController.animateTo(0, + duration: Duration(milliseconds: 300), curve: Curves.easeOutCubic), + dashboardViewModel: widget.dashboardViewModel, + accountListViewModel: accountListViewModel, + lightningMode: _lightningMode, + showSwap: widget.dashboardViewModel.isEnabledSwapAction, + ), + ), + ), + ), + ) ], ), ); @@ -254,148 +288,4 @@ class _NewHomePageState extends State { } widget.dashboardViewModel.loadCardDesigns(); } -} - -class CardsViewHeaderDelegate extends SliverPersistentHeaderDelegate { - CardsViewHeaderDelegate({ - required this.minHeight, - required this.maxHeight, - required this.minWidth, - required this.maxWidth, - required this.topPadding, - required this.sideWidget, - required this.bottomWidget, - required this.cardsViewBuilder, - }); - - final double minHeight; - final double maxHeight; - final double minWidth; - final double maxWidth; - final double topPadding; - final Widget sideWidget; - final Widget bottomWidget; - final Widget Function(BuildContext, double, bool) cardsViewBuilder; - - @override - Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { - final double scrollRange = maxExtent - minExtent; - final double progress = (shrinkOffset / scrollRange).clamp(0.0, 1.0); - - final double currentCardWidth = maxWidth - (progress * (maxWidth - minWidth)); - - final double fadeThreshold = 0.6; - final double elementsOpacity = - ((progress - fadeThreshold) / (1.0 - fadeThreshold)).clamp(0.0, 1.0); - - return Stack( - children: [ - Positioned( - child: Stack( - children: [ - Positioned( - top:0,bottom:0,left:0,right:0, - child: Opacity( - opacity: progress, - child: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - colors: [ - Theme.of(context).colorScheme.surfaceDim.withAlpha(5), - Theme.of(context).colorScheme.surfaceDim.withAlpha(25), - Theme.of(context).colorScheme.surfaceDim.withAlpha(50), - Theme.of(context).colorScheme.surfaceDim.withAlpha(100), - Theme.of(context).colorScheme.surfaceDim.withAlpha(150), - Theme.of(context).colorScheme.surfaceDim.withAlpha(175), - Theme.of(context).colorScheme.surfaceDim.withAlpha(200), - ], - ), - ), - ), - ), - ), - Positioned( - top: 36 * progress, - bottom: 0, - left: 18, - right: 18, - child: Opacity( - opacity: progress, - child: Align( - alignment: Alignment.center, - child: Container( - height: 74, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(18), - ), - ), - ), - ), - ), - Positioned( - top: 36*progress, - bottom: 0, - left: minWidth+42, - right: 36, - child: Opacity( - opacity: elementsOpacity, - child: Align( - alignment: Alignment.centerLeft, - child: sideWidget, - ), - ), - ), - Align( - alignment: Alignment(-progress, 0.0), - child: Padding( - padding: EdgeInsets.only( - top: 36*progress, - left: 30 * progress, - ), - child: SizedBox( - width: currentCardWidth, - child: cardsViewBuilder(context, currentCardWidth, progress == 0), - ), - ), - ), - - - ], - ), - ), - - Positioned( - left: 0, - right: 0, - bottom: 0, - height: 36, - child: Opacity( - opacity: elementsOpacity, - child: Center( - child: bottomWidget, - ), - ), - ), - ], - ); - } - - @override - double get maxExtent => maxHeight > minExtent ? maxHeight : minExtent; - - @override - double get minExtent => minHeight + topPadding + 40; - - @override - bool shouldRebuild(covariant CardsViewHeaderDelegate oldDelegate) { - return oldDelegate.maxHeight != maxHeight || - oldDelegate.minHeight != minHeight || - oldDelegate.topPadding != topPadding || - oldDelegate.sideWidget != sideWidget || - oldDelegate.bottomWidget != bottomWidget || - oldDelegate.cardsViewBuilder != cardsViewBuilder; - } } \ No newline at end of file diff --git a/lib/new-ui/widgets/coins_page/cards/cards_view.dart b/lib/new-ui/widgets/coins_page/cards/cards_view.dart index 013062652d..f011f4569c 100644 --- a/lib/new-ui/widgets/coins_page/cards/cards_view.dart +++ b/lib/new-ui/widgets/coins_page/cards/cards_view.dart @@ -31,20 +31,22 @@ class CardsView extends StatefulWidget { required this.accountListViewModel, required this.lightningMode, required this.onCompactModeBackgroundCardsTapped, - required this.cardWidth, required this.showContent}); + required this.showContent}); final DashboardViewModel dashboardViewModel; final MoneroAccountListViewModel? accountListViewModel; final VoidCallback onCompactModeBackgroundCardsTapped; final bool lightningMode; final bool showContent; - final double cardWidth; + @override _CardsViewState createState() => _CardsViewState(); } class _CardsViewState extends State { + late final cardWidth = MediaQuery.of(context).size.width*0.878; + late int _selectedIndex; @override @@ -171,11 +173,11 @@ class _CardsViewState extends State { : []; final double maxWidth = MediaQuery.of(context).size.width * 0.878; - final double widthFactor = (widget.cardWidth / maxWidth).clamp(0.0, 1.0); + final double widthFactor = (cardWidth / maxWidth).clamp(0.0, 1.0); final double radius = 10.0 + (10.0 * widthFactor); return BalanceCard( - width: widget.cardWidth, + width: cardWidth, accountName: accountName, accountBalance: accountBalance, designSwitchDuration: Duration(milliseconds: 150), @@ -220,7 +222,7 @@ class _CardsViewState extends State { double _getBoxHeight(int numCards, double overlapAmount) { return /* height of initial card */ - (2 / 3.2) * (widget.cardWidth) + + (2 / 3.2) * (cardWidth) + /* height of bg card * amount of bg cards */ overlapAmount * ((numCards) - 1); } @@ -228,7 +230,7 @@ class _CardsViewState extends State { @override Widget build(BuildContext context) { return Observer(builder: (_) { - final parentWidth = widget.cardWidth; + final parentWidth = cardWidth; final children = []; @@ -268,12 +270,12 @@ class _CardsViewState extends State { return Container( height: _getBoxHeight(numCards, overlapAmount), - width: widget.cardWidth, + width: cardWidth, child: SizedBox( key: ValueKey(_getBoxHeight(numCards, overlapAmount)), // height: _getBoxHeight(numCards, overlapAmount), - width: widget.cardWidth, - child: Stack(alignment: Alignment.centerLeft, children: children), + width: cardWidth, + child: Stack(alignment: Alignment.center, children: children), ), ); }); diff --git a/lib/new-ui/widgets/coins_page/compact_wallet_header.dart b/lib/new-ui/widgets/coins_page/compact_wallet_header.dart index 293d7657d9..930d9e7c20 100644 --- a/lib/new-ui/widgets/coins_page/compact_wallet_header.dart +++ b/lib/new-ui/widgets/coins_page/compact_wallet_header.dart @@ -1,66 +1,150 @@ +import 'package:cake_wallet/new-ui/widgets/coins_page/action_row/coin_action_row.dart'; +import 'package:cake_wallet/new-ui/widgets/coins_page/cards/balance_card.dart'; import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart'; +import 'package:cw_core/card_design.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; class CompactWalletHeader extends StatelessWidget { const CompactWalletHeader( - {super.key, required this.dashboardViewModel, this.accountListViewModel}); + {super.key, + required this.dashboardViewModel, + this.accountListViewModel, + required this.lightningMode, + required this.onHeaderTapped, + required this.showSwap}); final DashboardViewModel dashboardViewModel; + final bool lightningMode; + final bool showSwap; + final VoidCallback onHeaderTapped; final MoneroAccountListViewModel? accountListViewModel; @override Widget build(BuildContext context) { - final account = accountListViewModel?.accounts.where((item) => item.isSelected).firstOrNull; + return AbsorbPointer( + absorbing: false, + child: Stack( + children: [ + Positioned.fill( + child: AbsorbPointer( + absorbing: true, + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Theme.of(context).colorScheme.surface.withAlpha(5), + Theme.of(context).colorScheme.surface.withAlpha(25), + Theme.of(context).colorScheme.surface.withAlpha(50), + Theme.of(context).colorScheme.surface.withAlpha(100), + Theme.of(context).colorScheme.surface.withAlpha(150), + Theme.of(context).colorScheme.surface.withAlpha(175), + Theme.of(context).colorScheme.surface.withAlpha(200), + ], + ), + )), + ), + ), + SafeArea( + child: Observer(builder: (context) { + final account = + accountListViewModel?.accounts.where((item) => item.isSelected).firstOrNull; - late final String accountName; - if (account == null) { - accountName = ""; - } else { - accountName = account.label; - } + final String accountName = account == null ? "" : account.label; - final walletName = dashboardViewModel.wallet.name; + final walletName = dashboardViewModel.wallet.name; - final BalanceRecord? record = dashboardViewModel.balanceViewModel.formattedBalances.firstOrNull; - final String balance = record?.availableBalance ?? "0.00"; - final String currency = record?.asset.title ?? ""; + final BalanceRecord? record = + dashboardViewModel.balanceViewModel.formattedBalances.firstOrNull; + final String balance = record?.availableBalance ?? "0.00"; + final String currency = record?.asset.title ?? ""; - return Stack( - alignment: Alignment.centerLeft, - children: [ - Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (accountName.isNotEmpty) Text(accountName,style: TextStyle(fontWeight: FontWeight.w500,color: Theme.of(context).colorScheme.primary),), - Text( - walletName, - style: TextStyle( - fontWeight: accountName.isEmpty ? FontWeight.w500 : FontWeight.w400, - color: accountName.isEmpty - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onSurfaceVariant), - ) - ], - ), - Row( - spacing: 12, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Container( - width: 1, - height: 36, - color: Theme.of(context).colorScheme.surfaceContainerHigh, - ), - Text("$balance $currency") - ], - ), - ], + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.all(12.0), + child: GestureDetector( + onTap: onHeaderTapped, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + color: Theme.of(context).colorScheme.surfaceContainer), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + spacing: 8, + children: [ + BalanceCard( + width: 80, + design: dashboardViewModel.cardDesigns + .elementAtOrNull(account?.id ?? 0) ?? + CardDesign.genericDefault, + borderRadius: 7, + ), + Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (accountName.isNotEmpty) + Text( + accountName, + style: TextStyle( + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.primary), + ), + Text( + walletName, + style: TextStyle( + fontWeight: accountName.isEmpty + ? FontWeight.w500 + : FontWeight.w400, + color: accountName.isEmpty + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.onSurfaceVariant), + ) + ], + ), + ], + ), + Row( + spacing: 12, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + width: 1, + height: 36, + color: Theme.of(context).colorScheme.surfaceContainerHigh, + ), + Text("$balance $currency") + ], + ), + ], + ), + ), + ), + ), + ), + CompactCoinActionRow( + lightningMode: lightningMode, + showSwap: showSwap, + ) + ], + ); + }), + ) + ], + ), ); } }