CW-1069-implement-deuro-dapp-support (#2304)

* feat: started dEuro Savings integration

* fix: merge conflict regarding theming

* feat: Add dEuro Savings Screen

* feat: Change DEuro Savings UI

* feat: Complete DEuro Savings integration with UI enhancements and transaction support

* style: remove forgotten print statements

* feat: localize dEuro subtitle

* feat: add approval flow and priority handling to DEuro Savings integration

- Introduced approval flow for DEuro Savings to enable token approvals.
- Added priority handling for deposit and withdrawal operations.
- Updated UI to support approval state and interactions.
- Localized new strings for multiple languages.
- Enhanced transaction handling with separate approval and commit actions.

* feat: add support for ERC20 token approval transactions

- Introduced `signApprovalTransaction` and `createApprovalTransaction` methods.
- Added handling for infinite approvals.
- Implemented encoding for approval transaction data.
- Enhanced transaction creation flow with approval-specific functionality.

* Update UI

* feat: enhance DEuro Savings logic and UI with computed property and fix gradient background

* feat: localize transaction confirmation content for DEuro Savings

* feat: enable interest collection for DEuro Savings with localized support

* fix reformatting [skip ci]

---------

Co-authored-by: tuxsudo <tuxsudo@tux.pizza>
Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
This commit is contained in:
Konstantin Ullrich
2025-06-19 05:37:41 +03:00
committed by GitHub
co-authored by tuxsudo OmarHatem
parent 150becb679
commit edaf485993
49 changed files with 2081 additions and 66 deletions
@@ -5,15 +5,16 @@ import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/dashboard_card_widget.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/view_model/dashboard/cake_features_view_model.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
class CakeFeaturesPage extends StatelessWidget {
CakeFeaturesPage({required this.dashboardViewModel, required this.cakeFeaturesViewModel});
CakeFeaturesPage(
{required this.dashboardViewModel, required this.cakeFeaturesViewModel});
final DashboardViewModel dashboardViewModel;
final CakeFeaturesViewModel cakeFeaturesViewModel;
@@ -58,6 +59,23 @@ class CakeFeaturesPage extends StatelessWidget {
fit: BoxFit.cover,
),
),
if (dashboardViewModel.type == WalletType.ethereum) ...[
DashBoardRoundedCardWidget(
isDarkTheme: dashboardViewModel.isDarkTheme,
shadowBlur: dashboardViewModel.getShadowBlur(),
shadowSpread: dashboardViewModel.getShadowSpread(),
onTap: () =>
Navigator.of(context).pushNamed(Routes.dEuroSavings),
title: S.of(context).deuro_savings,
subTitle: S.of(context).deuro_savings_subtitle,
image: Image.asset(
'assets/images/deuro_icon.png',
height: 80,
width: 80,
fit: BoxFit.cover,
),
),
],
DashBoardRoundedCardWidget(
isDarkTheme: dashboardViewModel.isDarkTheme,
shadowBlur: dashboardViewModel.getShadowBlur(),
@@ -0,0 +1,197 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/integrations/deuro/widgets/interest_card_widget.dart';
import 'package:cake_wallet/src/screens/integrations/deuro/widgets/savings_card_widget.dart';
import 'package:cake_wallet/src/screens/integrations/deuro/widgets/savings_edit_sheet.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/gradient_background.dart';
import 'package:cake_wallet/view_model/integrations/deuro_view_model.dart';
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
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_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
class DEuroSavingsPage extends BasePage {
final DEuroViewModel _dEuroViewModel;
DEuroSavingsPage(this._dEuroViewModel);
@override
bool get gradientBackground => true;
@override
Widget Function(BuildContext, Widget) get rootWrapper =>
(context, scaffold) => GradientBackground(scaffold: scaffold);
@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,
currency: CryptoCurrency.deuro,
onAddSavingsPressed: () => _onSavingsAdd(context),
onRemoveSavingsPressed: () => _onSavingsRemove(context),
onApproveSavingsPressed: _dEuroViewModel.prepareApproval,
isEnabled: _dEuroViewModel.isEnabled,
),
),
Observer(
builder: (_) => InterestCardWidget(
isDarkTheme: currentTheme.isDark,
title: S.of(context).deuro_savings_collect_interest,
collectedInterest: _dEuroViewModel.accruedInterest,
onCollectInterest: _dEuroViewModel.prepareCollectInterest,
),
),
],
),
);
}
Future<void> _onSavingsAdd(BuildContext context) async {
final amount = await Navigator.of(context).push(MaterialPageRoute<String>(
builder: (BuildContext context) => SavingEditPage(isAdding: true)));
if (amount != null) _dEuroViewModel.prepareSavingsEdit(amount, true);
}
Future<void> _onSavingsRemove(BuildContext context) async {
final amount = await Navigator.of(context).push(MaterialPageRoute<String>(
builder: (BuildContext context) => SavingEditPage(isAdding: false)));
if (amount != null) _dEuroViewModel.prepareSavingsEdit(amount, false);
}
bool _isReactionsSet = false;
void _setReactions(BuildContext context, DEuroViewModel dEuroViewModel) {
if (_isReactionsSet) return;
reaction((_) => dEuroViewModel.transaction, (PendingTransaction? tx) async {
if (tx == null) return;
final result = await showModalBottomSheet<bool>(
context: context,
isDismissible: false,
isScrollControlled: true,
builder: (BuildContext bottomSheetContext) => ConfirmSendingBottomSheet(
key: ValueKey('savings_page_confirm_sending_dialog_key'),
titleText: S.of(bottomSheetContext).confirm_transaction,
currentTheme: currentTheme,
walletType: WalletType.ethereum,
titleIconPath: CryptoCurrency.deuro.iconPath,
currency: CryptoCurrency.deuro,
amount: S.of(bottomSheetContext).send_amount,
amountValue: tx.amountFormatted,
fiatAmountValue: "",
fee: S.of(bottomSheetContext).send_estimated_fee,
feeValue: tx.feeFormatted,
feeFiatAmount: "",
outputs: [],
onSlideComplete: () async {
Navigator.of(bottomSheetContext).pop(true);
dEuroViewModel.commitTransaction();
},
change: tx.change,
),
);
if (result == null) dEuroViewModel.dismissTransaction();
});
reaction((_) => dEuroViewModel.approvalTransaction, (PendingTransaction? tx) async {
if (tx == null) return;
final result = await showModalBottomSheet<bool>(
context: context,
isDismissible: false,
isScrollControlled: true,
builder: (BuildContext bottomSheetContext) => ConfirmSendingBottomSheet(
key: ValueKey('savings_page_confirm_approval_dialog_key'),
titleText: S.of(bottomSheetContext).approve_tokens,
currentTheme: currentTheme,
walletType: WalletType.ethereum,
titleIconPath: CryptoCurrency.deuro.iconPath,
currency: CryptoCurrency.deuro,
amount: S.of(bottomSheetContext).send_amount,
amountValue: tx.amountFormatted,
fiatAmountValue: "",
fee: S.of(bottomSheetContext).send_estimated_fee,
feeValue: tx.feeFormatted,
feeFiatAmount: "",
outputs: [],
onSlideComplete: () {
Navigator.of(bottomSheetContext).pop(true);
dEuroViewModel.commitApprovalTransaction();
},
change: tx.change,
),
);
if (result == null) dEuroViewModel.dismissTransaction();
});
reaction((_) => dEuroViewModel.state, (ExecutionState state) async {
if (state is TransactionCommitted) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (!context.mounted) return;
await showModalBottomSheet<void>(
context: context,
isDismissible: false,
builder: (BuildContext bottomSheetContext) => InfoBottomSheet(
currentTheme: currentTheme,
titleText: S.of(bottomSheetContext).transaction_sent,
contentImage: 'assets/images/birthday_cake.png',
content: S.of(bottomSheetContext).deuro_tx_commited_content,
actionButtonText: S.of(bottomSheetContext).close,
actionButtonKey: ValueKey('send_page_sent_dialog_ok_button_key'),
actionButton: () => Navigator.of(bottomSheetContext).pop(),
),
);
});
}
});
_isReactionsSet = true;
}
}
@@ -0,0 +1,45 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.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:cake_wallet/view_model/integrations/deuro_view_model.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
class EditSavingsBottomSheet extends BaseBottomSheet {
EditSavingsBottomSheet(this.dEuroViewModel, {required super.titleText});
final _amountController = TextEditingController();
final DEuroViewModel dEuroViewModel;
@override
Widget contentWidget(BuildContext context) => Column(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: CurrencyAmountTextField(
hasUnderlineBorder: true,
borderWidth: 1.0,
selectedCurrency: CryptoCurrency.deuro.name.toUpperCase(),
amountFocusNode: null,
amountController: _amountController,
tag: CryptoCurrency.deuro.tag,
isAmountEditable: true,
),
),
],
);
@override
Widget footerWidget(BuildContext context) => Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 34),
child: LoadingPrimaryButton(
onPressed: () => dEuroViewModel.prepareSavingsEdit(_amountController.text, true),
text: S.of(context).confirm,
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary,
isLoading: false,
isDisabled: false,
),
);
}
@@ -0,0 +1,67 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/integrations/deuro/widgets/savings_card_widget.dart';
import 'package:cake_wallet/themes/utils/custom_theme_colors.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
class InterestCardWidget extends StatelessWidget {
InterestCardWidget({
required this.title,
required this.collectedInterest,
super.key,
required this.isDarkTheme,
required this.onCollectInterest,
});
final String title;
final String collectedInterest;
final bool isDarkTheme;
final VoidCallback onCollectInterest;
@override
Widget build(BuildContext context) {
return Stack(children: [
Container(
margin: EdgeInsets.symmetric(horizontal: 16),
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
gradient: LinearGradient(
colors: [
isDarkTheme
? CustomThemeColors.cardGradientColorPrimaryDark
: CustomThemeColors.cardGradientColorPrimaryLight,
isDarkTheme
? CustomThemeColors.cardGradientColorSecondaryDark
: CustomThemeColors.cardGradientColorSecondaryLight,
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
SavingsCard.getAssetBalanceRow(
context,
title: title,
subtitle: collectedInterest,
currency: CryptoCurrency.deuro,
hideSymbol: true,
),
SizedBox(height: 10),
SavingsCard.getButton(
context,
label: S.of(context).deuro_collect_interest,
onPressed: onCollectInterest,
backgroundColor: Theme.of(context).colorScheme.primary,
color: Theme.of(context).colorScheme.onPrimary,
),
],
),
),
),
]);
}
}
@@ -0,0 +1,107 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class NumberPad extends StatelessWidget {
final VoidCallback? onDecimalPressed;
final VoidCallback onDeletePressed;
final void Function(int index) onNumberPressed;
final FocusNode focusNode;
const NumberPad({
super.key,
required this.onNumberPressed,
required this.onDeletePressed,
required this.focusNode,
this.onDecimalPressed,
});
@override
Widget build(BuildContext context) => KeyboardListener(
focusNode: focusNode,
onKeyEvent: (keyEvent) {
if (keyEvent is KeyDownEvent) {
if (keyEvent.logicalKey.keyLabel == "Backspace") {
return onDeletePressed();
}
if ([".", ","].contains(keyEvent.logicalKey.keyLabel) &&
onDecimalPressed != null) {
return onDecimalPressed!();
}
int? number = int.tryParse(keyEvent.character ?? '');
if (number != null) return onNumberPressed(number);
}
},
child: SizedBox(
height: 300,
child: GridView.count(
childAspectRatio: 2,
shrinkWrap: true,
crossAxisCount: 3,
physics: const NeverScrollableScrollPhysics(),
children: List.generate(12, (index) {
if (index == 9) {
if (onDecimalPressed == null) return Container();
return InkWell(
onTap: onDecimalPressed,
child: Center(
child: Text(
'.',
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.w600,
fontSize: 30,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
textAlign: TextAlign.center,
),
),
);
} else if (index == 10) {
index = 0;
} else if (index == 11) {
return MergeSemantics(
child: Container(
child: Semantics(
label: S.of(context).delete,
button: true,
onTap: onDeletePressed,
child: TextButton(
onPressed: onDeletePressed,
style: TextButton.styleFrom(
backgroundColor:
Colors.transparent,
shape: CircleBorder(),
),
child: Image.asset(
'assets/images/delete_icon.png',
color: Theme.of(context).colorScheme.primary,
),
),
),
),
);
} else {
index++;
}
return InkWell(
onTap: () => onNumberPressed(index),
child: Center(
child: Text(
'$index',
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.w600,
fontSize: 30,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
textAlign: TextAlign.center,
),
),
);
}),
),
),
);
}
@@ -0,0 +1,263 @@
import 'dart:math';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/themes/utils/custom_theme_colors.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
class SavingsCard extends StatelessWidget {
final bool isDarkTheme;
final bool isEnabled;
final String interestRate;
final String savingsBalance;
final CryptoCurrency currency;
final VoidCallback onAddSavingsPressed;
final VoidCallback onRemoveSavingsPressed;
final VoidCallback onApproveSavingsPressed;
const SavingsCard({
super.key,
required this.isDarkTheme,
required this.interestRate,
required this.savingsBalance,
required this.currency,
required this.onAddSavingsPressed,
required this.onRemoveSavingsPressed,
required this.onApproveSavingsPressed,
this.isEnabled = true,
});
@override
Widget build(BuildContext context) => Container(
margin: const EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
gradient: LinearGradient(
colors: [
isDarkTheme
? CustomThemeColors.cardGradientColorPrimaryDark
: CustomThemeColors.cardGradientColorPrimaryLight,
isDarkTheme
? CustomThemeColors.cardGradientColorSecondaryDark
: CustomThemeColors.cardGradientColorSecondaryLight,
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
children: [
getAssetBalanceRow(context,
title: S.of(context).deuro_savings_balance,
subtitle: savingsBalance,
currency: currency),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
children: [
Expanded(
child: Text(
'Current APR',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color:
Theme.of(context).colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w500,
),
softWrap: true,
),
),
Text(
interestRate,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
fontWeight: FontWeight.w500,
),
softWrap: true,
),
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: isEnabled
? [
Expanded(
child: getButton(
context,
label: S.of(context).deuro_savings_add,
imagePath: 'assets/images/received.png',
onPressed: onAddSavingsPressed,
backgroundColor:
Theme.of(context).colorScheme.primary,
color: Theme.of(context).colorScheme.onPrimary,
),
),
SizedBox(width: 12),
Expanded(
child: getButton(
context,
label: S.of(context).deuro_savings_remove,
imagePath: 'assets/images/upload.png',
onPressed: onRemoveSavingsPressed,
backgroundColor:
Theme.of(context).colorScheme.surface,
color: Theme.of(context)
.colorScheme
.onSecondaryContainer,
),
),
]
: [
Expanded(
child: getButton(
context,
label: S.of(context).deuro_savings_set_approval,
onPressed: onApproveSavingsPressed,
backgroundColor:
Theme.of(context).colorScheme.primary,
color: Theme.of(context).colorScheme.onPrimary,
),
)
],
),
],
),
));
static Widget getButton(
BuildContext context, {
required String label,
String? imagePath,
required VoidCallback onPressed,
required Color backgroundColor,
required Color color,
}) =>
Semantics(
label: label,
child: OutlinedButton(
onPressed: onPressed,
style: OutlinedButton.styleFrom(
backgroundColor: backgroundColor,
side: BorderSide(
color: Theme.of(context).colorScheme.outlineVariant.withAlpha(0),
width: 0,
),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (imagePath != null) ...[
Image.asset(
imagePath,
height: 30,
width: 30,
color: color,
),
const SizedBox(width: 8),
],
Text(
label,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: color,
fontWeight: FontWeight.w700,
),
),
],
),
),
),
);
static Widget getAssetBalanceRow(
BuildContext context, {
required String title,
required String subtitle,
required CryptoCurrency currency,
bool hideSymbol = true,
}) =>
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
height: 1,
),
),
SizedBox(height: 6),
AutoSizeText(
subtitle,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.w900,
fontSize: 24,
height: 1,
),
maxLines: 1,
textAlign: TextAlign.start,
),
],
),
SizedBox(
//width: min(MediaQuery.of(context).size.width * 0.2, 100),
child: Center(
child: Column(
children: [
CakeImageWidget(
imageUrl: currency.iconPath,
height: 40,
width: 40,
errorWidget: Container(
height: 30.0,
width: 30.0,
child: Center(
child: Text(
currency.title
.substring(0, min(currency.title.length, 2)),
style:
Theme.of(context).textTheme.bodySmall?.copyWith(
fontSize: 11,
color: Theme.of(context)
.colorScheme
.onSurfaceVariant,
),
),
),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.surfaceContainer,
),
),
),
if (!hideSymbol) ...[
const SizedBox(height: 10),
Text(
currency.title,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Theme.of(context).colorScheme.onSurface,
height: 1,
),
),
]
],
),
),
),
],
);
}
@@ -0,0 +1,90 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/integrations/deuro/widgets/numpad.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:flutter/material.dart';
class SavingEditPage extends BasePage {
final bool isAdding;
SavingEditPage({required this.isAdding});
String get title =>
isAdding ? S.current.deuro_savings_add : S.current.deuro_savings_remove;
@override
Widget body(BuildContext context) => _SavingsEditBody();
}
class _SavingsEditBody extends StatefulWidget {
const _SavingsEditBody();
@override
State<StatefulWidget> createState() => _SavingsEditBodyState();
}
class _SavingsEditBodyState extends State<_SavingsEditBody> {
@override
void initState() {
WidgetsBinding.instance
.addPostFrameCallback((_) => _numpadFocusNode.requestFocus());
super.initState();
}
@override
void dispose() {
_numpadFocusNode.dispose();
super.dispose();
}
String amount = '0';
final FocusNode _numpadFocusNode = FocusNode();
@override
Widget build(BuildContext context) => SafeArea(
child: Column(children: [
Expanded(
child: Center(
child: Padding(
padding: const EdgeInsets.only(left: 26, right: 26, top: 10),
child: AutoSizeText(
"${amount.toString()} dEuro",
maxLines: 1,
maxFontSize: 60,
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.w600,
fontSize: 60,
color: Theme.of(context).colorScheme.onSurface,
),
textAlign: TextAlign.center,
),
),
)),
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('.', '')}.'),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 34),
child: LoadingPrimaryButton(
onPressed: () => Navigator.pop(context, amount),
text: S.of(context).confirm,
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary,
isLoading: false,
isDisabled: false,
),
)
]),
);
}
@@ -109,6 +109,7 @@ class DashBoardRoundedCardWidget extends StatelessWidget {
],
),
),
Padding(padding: EdgeInsets.only(left: 10)),
if (image != null) image! else if (svgPicture != null) svgPicture!,
if (icon != null) icon!
],