Merge remote-tracking branch 'origin/main'

This commit is contained in:
OmarHatem
2025-07-29 17:16:18 +03:00
174 changed files with 2039 additions and 682 deletions
@@ -197,9 +197,9 @@ class ConnectDevicePageBodyState extends State<ConnectDevicePageBody> {
Padding(
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
child: Text(
Platform.isIOS
? S.of(context).connect_your_hardware_wallet_ios
: S.of(context).connect_your_hardware_wallet,
Platform.isAndroid
? S.of(context).connect_your_hardware_wallet
: S.of(context).connect_your_hardware_wallet_ios,
style: Theme.of(context)
.textTheme.titleMedium,
textAlign: TextAlign.center,
@@ -174,8 +174,12 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
onPressed: () async {
Navigator.of(context).pushNamed(Routes.exchangeTradeExternalSendPage);
},
color: Theme.of(context).colorScheme.surfaceContainer,
textColor: Theme.of(context).colorScheme.onSecondaryContainer,
color: widget.exchangeTradeViewModel.isSendable
? Theme.of(context).colorScheme.surfaceContainer
: Theme.of(context).colorScheme.primary,
textColor: widget.exchangeTradeViewModel.isSendable
? Theme.of(context).colorScheme.onSecondaryContainer
: Theme.of(context).colorScheme.onPrimary,
),
SizedBox(height: 16),
Observer(
@@ -183,18 +187,19 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
final trade = widget.exchangeTradeViewModel.trade;
final sendingState = widget.exchangeTradeViewModel.sendViewModel.state;
return widget.exchangeTradeViewModel.isSendable &&
!(sendingState is TransactionCommitted)
? LoadingPrimaryButton(
key: ValueKey('exchange_trade_page_send_from_cake_button_key'),
isDisabled: trade.inputAddress == null || trade.inputAddress!.isEmpty,
isLoading: sendingState is IsExecutingState,
onPressed: () => widget.exchangeTradeViewModel.confirmSending(),
text: S.current.send_from_cake_wallet,
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary,
)
: Offstage();
return Offstage(
offstage: !(widget.exchangeTradeViewModel.isSendable &&
!(sendingState is TransactionCommitted)),
child: LoadingPrimaryButton(
key: ValueKey('exchange_trade_page_send_from_cake_button_key'),
isDisabled: trade.inputAddress == null || trade.inputAddress!.isEmpty,
isLoading: sendingState is IsExecutingState,
onPressed: () => widget.exchangeTradeViewModel.confirmSending(),
text: S.current.send_from_cake_wallet,
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary,
),
);
},
),
],
@@ -9,7 +9,9 @@ import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/confirm_sending_bottom_sheet_widget.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/tooltip_bottom_sheet.dart';
import 'package:cake_wallet/src/widgets/gradient_background.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/integrations/deuro_view_model.dart';
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
@@ -37,86 +39,71 @@ class DEuroSavingsPage extends BasePage {
@override
String get title => S.current.deuro_savings;
Widget trailing(BuildContext context) => MergeSemantics(
child: SizedBox(
height: 37,
width: 37,
child: ButtonTheme(
minWidth: double.minPositive,
child: Semantics(
label: "Refresh",
child: TextButton(
style: TextButton.styleFrom(
foregroundColor: Theme.of(context).colorScheme.onSurface,
overlayColor: WidgetStateColor.resolveWith((states) => Colors.transparent),
),
onPressed: _dEuroViewModel.reloadSavingsUserData,
child: Icon(
Icons.refresh,
color: pageIconColor(context),
size: 20,
),
),
),
),
),
);
@override
Widget body(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => _setReactions(context, _dEuroViewModel));
return Container(
width: double.infinity,
child: Column(
children: <Widget>[
Observer(
builder: (_) => SavingsCard(
isDarkTheme: currentTheme.isDark,
interestRate: "${_dEuroViewModel.interestRate}%",
savingsBalance: _dEuroViewModel.savingsBalance,
fiatSavingsBalance: _dEuroViewModel.fiatSavingsBalance,
currency: CryptoCurrency.deuro,
fiatCurrency: _dEuroViewModel.isFiatDisabled ? null : _dEuroViewModel.fiat,
onAddSavingsPressed: () => _onSavingsAdd(context),
onRemoveSavingsPressed: () => _onSavingsRemove(context),
onApproveSavingsPressed: _dEuroViewModel.prepareApproval,
onTooltipPressed: () => _onSavingsTooltipPressed(context),
isEnabled: _dEuroViewModel.isEnabled,
isLoading: _dEuroViewModel.isLoading,
),
),
Observer(
builder: (_) => InterestCardWidget(
isDarkTheme: currentTheme.isDark,
title: S.of(context).deuro_savings_collect_interest,
fiatAccruedInterest: _dEuroViewModel.fiatAccruedInterest,
fiatCurrency: _dEuroViewModel.isFiatDisabled ? null : _dEuroViewModel.fiat,
accruedInterest: _dEuroViewModel.accruedInterest,
onCollectInterest: _dEuroViewModel.prepareCollectInterest,
onReinvestInterest: _dEuroViewModel.prepareReinvestInterest,
onTooltipPressed: () => _onInterestTooltipPressed(context),
isEnabled: _dEuroViewModel.isEnabled,
),
),
Spacer(),
SafeArea(
child: Padding(
padding: EdgeInsets.only(bottom: 30),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InfoChip(
label: S.of(context).deuro_about_deuro,
icon: CupertinoIcons.info,
onPressed: () => _showWelcomeTooltip(context),
return RefreshIndicator(
displacement: responsiveLayoutUtil.screenHeight * 0.1,
onRefresh: _dEuroViewModel.reloadSavingsUserData,
child: CustomScrollView(
slivers: <Widget>[
SliverFillRemaining(
child: SizedBox(
width: double.infinity,
child: Column(
children: <Widget>[
Observer(
builder: (_) => SavingsCard(
isDarkTheme: currentTheme.isDark,
interestRate: "${_dEuroViewModel.interestRateFormated}%",
savingsBalance: _dEuroViewModel.savingsBalanceFormated,
fiatSavingsBalance: _dEuroViewModel.fiatSavingsBalanceFormated,
currency: CryptoCurrency.deuro,
fiatCurrency: _dEuroViewModel.isFiatDisabled ? null : _dEuroViewModel.fiat,
onAddSavingsPressed: () => _onSavingsAdd(context),
onRemoveSavingsPressed: () => _onSavingsRemove(context),
onApproveSavingsPressed: _dEuroViewModel.prepareApproval,
onTooltipPressed: () => _onSavingsTooltipPressed(context),
isEnabled: _dEuroViewModel.isEnabled,
isLoading: _dEuroViewModel.isLoading,
),
),
Observer(
builder: (_) => InterestCardWidget(
isDarkTheme: currentTheme.isDark,
title: S.of(context).deuro_savings_collect_interest,
fiatAccruedInterest: _dEuroViewModel.fiatAccruedInterestFormated,
fiatCurrency: _dEuroViewModel.isFiatDisabled ? null : _dEuroViewModel.fiat,
accruedInterest: _dEuroViewModel.accruedInterestFormated,
onCollectInterest: _onCollectInterest,
onReinvestInterest: _onReinvestInterest,
onTooltipPressed: () => _onInterestTooltipPressed(context),
isEnabled: _dEuroViewModel.isSavingsActionsEnabled,
),
),
Spacer(),
SafeArea(
child: Padding(
padding: EdgeInsets.only(bottom: 30),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
InfoChip(
label: S.of(context).deuro_about_deuro,
icon: CupertinoIcons.info,
onPressed: () => _showWelcomeTooltip(context),
),
SizedBox(width: 10),
InfoChip(
label: S.of(context).website,
icon: CupertinoIcons.globe,
onPressed: () => launchUrlString("https://deuro.com/"),
)
],
),
),
),
SizedBox(width: 10),
InfoChip(
label: S.of(context).website,
icon: CupertinoIcons.globe,
onPressed: () => launchUrlString("https://deuro.com/"),
)
],
),
),
@@ -126,14 +113,36 @@ class DEuroSavingsPage extends BasePage {
);
}
bool _editSheetIsOpen = false;
Future<void> _onSavingsAdd(BuildContext context) async {
if (_editSheetIsOpen) return;
_editSheetIsOpen = true;
final amount = await _showEditBottomSheet(context, isAdding: true);
if (amount != null) _dEuroViewModel.prepareSavingsEdit(amount, true);
_editSheetIsOpen = false;
}
Future<void> _onSavingsRemove(BuildContext context) async {
if (_editSheetIsOpen) return;
_editSheetIsOpen = true;
final amount = await _showEditBottomSheet(context, isAdding: false);
if (amount != null) _dEuroViewModel.prepareSavingsEdit(amount, false);
_editSheetIsOpen = false;
}
Future<void> _onReinvestInterest() async {
if (_editSheetIsOpen) return;
_editSheetIsOpen = true;
await _dEuroViewModel.prepareReinvestInterest();
_editSheetIsOpen = false;
}
Future<void> _onCollectInterest() async {
if (_editSheetIsOpen) return;
_editSheetIsOpen = true;
await _dEuroViewModel.prepareCollectInterest();
_editSheetIsOpen = false;
}
bool _isReactionsSet = false;
@@ -175,10 +184,10 @@ class DEuroSavingsPage extends BasePage {
currency: CryptoCurrency.deuro,
amount: S.of(bottomSheetContext).send_amount,
amountValue: _dEuroViewModel.actionType == DEuroActionType.reinvest
? _dEuroViewModel.accruedInterest
? _dEuroViewModel.accruedInterestFormated
: tx.amountFormatted,
fiatAmountValue: _dEuroViewModel.actionType == DEuroActionType.reinvest
? _dEuroViewModel.fiatAccruedInterest
? _dEuroViewModel.fiatAccruedInterestFormated
: _dEuroViewModel.pendingTransactionFiatAmountFormatted,
fee: S.of(bottomSheetContext).send_estimated_fee,
feeValue: tx.feeFormatted,
@@ -301,11 +310,11 @@ class DEuroSavingsPage extends BasePage {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext bottomSheetContext) => InfoBottomSheet(
builder: (BuildContext bottomSheetContext) => TooltipSheet(
currentTheme: currentTheme,
titleText: title,
titleIconPath: CryptoCurrency.deuro.iconPath,
content: content,
tooltip: content,
footerType: FooterType.doubleActionButton,
doubleActionRightButtonText: S.of(context).close,
rightActionButtonKey: ValueKey('deuro_page_tooltip_dialog_${key}_ok_button_key'),
@@ -358,7 +367,8 @@ class DEuroSavingsPage extends BasePage {
content: S.of(context).deuro_tooltip_no_eth,
singleActionButtonKey: ValueKey('deuro_page_tooltip_dialog_no_eth_ok_button_key'),
singleActionButtonText: S.of(context).close,
onSingleActionButtonPressed: () => Navigator.of(bottomSheetContext).pop(), footerType: FooterType.singleActionButton,
onSingleActionButtonPressed: () => Navigator.of(bottomSheetContext).pop(),
footerType: FooterType.singleActionButton,
),
);
}
@@ -375,7 +385,9 @@ class DEuroSavingsPage extends BasePage {
balanceTitle: isAdding
? S.of(context).deuro_savings_available_to_add
: S.of(context).deuro_savings_available_to_remove,
balance: isAdding ? _dEuroViewModel.accountBalance : _dEuroViewModel.savingsBalance,
balance: isAdding
? _dEuroViewModel.accountBalanceFormated
: _dEuroViewModel.savingsBalanceFormated,
footerType: FooterType.none,
maxHeight: MediaQuery.of(context).size.height * 0.8,
),
@@ -3,6 +3,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/integrations/deuro/widgets/numpad.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cw_core/parse_fixed.dart';
import 'package:flutter/material.dart';
class SavingsEditSheet extends BaseBottomSheet {
@@ -14,7 +15,8 @@ class SavingsEditSheet extends BaseBottomSheet {
super.titleIconPath,
this.balance,
this.balanceTitle,
required super.footerType, required super.maxHeight,
required super.footerType,
required super.maxHeight,
});
@override
@@ -53,8 +55,35 @@ class _SavingsEditBodyState extends State<_SavingsEditBody> {
}
String amount = '0';
bool isValid = false;
final FocusNode _numpadFocusNode = FocusNode();
void _onPressedAll() => setState(() {
amount = widget.balance!;
isValid = _validate();
});
void _onNumberPressed(int i) => setState(() {
amount = amount == '0' ? i.toString() : '${amount}${i}';
isValid = _validate();
});
void _onDeletePressed() => setState(() {
amount = amount.length > 1 ? amount.substring(0, amount.length - 1) : '0';
isValid = _validate();
});
void _onDecimalPressed() => setState(() {
amount = '${amount.replaceAll('.', '')}.';
isValid = _validate();
});
bool _validate() {
final amountBigInt = parseFixed(amount, 18);
final balanceBigInt = parseFixed(widget.balance!, 18);
return balanceBigInt >= amountBigInt && amountBigInt > BigInt.zero;
}
@override
Widget build(BuildContext context) => SafeArea(
child: Column(children: [
@@ -80,18 +109,14 @@ class _SavingsEditBodyState extends State<_SavingsEditBody> {
AssetBalanceRow(
title: widget.balanceTitle!,
amount: widget.balance!,
onAllPressed: () => setState(() => amount = widget.balance!),
onAllPressed: _onPressedAll,
),
],
NumberPad(
focusNode: _numpadFocusNode,
onNumberPressed: (i) => setState(
() => amount = amount == '0' ? i.toString() : '${amount}${i}',
),
onDeletePressed: () => setState(
() => amount = amount.length > 1 ? amount.substring(0, amount.length - 1) : '0',
),
onDecimalPressed: () => setState(() => amount = '${amount.replaceAll('.', '')}.'),
onNumberPressed: _onNumberPressed,
onDeletePressed: _onDeletePressed,
onDecimalPressed: _onDecimalPressed,
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
@@ -101,7 +126,7 @@ class _SavingsEditBodyState extends State<_SavingsEditBody> {
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary,
isLoading: false,
isDisabled: false,
isDisabled: !isValid,
),
)
]),
@@ -32,6 +32,7 @@ class NodeForm extends StatelessWidget {
..setLogin((editingNode!.login ?? ''))
..setSSL((editingNode!.isSSL))
..setTrusted((editingNode!.trusted))
..setIsEnabledForAutoSwitching((editingNode!.isEnabledForAutoSwitching))
..setSocksProxy((editingNode!.useSocksProxy))
..setSocksProxyAddress((editingNode!.socksProxyAddress ?? ''));
}
@@ -153,6 +154,25 @@ class NodeForm extends StatelessWidget {
),
),
SizedBox(height: 10.0),
Padding(
padding: EdgeInsets.only(top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Observer(
builder: (_) => StandardCheckbox(
value: nodeViewModel.isEnabledForAutoSwitching,
borderColor: Theme.of(context).colorScheme.surfaceContainerHighest,
iconColor: Theme.of(context).colorScheme.primary,
onChanged: (value) => nodeViewModel.isEnabledForAutoSwitching = value,
caption: S.current.enable_for_auto_switching,
),
),
],
),
),
SizedBox(height: 10.0),
if (nodeViewModel.hasAuthCredentials) ...[
Row(
children: <Widget>[
@@ -115,7 +115,8 @@ class QRWidget extends StatelessWidget {
),
),
if (addressListViewModel.isPayjoinUnavailable &&
!addressListViewModel.isSilentPayments) ...[
!addressListViewModel.isSilentPayments &&
!addressListViewModel.isCupcake) ...[
GestureDetector(
onTap: () => _onPayjoinInactivePressed(context),
child: Row(
@@ -6,6 +6,7 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/option_tile.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/utils/permission_handler.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
@@ -45,10 +46,6 @@ class _RestoreOptionsBodyState extends State<_RestoreOptionsBody> {
bool isRestoring = false;
bool get _doesSupportHardwareWallets {
if (!DeviceInfo.instance.isMobile) {
return false;
}
if (isMoneroOnly) {
return DeviceConnectionType.supportedConnectionTypes(WalletType.monero, Platform.isIOS)
.isNotEmpty;
@@ -57,13 +54,40 @@ class _RestoreOptionsBodyState extends State<_RestoreOptionsBody> {
return true;
}
String get imageRestoreHWPath =>
widget.themeType == ThemeType.dark
? 'assets/images/restore_hw_dark.png'
: 'assets/images/restore_hw.png';
String get imageRestoreCupcakePath =>
widget.themeType == ThemeType.dark
? 'assets/images/restore_cupcake_dark.png'
: 'assets/images/restore_cupcake.png';
String get imageRestoreQRPath =>
widget.themeType == ThemeType.dark
? 'assets/images/restore_qr_dark.png'
: 'assets/images/restore_qr.png';
String get imageRestoreHotWalletPath =>
widget.themeType == ThemeType.dark
? 'assets/images/restore_hot_wallet_dark.png'
: 'assets/images/restore_hot_wallet.png';
String get imageRestoreBackupPath =>
widget.themeType == ThemeType.dark
? 'assets/images/restore_backup_dark.png'
: 'assets/images/restore_backup.png';
@override
Widget build(BuildContext context) {
final imageColor = Theme.of(context).colorScheme.primary;
final imageLedger = Image.asset('assets/images/hardware_wallet/ledger_nano_x.png',
width: 40, color: imageColor);
final imageSeedKeys = Image.asset('assets/images/restore_wallet_image.png', color: imageColor);
final imageBackup = Image.asset('assets/images/backup.png', color: imageColor);
final imageRestoreHW = Image.asset(imageRestoreHWPath, width: 55);
final imageRestoreCupcake = Image.asset(imageRestoreCupcakePath, width: 55);
final imageRestoreQR = Image.asset(imageRestoreQRPath, width: 55);
final imageSeedKeys = Image.asset(imageRestoreHotWalletPath, width: 55);
final imageRestoreBackup = Image.asset(imageRestoreBackupPath, width: 55);
return Center(
child: Container(
@@ -80,15 +104,16 @@ class _RestoreOptionsBodyState extends State<_RestoreOptionsBody> {
title: S.of(context).restore_title_from_seed_keys,
description: S.of(context).restore_description_from_seed_keys,
),
if (widget.isNewInstall)
if (FeatureFlag.hasBitcoinViewOnly && DeviceInfo.instance.isMobile)
Padding(
padding: EdgeInsets.only(top: 24),
child: OptionTile(
key: ValueKey('restore_options_from_backup_button_key'),
onPressed: () => Navigator.pushNamed(context, Routes.restoreFromBackup),
image: imageBackup,
title: S.of(context).restore_title_from_backup,
description: S.of(context).restore_description_from_backup,
key: ValueKey('restore_options_from_cupcake_button_key'),
onPressed: () => _onScanQRCode(context),
image: imageRestoreCupcake,
title: S.of(context).restore_title_from_cupcake,
description: S.of(context).restore_description_from_cupcake,
tag: S.of(context).new_tag,
),
),
if (_doesSupportHardwareWallets)
@@ -98,25 +123,33 @@ class _RestoreOptionsBodyState extends State<_RestoreOptionsBody> {
key: ValueKey('restore_options_from_hardware_wallet_button_key'),
onPressed: () =>
Navigator.pushNamed(context, Routes.restoreWalletFromHardwareWallet),
image: imageLedger,
image: imageRestoreHW,
title: S.of(context).restore_title_from_hardware_wallet,
description: S.of(context).restore_description_from_hardware_wallet,
),
),
if (widget.isNewInstall)
Padding(
padding: EdgeInsets.only(top: 24),
child: OptionTile(
key: ValueKey('restore_options_from_backup_button_key'),
onPressed: () => Navigator.pushNamed(context, Routes.restoreFromBackup),
image: imageRestoreBackup,
title: S.of(context).restore_title_from_backup,
description: S.of(context).restore_description_from_backup,
),
),
if (DeviceInfo.instance.isMobile)
Padding(
padding: EdgeInsets.only(top: 24),
child: OptionTile(
key: ValueKey('restore_options_from_qr_button_key'),
onPressed: () => _onScanQRCode(context),
icon: Icon(
Icons.qr_code_rounded,
color: imageColor,
size: 50,
),
image: imageRestoreQR,
title: S.of(context).scan_qr_code,
description: S.of(context).cold_or_recover_wallet),
)
description: S.of(context).cold_or_recover_wallet,
),
),
],
),
)),
@@ -198,14 +198,14 @@ class WalletRestoreFromKeysFormState extends State<WalletRestoreFromKeysForm> {
Widget _restoreFromKeysFormFields() {
// Decred can only restore a view only wallet with an account pubkey. Other
// fields are not used.
if (widget.walletRestoreViewModel.type == WalletType.decred) {
if (widget.walletRestoreViewModel.onlyViewKeyRestore) {
return Column(
children: [
BaseTextFormField(
controller: viewKeyController,
hintText: S.of(context).view_key_public,
maxLines: null,
)
),
],
);
}
@@ -253,13 +253,14 @@ class WalletRestoreFromKeysFormState extends State<WalletRestoreFromKeysForm> {
maxLines: null,
),
),
BlockchainHeightWidget(
key: blockchainHeightKey,
hasDatePicker: widget.walletRestoreViewModel.type != WalletType.haven,
onHeightChange: (_) => null,
onHeightOrDateEntered: widget.onHeightOrDateEntered,
walletType: widget.walletRestoreViewModel.type,
),
if (widget.walletRestoreViewModel.hasBlockchainHeightSelector)
BlockchainHeightWidget(
key: blockchainHeightKey,
hasDatePicker: widget.walletRestoreViewModel.type != WalletType.haven,
onHeightChange: (_) => null,
onHeightOrDateEntered: widget.onHeightOrDateEntered,
walletType: widget.walletRestoreViewModel.type,
),
],
);
}
@@ -199,7 +199,7 @@ class WalletRestorePage extends BasePage {
credentials['seed'] =
walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text;
if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) {
if (walletRestoreViewModel.hasBlockchainHeightSelector) {
credentials['height'] =
walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey.currentState?.height ??
-1;
@@ -219,7 +219,7 @@ class WalletRestorePage extends BasePage {
credentials['name'] =
walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
if (walletRestoreViewModel.type != WalletType.decred) {
if (!walletRestoreViewModel.onlyViewKeyRestore) {
credentials['address'] =
walletRestoreFromKeysFormKey.currentState!.addressController.text;
credentials['spendKey'] =
@@ -519,7 +519,7 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
}
},
onViewKeyEntered: (bool entered) {
if (walletRestoreViewModel.type == WalletType.decred) {
if (widget.walletRestoreViewModel.onlyViewKeyRestore) {
walletRestoreViewModel.isButtonEnabled = entered;
}
},
@@ -536,7 +536,7 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
key: widget.walletRestoreFromSeedFormKey,
restoredWallet: walletRestoreViewModel.restoredWallet,
seedSettingsViewModel: widget.seedSettingsViewModel,
displayBlockHeightSelector: widget.walletRestoreViewModel.hasBlockchainHeightLanguageSelector,
displayBlockHeightSelector: widget.walletRestoreViewModel.hasBlockchainHeightSelector,
displayLanguageSelector: widget.walletRestoreViewModel.hasSeedLanguageSelector,
type: widget.walletRestoreViewModel.type,
blockHeightFocusNode: widget.blockHeightFocusNode,
@@ -563,7 +563,7 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
}
void _validateOnChange({bool isPolyseed = false}) {
if (!isPolyseed && walletRestoreViewModel.hasBlockchainHeightLanguageSelector) {
if (!isPolyseed && walletRestoreViewModel.hasBlockchainHeightSelector) {
final hasHeight = walletRestoreFromSeedFormKey
.currentState?.blockchainHeightKey.currentState?.restoreHeightController.text.isNotEmpty;
+8
View File
@@ -2,8 +2,10 @@ import 'dart:async';
import 'dart:io';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/core/node_switching_service.dart';
import 'package:cake_wallet/core/totp_request_details.dart';
import 'package:cake_wallet/core/trade_monitor.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cw_core/utils/print_verbose.dart';
@@ -29,6 +31,7 @@ class Root extends StatefulWidget {
required this.authService,
required this.linkViewModel,
required this.tradeMonitor,
required this.nodeSwitchingService,
}) : super(key: key);
final AuthenticationStore authenticationStore;
@@ -38,6 +41,7 @@ class Root extends StatefulWidget {
final Widget child;
final LinkViewModel linkViewModel;
final TradeMonitor tradeMonitor;
final NodeSwitchingService nodeSwitchingService;
@override
RootState createState() => RootState();
@@ -161,6 +165,10 @@ class RootState extends State<Root> with WidgetsBindingObserver {
}
widget.tradeMonitor.resumeTradeMonitoring();
// Trigger node health check when app resumes
widget.nodeSwitchingService.performHealthCheck();
break;
default:
break;
+2 -1
View File
@@ -450,7 +450,7 @@ class SendPage extends BasePage {
}
if (monero!.needExportOutputs(sendViewModel.wallet, amount)) {
await Navigator.of(context).pushNamed(Routes.urqrAnimatedPage,
arguments: 'export-outputs');
arguments: monero!.exportOutputsUR(sendViewModel.wallet));
await Future.delayed(
Duration(seconds: 1)); // wait for monero to refresh the state
}
@@ -570,6 +570,7 @@ class SendPage extends BasePage {
return ConfirmSendingBottomSheet(
key: ValueKey('send_page_confirm_sending_bottom_sheet_key'),
titleText: S.of(bottomSheetContext).confirm_transaction,
accessibleNavigationModeSlideActionButtonText: S.of(bottomSheetContext).send,
currentTheme: currentTheme,
footerType: FooterType.slideActionButton,
walletType: sendViewModel.walletType,
+1 -1
View File
@@ -258,7 +258,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
!sendViewModel.isBatchSending && sendViewModel.shouldDisplaySendALL,
currencyValueValidator: output.sendAll
? sendViewModel.allAmountValidator
: sendViewModel.amountValidator,
: sendViewModel.amountValidator(output),
allAmountCallback: () async =>
output.setSendAll(await sendViewModel.sendingBalance),
),
@@ -2,6 +2,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/nodes/widgets/node_list_row.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
@@ -34,7 +35,23 @@ class ManageNodesPage extends BasePage {
onTap: (_) async => await Navigator.of(context).pushNamed(Routes.newNode),
),
),
SizedBox(height: 20),
Observer(
builder: (_) => SettingsSwitcherCell(
key: ValueKey('manage_nodes_page_enable_auto_node_switching_button_key'),
title: S.current.enable_auto_node_switching,
value: isPow
? powNodeListViewModel!.enableAutomaticNodeSwitching
: nodeListViewModel!.enableAutomaticNodeSwitching,
onValueChange: (BuildContext context, bool value) {
if (isPow) {
powNodeListViewModel!.setEnableAutomaticNodeSwitching(value);
} else {
nodeListViewModel!.setEnableAutomaticNodeSwitching(value);
}
},
),
),
SizedBox(height: 8),
Observer(
builder: (BuildContext context) {
int itemsCount =
@@ -36,22 +36,22 @@ class OtherSettingsPage extends BasePage {
if (_otherSettingsViewModel.displayTransactionPriority)
_otherSettingsViewModel.walletType == WalletType.bitcoin ?
SettingsPriorityPickerCell(
title: S.current.settings_fee_priority,
items: priorityForWalletType(_otherSettingsViewModel.walletType),
displayItem: _otherSettingsViewModel.getDisplayBitcoinPriority,
selectedItem: _otherSettingsViewModel.transactionPriority,
customItemIndex: _otherSettingsViewModel.customPriorityItemIndex,
onItemSelected: _otherSettingsViewModel.onDisplayBitcoinPrioritySelected,
customValue: _otherSettingsViewModel.customBitcoinFeeRate,
maxValue: _otherSettingsViewModel.maxCustomFeeRate?.toDouble(),
title: S.current.settings_fee_priority,
items: priorityForWalletType(_otherSettingsViewModel.walletType),
displayItem: _otherSettingsViewModel.getDisplayBitcoinPriority,
selectedItem: _otherSettingsViewModel.transactionPriority,
customItemIndex: _otherSettingsViewModel.customPriorityItemIndex,
onItemSelected: _otherSettingsViewModel.onDisplayBitcoinPrioritySelected,
customValue: _otherSettingsViewModel.customBitcoinFeeRate,
maxValue: _otherSettingsViewModel.maxCustomFeeRate?.toDouble(),
) :
SettingsPickerCell(
title: S.current.settings_fee_priority,
items: priorityForWalletType(_otherSettingsViewModel.walletType),
displayItem: _otherSettingsViewModel.getDisplayPriority,
selectedItem: _otherSettingsViewModel.transactionPriority,
onItemSelected: _otherSettingsViewModel.onDisplayPrioritySelected,
),
title: S.current.settings_fee_priority,
items: priorityForWalletType(_otherSettingsViewModel.walletType),
displayItem: _otherSettingsViewModel.getDisplayPriority,
selectedItem: _otherSettingsViewModel.transactionPriority,
onItemSelected: _otherSettingsViewModel.onDisplayPrioritySelected,
),
if (_otherSettingsViewModel.changeRepresentativeEnabled)
SettingsCellWithArrow(
title: S.current.change_rep,
+85 -37
View File
@@ -1,5 +1,7 @@
import 'dart:async';
import 'package:bbqrdart/bbqrdart.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/qr_scanner.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/monero/monero.dart';
@@ -7,31 +9,49 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/animated_ur_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// ur:xmr-txunsigned - unsigned transaction
// should show a scanner afterwards.
class AnimatedURPage extends BasePage {
final bool isAll;
AnimatedURPage(this.animatedURmodel, {required String urQr, this.isAll = false}) {
if (urQr == "export-outputs") {
this.urQr = monero!.exportOutputsUR(animatedURmodel.wallet, false);
} else if (urQr == "export-outputs-all") {
this.urQr = monero!.exportOutputsUR(animatedURmodel.wallet, true);
} else {
this.urQr = urQr;
AnimatedURPage(this.animatedURmodel, {
required Map<String, String> this.urQr,
this.isAll = false,
}) {
if (urQr.keys.first.startsWith("export-outputs") && animatedURmodel.wallet.type == WalletType.monero) {
if (animatedURmodel.wallet.type == WalletType.monero) {
urQr = monero!.exportOutputsUR(animatedURmodel.wallet);
} else {
throw UnimplementedError("unable to handle UR: ${urQr.keys.first}");
}
}
for (var key in urQr.keys) {
if (key.isEmpty || urQr[key]!.isEmpty) {
urQr.remove(key);
continue;
}
urQr[key] = urQr[key]!.trim();
}
}
late String urQr;
Map<String, String> urQr = {};
final AnimatedURModel animatedURmodel;
String get urQrType {
final first = urQr.trim().split("\n")[0];
if (urQr.values.first.trim().substring(0, 2) == BBQR.header) {
return BBQR.header;
}
if (urQr.isEmpty) return "unknown";
final first = urQr.values.first.trim().split("\n")[0];
return first.split('/')[0];
}
@@ -43,11 +63,11 @@ class AnimatedURPage extends BasePage {
Padding(
padding: const EdgeInsets.only(top: 64.0),
child: URQR(
frames: urQr.trim().split("\n"),
urqr: urQr,
),
),
if (["ur:xmr-txunsigned", "ur:xmr-output", "ur:psbt", BBQR.header].contains(urQrType)) ...{
SizedBox(height: 32),
if (urQrType == "ur:xmr-txunsigned" || urQrType == "ur:xmr-output")
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: SizedBox(
@@ -60,30 +80,8 @@ class AnimatedURPage extends BasePage {
),
),
),
SizedBox(height: 32),
if (urQrType == "ur:xmr-output" && !isAll) Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: SizedBox(
width: double.maxFinite,
child: PrimaryButton(
onPressed: () => _exportAll(context),
text: "Export all",
color: Theme.of(context).colorScheme.secondary,
textColor: Theme.of(context).colorScheme.onPrimary,
),
),
),
],
);
}
void _exportAll(BuildContext context) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) {
return AnimatedURPage(animatedURmodel, urQr: "export-outputs-all", isAll: true);
},
),
],
);
}
@@ -106,6 +104,11 @@ class AnimatedURPage extends BasePage {
Navigator.of(context).pop(true);
}
break;
case "ur:psbt": // psbt
final ur = await presentQRScanner(context);
if (ur == null) return;
await bitcoin!.commitPsbtUR(animatedURmodel.wallet, ur.trim().split("\n"));
Navigator.of(context).pop(true);
default:
throw UnimplementedError("unable to handle UR: ${urQrType}");
}
@@ -124,9 +127,9 @@ class AnimatedURPage extends BasePage {
}
class URQR extends StatefulWidget {
URQR({super.key, required this.frames});
URQR({super.key, required this.urqr});
List<String> frames;
final Map<String, String> urqr;
@override
// ignore: library_private_types_in_public_api
@@ -160,6 +163,24 @@ class _URQRState extends State<URQR> {
super.dispose();
}
late String selected = (widget.urqr.isEmpty) ? "unknown" : widget.urqr.keys.first;
int selectedInt = 0;
List<String> get frames {
return widget.urqr[selected]?.split("\n") ?? [];
}
late String nextLabel = widget.urqr.keys.toList()[(selectedInt + 1) % widget.urqr.length] ;
void next() {
final keys = widget.urqr.keys.toList();
setState(() {
selectedInt++;
nextLabel = keys[(selectedInt + 1) % keys.length];
selected = keys[(selectedInt) % keys.length];
});
}
@override
Widget build(BuildContext context) {
return Column(
@@ -168,10 +189,37 @@ class _URQRState extends State<URQR> {
children: [
Center(
child: QrImage(
data: widget.frames[frame % widget.frames.length], version: -1,
data: frames[frame % frames.length],
version: -1,
size: 400,
),
),
if (widget.urqr.values.length > 1)
SizedBox(
width: double.maxFinite,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: SizedBox(
width: double.maxFinite,
child: PrimaryButton(
onPressed: next,
text: nextLabel,
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary,
),
),
),
),
if (FeatureFlag.hasDevOptions) ...{
TextButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: """Current frame (${frame % frames.length}): ${frames[frame % frames.length]},
All frames:
- ${frames.join("\n - ")}"""));
},
child: Text("[dev] copy debug info"),
),
}
],
);
}
@@ -82,7 +82,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
showLegacySeedTab = widget.walletKeysViewModel.legacySeedSplit.isNotEmpty;
isLegacySeedOnly = widget.walletKeysViewModel.isLegacySeedOnly;
final totalTabs = 1 + (showKeyTab ? 1 : 0) + (showLegacySeedTab ? 1 : 0);
final totalTabs = (_hasSeeds ? 1 : 0) + (showKeyTab ? 1 : 0) + (showLegacySeedTab ? 1 : 0);
_tabController = TabController(length: totalTabs, vsync: this);
}
@@ -126,7 +126,7 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
dividerColor: Colors.transparent,
padding: EdgeInsets.zero,
tabs: [
Tab(text: S.of(context).widgets_seed, key: ValueKey('wallet_keys_page_seed')),
if (_hasSeeds) Tab(text: S.of(context).widgets_seed, key: ValueKey('wallet_keys_page_seed')),
if (showKeyTab) Tab(text: S.of(context).keys, key: ValueKey('wallet_keys_page_keys'),),
if (showLegacySeedTab) Tab(text: S.of(context).legacy, key: ValueKey('wallet_keys_page_seed_legacy')),
],
@@ -137,10 +137,11 @@ class _WalletKeysPageBodyState extends State<WalletKeysPageBody>
child: TabBarView(
controller: _tabController,
children: [
Padding(
padding: const EdgeInsets.only(left: 22, right: 22),
child: _buildSeedTab(context, false),
),
if (_hasSeeds)
Padding(
padding: const EdgeInsets.only(left: 22, right: 22),
child: _buildSeedTab(context, false),
),
if (showKeyTab)
Padding(
padding: const EdgeInsets.only(left: 22, right: 22),
@@ -1,9 +1,11 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/rounded_icon_button.dart';
import 'package:cake_wallet/utils/image_utill.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class CakePayTransactionSentBottomSheet extends StatelessWidget {
const CakePayTransactionSentBottomSheet({
@@ -39,18 +41,36 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
final String paymentId;
final String paymentIdValue;
TextStyle _titleStyle(BuildContext ctx) => Theme.of(ctx).textTheme.bodyLarge!;
TextStyle _titleStyle(BuildContext ctx) =>
Theme
.of(ctx)
.textTheme
.bodyLarge!;
TextStyle _valueStyle(BuildContext ctx) => Theme.of(ctx).textTheme.titleMedium!.copyWith(
TextStyle _valueStyle(BuildContext ctx) =>
Theme
.of(ctx)
.textTheme
.titleMedium!
.copyWith(
fontSize: 18,
fontWeight: FontWeight.w600,
);
TextStyle _subStyle(BuildContext ctx) => Theme.of(ctx).textTheme.labelSmall!.copyWith(
color: Theme.of(ctx).colorScheme.onSurfaceVariant,
TextStyle _subStyle(BuildContext ctx) =>
Theme
.of(ctx)
.textTheme
.labelSmall!
.copyWith(
color: Theme
.of(ctx)
.colorScheme
.onSurfaceVariant,
);
Widget _buildHeader(BuildContext ctx) => Column(
Widget _buildHeader(BuildContext ctx) =>
Column(
children: [
const SizedBox(height: 12),
Container(
@@ -58,7 +78,10 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
height: 5,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: Theme.of(ctx).colorScheme.onSurface,
color: Theme
.of(ctx)
.colorScheme
.onSurface,
),
),
const SizedBox(height: 20),
@@ -69,11 +92,15 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
const SizedBox(width: 6),
Text(
titleText,
style: Theme.of(ctx).textTheme.titleLarge!.copyWith(
fontSize: 20,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none,
),
style: Theme
.of(ctx)
.textTheme
.titleLarge!
.copyWith(
fontSize: 20,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none,
),
),
],
),
@@ -81,7 +108,8 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
],
);
Widget _buildBody(BuildContext context) => Padding(
Widget _buildBody(BuildContext context) =>
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Column(
children: [
@@ -112,25 +140,49 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
itemSubTitleStyle: _subStyle(context),
imagePath: output.parsedAddress.profileImageUrl,
),
const SizedBox(height: 8),
_StandardTile(
itemTitle: paymentId + ': \n' + paymentIdValue,
titleStyle: _titleStyle(context).copyWith(
fontSize: 14
),
itemValue: '',
itemValueStyle: _valueStyle(context),
copyButton: true,
copyButtonOnPressed: () async =>
await Clipboard.setData(ClipboardData(text: paymentIdValue)),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Column(
children: [
Text(paymentId + ': ' + paymentIdValue,
style: Theme.of(context).textTheme.bodyLarge, textAlign: TextAlign.center),
const SizedBox(height: 18),
Image.asset('assets/images/envelope.png'),
const SizedBox(height: 18),
Text(
S.of(context).cake_pay_card_email_delivered_message,
style: Theme.of(context).textTheme.bodyLarge,
S
.of(context)
.cake_pay_card_email_delivered_message,
style: Theme
.of(context)
.textTheme
.bodyLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
PrimaryButton(
text: S.of(context).close,
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimaryContainer,
text: S
.of(context)
.close,
color: Theme
.of(context)
.colorScheme
.primary,
textColor: Theme
.of(context)
.colorScheme
.onPrimaryContainer,
onPressed: onClose,
),
],
@@ -142,12 +194,18 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
@override
Widget build(BuildContext context) {
final maxHeight = MediaQuery.of(context).size.height * 0.9;
final maxHeight = MediaQuery
.of(context)
.size
.height * 0.9;
return ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(30)),
child: Material(
color: Theme.of(context).colorScheme.surface,
color: Theme
.of(context)
.colorScheme
.surface,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: maxHeight),
child: SingleChildScrollView(
@@ -174,6 +232,8 @@ class _StandardTile extends StatelessWidget {
this.itemSubTitle,
this.itemSubTitleStyle,
this.imagePath,
this.copyButton = false,
this.copyButtonOnPressed,
});
final String itemTitle;
@@ -183,6 +243,8 @@ class _StandardTile extends StatelessWidget {
final String? itemSubTitle;
final TextStyle? itemSubTitleStyle;
final String? imagePath;
final bool copyButton;
final VoidCallback? copyButtonOnPressed;
@override
Widget build(BuildContext context) {
@@ -193,12 +255,17 @@ class _StandardTile extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Theme.of(context).colorScheme.surfaceContainerLowest.withAlpha(80)),
color: Theme
.of(context)
.colorScheme
.surfaceContainerLowest
.withAlpha(80)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
mainAxisAlignment: copyButton ? MainAxisAlignment.spaceBetween : MainAxisAlignment.start,
children: [
if (imagePath != null)
Padding(
@@ -216,10 +283,19 @@ class _StandardTile extends StatelessWidget {
itemTitle,
style: titleStyle,
overflow: TextOverflow.ellipsis,
maxLines: 1,
softWrap: false,
maxLines: 2,
softWrap: true,
)),
),
SizedBox(width: 8),
if (copyButton)
RoundedIconButton(
icon: Icons.copy_all_outlined,
onPressed: copyButtonOnPressed ?? () {},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
),
],
),
),
@@ -1,5 +1,5 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/standard_slide_button_widget.dart';
import 'package:cake_wallet/src/widgets/rounded_icon_button.dart';
import 'package:cake_wallet/themes/core/material_base_theme.dart';
import 'package:cake_wallet/themes/utils/custom_theme_colors.dart';
import 'package:cake_wallet/utils/address_formatter.dart';
@@ -10,6 +10,7 @@ import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'base_bottom_sheet_widget.dart';
@@ -45,7 +46,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
_currentTheme = currentTheme,
super(
titleText: titleText,
maxHeight: 900,
maxHeight: 900,
titleIconPath: titleIconPath,
currentTheme: currentTheme,
footerType: footerType,
@@ -115,6 +116,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
itemSubTitleTextStyle: itemSubTitleTextStyle,
tileBackgroundColor: tileBackgroundColor,
applyAddressFormatting: false,
copyButton: true,
)),
),
if (explanation != null)
@@ -307,20 +309,20 @@ class StandardInfoTile extends StatelessWidget {
}
class AddressTile extends StatelessWidget {
const AddressTile({
super.key,
required this.itemTitle,
required this.itemTitleTextStyle,
required this.address,
required this.itemSubTitleTextStyle,
required this.tileBackgroundColor,
required this.walletType,
this.amountTextStyle,
this.applyAddressFormatting = true,
this.imagePath,
this.amount,
this.itemSubTitle,
});
const AddressTile(
{super.key,
required this.itemTitle,
required this.itemTitleTextStyle,
required this.address,
required this.itemSubTitleTextStyle,
required this.tileBackgroundColor,
required this.walletType,
this.amountTextStyle,
this.applyAddressFormatting = true,
this.imagePath,
this.amount,
this.itemSubTitle,
this.copyButton = false});
final String itemTitle;
final TextStyle itemTitleTextStyle;
@@ -333,6 +335,7 @@ class AddressTile extends StatelessWidget {
final bool applyAddressFormatting;
final String? imagePath;
final String? itemSubTitle;
final bool copyButton;
@override
Widget build(BuildContext context) {
@@ -387,9 +390,24 @@ class AddressTile extends StatelessWidget {
fontWeight: FontWeight.w600,
decoration: TextDecoration.none,
))
: Text(
address,
style: itemTitleTextStyle,
: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(address,
style: copyButton
? itemTitleTextStyle.copyWith(fontSize: 12)
: itemTitleTextStyle),
SizedBox(width: 8),
if (copyButton)
RoundedIconButton(
icon: Icons.copy_all_outlined,
onPressed: () async =>
await Clipboard.setData(ClipboardData(text: address)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
),
],
),
itemSubTitle == null
? Container()
@@ -0,0 +1,46 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
import 'package:flutter/material.dart';
class TooltipSheet extends BaseBottomSheet {
final String tooltip;
const TooltipSheet({
required super.currentTheme,
required super.titleText,
super.titleIconPath,
required this.tooltip,
required super.footerType,
super.maxHeight = 900,
super.singleActionButtonText,
super.onSingleActionButtonPressed,
super.singleActionButtonKey,
super.doubleActionLeftButtonText,
super.doubleActionRightButtonText,
super.onLeftActionButtonPressed,
super.onRightActionButtonPressed,
super.leftActionButtonKey,
super.rightActionButtonKey,
});
@override
Widget contentWidget(BuildContext context) => Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Theme.of(context).colorScheme.surfaceContainer),
margin: const EdgeInsets.all(12),
padding: const EdgeInsets.all(12),
child: AutoSizeText(
tooltip,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.onSurfaceVariant,
decoration: TextDecoration.none,
),
),
);
Widget footerWidget(BuildContext context) => SizedBox.shrink();
}
+20 -9
View File
@@ -7,6 +7,7 @@ class OptionTile extends StatelessWidget {
this.icon,
required this.title,
required this.description,
this.tag,
super.key,
}) : assert(image != null || icon != null);
@@ -15,6 +16,7 @@ class OptionTile extends StatelessWidget {
final Icon? icon;
final String title;
final String description;
final String? tag;
@override
Widget build(BuildContext context) {
@@ -36,25 +38,34 @@ class OptionTile extends StatelessWidget {
icon ?? image!,
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 16),
padding: EdgeInsets.only(left: 20),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
title,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.onSurface,
),
Row(
children: [
ConstrainedBox(
constraints: BoxConstraints(maxWidth: 220),
child: Text(title, style: Theme.of(context).textTheme.titleMedium),
),
if (tag != null) Container(
decoration: BoxDecoration(
border: Border.all(),
borderRadius: BorderRadius.circular(20),
color: Theme.of(context).colorScheme.onSurfaceVariant
),
padding: EdgeInsets.symmetric(horizontal: 5),
margin: EdgeInsets.only(left: 5),
child: Text(tag!))
],
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
description,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
+38
View File
@@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
class RoundedIconButton extends StatelessWidget {
const RoundedIconButton(
{this.icon,
this.iconWidget,
required this.onPressed,
this.shape,
this.width,
this.height,
this.iconSize,
this.fillColor});
final IconData? icon;
final Widget? iconWidget;
final VoidCallback onPressed;
final ShapeBorder? shape;
final double? width;
final double? height;
final double? iconSize;
final Color? fillColor;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return RawMaterialButton(
onPressed: onPressed,
fillColor: fillColor ?? colorScheme.surfaceContainerHighest,
elevation: 0,
constraints: BoxConstraints.tightFor(width: width ?? 30, height: height ?? 30),
padding: EdgeInsets.zero,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: shape ?? const CircleBorder(),
child: icon != null
? Icon(icon, size: iconSize ?? 14, color: colorScheme.onSurface)
: iconWidget);
}
}
+1 -1
View File
@@ -45,7 +45,7 @@ class SettingActions {
name: (context) => S.of(context).export_outputs,
image: 'assets/images/monero_menu.png',
onTap: (BuildContext context) {
Navigator.of(context).pushNamed(Routes.urqrAnimatedPage, arguments: 'export-outputs');
Navigator.of(context).pushNamed(Routes.urqrAnimatedPage, arguments: {'export-outputs': 'export-outputs'});
},
);