added zwg support

This commit is contained in:
Prince
2026-06-17 14:42:46 +02:00
parent cc4b02f3c9
commit 64eaa38079
12 changed files with 324 additions and 247 deletions

View File

@@ -0,0 +1,43 @@
class UserModel {
final String token;
final String type;
final String username;
final String email;
final String phone;
final String firstName;
final String uuid;
UserModel({
required this.token,
required this.type,
required this.username,
required this.email,
required this.phone,
required this.firstName,
required this.uuid,
});
factory UserModel.fromJson(Map<String, dynamic> json) {
return UserModel(
token: json['token'] as String,
type: json['type'] as String,
username: json['username'] as String,
email: json['email'] as String,
phone: json['phone'] as String,
firstName: json['firstName'] as String,
uuid: json['uuid'] as String,
);
}
Map<String, dynamic> toJson() {
return {
'token': token,
'type': type,
'username': username,
'email': email,
'phone': phone,
'firstName': firstName,
'uuid': uuid,
};
}
}

View File

@@ -5,14 +5,20 @@ import 'package:qpay/screens/accounts/models/account_model.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AccountBalanceWidget extends StatefulWidget {
const AccountBalanceWidget({super.key});
final String? selectedCurrency;
final ValueChanged<String>? onCurrencySelected;
const AccountBalanceWidget({
super.key,
this.selectedCurrency,
this.onCurrencySelected,
});
@override
State<AccountBalanceWidget> createState() => _AccountBalanceWidgetState();
}
class _AccountBalanceWidgetState extends State<AccountBalanceWidget>
with SingleTickerProviderStateMixin {
class _AccountBalanceWidgetState extends State<AccountBalanceWidget> {
late AccountProvider _accountProvider;
AccountModel? _usdAccount;
@@ -23,22 +29,15 @@ class _AccountBalanceWidgetState extends State<AccountBalanceWidget>
String? _zwgError;
String? _phoneNumber;
late AnimationController _pulseController;
@override
void initState() {
super.initState();
_pulseController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 800),
);
_accountProvider = AccountProvider();
_loadBalances();
}
@override
void dispose() {
_pulseController.dispose();
_accountProvider.dispose();
super.dispose();
}
@@ -85,10 +84,6 @@ class _AccountBalanceWidgetState extends State<AccountBalanceWidget>
}
});
}
if (mounted) {
_pulseController.forward();
}
}
@override
@@ -101,46 +96,39 @@ class _AccountBalanceWidgetState extends State<AccountBalanceWidget>
}
return Padding(
padding: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.only(bottom: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionLabel(
theme,
isDark,
Icons.account_balance_wallet_rounded,
'Account Balances',
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: _buildBalanceCard(
child: _buildCompactCard(
theme: theme,
isDark: isDark,
currencyCode: 'USD',
currencyName: 'US Dollar',
flagAsset: 'united-states.png',
balance: _usdAccount?.balance,
isLoading: _isLoadingUsd,
error: _usdError,
isSelected: widget.selectedCurrency == 'USD',
gradientColors: [
const Color(0xFF0D47A1),
const Color(0xFF1976D2),
],
),
),
const SizedBox(width: 12),
const SizedBox(width: 10),
Expanded(
child: _buildBalanceCard(
child: _buildCompactCard(
theme: theme,
isDark: isDark,
currencyCode: 'ZWG',
currencyName: 'Zimbabwe Gold',
flagAsset: 'zimbabwe.png',
balance: _zwgAccount?.balance,
isLoading: _isLoadingZwG,
error: _zwgError,
isSelected: widget.selectedCurrency == 'ZWG',
gradientColors: [
const Color(0xFF1B5E20),
const Color(0xFF388E3C),
@@ -149,42 +137,38 @@ class _AccountBalanceWidgetState extends State<AccountBalanceWidget>
),
],
),
const SizedBox(height: 8),
const SizedBox(height: 6),
Center(
child: TextButton.icon(
onPressed: _isLoadingUsd || _isLoadingZwG
? null
: () {
_pulseController.reset();
_loadBalances();
},
icon: AnimatedBuilder(
animation: _pulseController,
builder: (context, child) {
return Transform.rotate(
angle: _pulseController.value * 6.2832,
child: Icon(
Icons.refresh_rounded,
size: 16,
color: _isLoadingUsd || _isLoadingZwG
? (isDark ? Colors.white24 : Colors.grey.shade400)
: theme.colorScheme.primary,
),
);
},
icon: Icon(
Icons.refresh_rounded,
size: 14,
color: _isLoadingUsd || _isLoadingZwG
? (isDark ? Colors.white24 : Colors.grey.shade400)
: theme.colorScheme.primary,
),
label: Text(
_isLoadingUsd || _isLoadingZwG
? 'Refreshing...'
: 'Refresh Balances',
: 'Refresh',
style: TextStyle(
fontSize: 12,
fontSize: 11,
fontWeight: FontWeight.w600,
color: _isLoadingUsd || _isLoadingZwG
? (isDark ? Colors.white24 : Colors.grey.shade400)
: theme.colorScheme.primary,
),
),
style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
),
),
],
@@ -192,197 +176,158 @@ class _AccountBalanceWidgetState extends State<AccountBalanceWidget>
);
}
Widget _buildBalanceCard({
Widget _buildCompactCard({
required ThemeData theme,
required bool isDark,
required String currencyCode,
required String currencyName,
required String flagAsset,
double? balance,
required bool isLoading,
String? error,
required bool isSelected,
required List<Color> gradientColors,
}) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: LinearGradient(
colors: gradientColors,
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: gradientColors[0].withValues(alpha: 0.3),
blurRadius: 12,
offset: const Offset(0, 4),
return GestureDetector(
onTap: () {
widget.onCurrencySelected?.call(currencyCode);
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
gradient: LinearGradient(
colors: gradientColors,
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Top row: flag + currency code
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 24,
height: 24,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.white.withValues(alpha: 0.3),
width: 1.5,
),
boxShadow: [
BoxShadow(
color: gradientColors[0].withValues(alpha: isSelected ? 0.5 : 0.25),
blurRadius: isSelected ? 12 : 6,
offset: Offset(0, isSelected ? 4 : 2),
),
],
border: isSelected
? Border.all(
color: Colors.white.withValues(alpha: 0.8),
width: 2,
)
: Border.all(
color: Colors.white.withValues(alpha: 0.15),
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// Top row: flag + currency code
Row(
children: [
Container(
width: 22,
height: 22,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.white.withValues(alpha: 0.3),
width: 1.5,
),
child: ClipOval(
child: Image.asset(
'assets/$flagAsset',
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => Icon(
Icons.monetization_on_outlined,
size: 14,
color: Colors.white.withValues(alpha: 0.8),
),
),
child: ClipOval(
child: Image.asset(
'assets/$flagAsset',
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => Icon(
Icons.monetization_on_outlined,
size: 12,
color: Colors.white.withValues(alpha: 0.8),
),
),
),
const SizedBox(width: 8),
Text(
),
const SizedBox(width: 6),
Expanded(
child: Text(
currencyCode,
style: const TextStyle(
fontSize: 13,
fontSize: 12,
fontWeight: FontWeight.w700,
color: Colors.white,
letterSpacing: 0.5,
),
),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(8),
),
child: Text(
currencyName,
style: TextStyle(
fontSize: 8,
fontWeight: FontWeight.w500,
color: Colors.white.withValues(alpha: 0.9),
if (isSelected)
Container(
width: 18,
height: 18,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.3),
shape: BoxShape.circle,
),
child: const Icon(
Icons.check_rounded,
size: 12,
color: Colors.white,
),
),
],
),
const SizedBox(height: 10),
// Balance amount
if (isLoading)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 70,
height: 12,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(4),
),
),
],
)
else if (error != null)
Row(
children: [
Icon(
Icons.error_outline_rounded,
color: Colors.white.withValues(alpha: 0.7),
size: 16,
),
const SizedBox(width: 4),
Text(
'Unavailable',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w500,
color: Colors.white.withValues(alpha: 0.7),
),
),
],
)
else ...[
Text(
_formatBalance(balance ?? 0.0),
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.w800,
color: Colors.white,
letterSpacing: 0.5,
height: 1.1,
),
),
],
),
const SizedBox(height: 16),
// Balance amount
if (isLoading)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 80,
height: 14,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(4),
),
),
const SizedBox(height: 4),
Container(
width: 50,
height: 10,
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(4),
),
),
],
)
else if (error != null)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
Icons.error_outline_rounded,
color: Colors.white.withValues(alpha: 0.7),
size: 20,
),
const SizedBox(height: 4),
Text(
'Unavailable',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
color: Colors.white.withValues(alpha: 0.7),
),
),
],
)
else ...[
Text(
_formatBalance(balance ?? 0.0),
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.w800,
color: Colors.white,
letterSpacing: 0.5,
height: 1.1,
),
),
const SizedBox(height: 4),
Text(
currencyCode == 'USD' ? 'Available Balance' : 'Available Balance',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w400,
color: Colors.white.withValues(alpha: 0.7),
),
),
],
],
),
),
);
}
Widget _buildSectionLabel(
ThemeData theme,
bool isDark,
IconData icon,
String label,
) {
return Row(
children: [
Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: theme.colorScheme.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(icon, size: 16, color: theme.colorScheme.primary),
),
const SizedBox(width: 10),
Text(
label,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: isDark ? Colors.white : Colors.black87,
letterSpacing: 0.3,
),
),
],
);
}
String _formatBalance(double balance) {
final formatter = NumberFormat('#,##0.00', 'en_US');
return '\$${formatter.format(balance)}';

View File

@@ -370,6 +370,15 @@ class _ConfirmScreenState extends State<ConfirmScreen>
children: [
_buildDetailRow(
icon: Icons.monetization_on_outlined,
label: "Currency",
value: controller
.transactionController
.model
.confirmationData["debitCurrency"]
.toString(),
),
_buildDetailRow(
icon: Icons.money,
label: "Amount",
value: controller
.transactionController

View File

@@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
@@ -8,7 +9,6 @@ import 'package:qpay/screens/home/home_controller.dart';
import 'package:qpay/screens/receipt/receipt_controller.dart';
import 'package:qpay/screens/transaction_controller.dart';
import 'package:qpay/screens/transactions/transaction_model.dart' as txn;
import 'package:qpay/widgets/app_snack_bar.dart';
import 'package:qpay/widgets/transaction_item_widget.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -28,8 +28,8 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
late final router = GoRouter.of(context);
bool _isLoggedIn = false;
bool _isZWGSelected = false;
String initials = '';
String _selectedCurrency = 'USD';
late AnimationController _providersAnimController;
late Animation<Offset> _providersSlideAnimation;
@@ -107,7 +107,10 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
homeController = HomeController(context);
await homeController.getAccounts();
await homeController.getCategories();
await homeController.getProviders(_isZWGSelected ? "ZWG" : "USD");
await homeController.getProviders(_selectedCurrency);
transactionController.model.formData = transactionController.model.formData
.copyWith(debitCurrency: _selectedCurrency);
prefs = await SharedPreferences.getInstance();
@@ -127,10 +130,22 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
await homeController.getTransactions(prefs.getString("userId")!);
}
void _onCurrencySelected(String currency) {
if (_selectedCurrency == currency) return;
setState(() {
_selectedCurrency = currency;
});
homeController.getProviders(currency);
transactionController.updateCurrency(currency);
}
Future<void> _onRefresh() async {
await homeController.getAccounts();
await homeController.getCategories();
await homeController.getProviders(_isZWGSelected ? "ZWG" : "USD");
await homeController.getProviders(_selectedCurrency);
prefs = await SharedPreferences.getInstance();
if (prefs.getString("userId") != null) {
@@ -153,9 +168,21 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
/// Handles the repeat action for a transaction.
Future<void> _repeatTransaction(Map<String, dynamic> item) async {
final receiptController = ReceiptController(context);
final transaction = txn.TransactionModel.fromJson(item);
await receiptController.repeatTransaction(transaction);
try {
final receiptController = ReceiptController(context);
final transaction = txn.TransactionModel.fromJson(item);
await receiptController.repeatTransaction(transaction);
} catch (e, s) {
if (kDebugMode) {
print(e);
print(s);
}
if (mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Error repeating transaction')));
}
}
if (mounted) {
context.push('/make-payment');
}
@@ -370,7 +397,11 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
_buildLoginPrompt(theme, isDark),
const SizedBox(height: 20),
],
if (_isLoggedIn) const AccountBalanceWidget(),
if (_isLoggedIn)
AccountBalanceWidget(
selectedCurrency: _selectedCurrency,
onCurrencySelected: _onCurrencySelected,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [

View File

@@ -0,0 +1,18 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import '../../models/user_model.dart';
class AuthProvider {
// get the user model from shared preferences
static Future<UserModel?> getUserModel() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? userModelString = prefs.getString("userModel");
if (userModelString != null) {
Map<String, dynamic> userModelJson = jsonDecode(userModelString);
return UserModel.fromJson(userModelJson);
}
return null;
}
}

View File

@@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:qpay/auth_state.dart';
@@ -5,6 +7,8 @@ import 'package:qpay/http/http.dart';
import 'package:qpay/models/api_response.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../models/user_model.dart';
class LoginScreenModel {
String username = "";
String password = "";
@@ -72,6 +76,9 @@ class LoginController extends ChangeNotifier {
: ''),
);
UserModel userModel = UserModel.fromJson(response);
prefs.setString("userModel", jsonEncode(userModel));
// Notify the router that the user is now signed in so any
// auth-gated redirects (e.g. the groups flow) are re-evaluated
// and the user is sent to their originally requested page.

View File

@@ -89,7 +89,7 @@ class PayController extends ChangeNotifier {
type: "CONFIRM",
billClientId: model.selectedProvider?.clientId ?? '',
debitRef: 'Velocity',
debitCurrency: 'USD',
debitCurrency: transactionController.model.currency,
creditAccount: transactionController.model.formData.creditAccount,
creditPhone: transactionController.model.formData.creditPhone,
billName: model.selectedProvider?.name ?? '',

View File

@@ -259,6 +259,12 @@ class _PayScreenState extends State<PayScreen> with TickerProviderStateMixin {
.formData
.creditAccount,
),
_buildDetailRow(
icon: Icons.monetization_on,
label: "Currency",
value:
transactionController.model.currency,
),
],
),
SizedBox(height: 20),
@@ -643,7 +649,7 @@ class _PayScreenState extends State<PayScreen> with TickerProviderStateMixin {
const SizedBox(height: 5),
DropdownButtonHideUnderline(
child: DropdownButtonFormField<String>(
initialValue: controller.model.selectedProduct?.uid,
initialValue: transactionController.model.selectedProduct?.name,
isExpanded: true,
icon: const Icon(Icons.arrow_drop_down),
decoration: InputDecoration(
@@ -661,10 +667,10 @@ class _PayScreenState extends State<PayScreen> with TickerProviderStateMixin {
DropdownMenuItem(child: Text("Select Product")),
...controller.model.products.map(
(e) => DropdownMenuItem<String>(
value: e.uid,
value: e.name,
child: Text(
'${e.displayName} - '
'${transactionController.model.formData.debitCurrency}${e.defaultAmount}',
'${transactionController.model.currency}${e.defaultAmount}',
),
),
),
@@ -718,7 +724,7 @@ class _PayScreenState extends State<PayScreen> with TickerProviderStateMixin {
),
const SizedBox(width: 8),
Text(
"TRANSACTION DETAILS",
"PROVIDER DETAILS",
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,

View File

@@ -82,8 +82,8 @@ class ReceiptController extends ChangeNotifier {
Map<String, dynamic> provider = await http.get(
'/public/providers/client/$providerId',
);
transactionController.model.selectedProvider = hc.BillProvider.fromJson(
provider,
transactionController.updateSelectedProvider(
hc.BillProvider.fromJson(provider),
);
// update tran controller formdata with credit account, email, name, phone & providerLabel
@@ -96,19 +96,20 @@ class ReceiptController extends ChangeNotifier {
);
// fetch product and update tran controller formdata
String providerUid =
transactionController.model.selectedProvider?.uid ?? '';
String productUid = transaction.productUid ?? '';
String providerUid = transactionController.model.selectedProvider?.id ?? '';
String productName = transaction.billProductName ?? '';
if (productUid.isNotEmpty) {
if (productName.isNotEmpty) {
Map<String, dynamic> product = await http.get(
'/public/providers/$providerUid/products/$productUid',
'/public/providers/$providerUid/products/$productName',
);
transactionController.model.selectedProduct = pc.BillProduct.fromJson(
product,
transactionController.updateSelectedProduct(
pc.BillProduct.fromJson(product),
);
}
transactionController.updateCurrency(transaction.debitCurrency);
// update credit amount, notification phone number, payment processor
transactionController.model.formData = transactionController.model.formData
.copyWith(

View File

@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@@ -6,11 +7,14 @@ import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import 'package:qpay/models/responsive_policy.dart';
import 'package:qpay/models/user_model.dart';
import 'package:qpay/screens/transaction_controller.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:qpay/screens/recipient/recipients_controller.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../onboarding/auth_provider.dart';
class RecipientsScreen extends StatefulWidget {
const RecipientsScreen({super.key});
@@ -66,6 +70,16 @@ class _RecipientsScreenState extends State<RecipientsScreen>
controller.getRecipients(_queryParams);
}
Future<UserModel?> getAuthenticatedUser() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? userModelString = prefs.getString("userModel");
if (userModelString != null) {
Map<String, dynamic> userModelJson = jsonDecode(userModelString);
return UserModel.fromJson(userModelJson);
}
return null;
}
@override
void dispose() {
_animationController.dispose();
@@ -577,7 +591,7 @@ class _RecipientsScreenState extends State<RecipientsScreen>
),
const SizedBox(width: 8),
Text(
"TRANSACTION DETAILS",
"PROVIDER DETAILS",
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
@@ -592,12 +606,18 @@ class _RecipientsScreenState extends State<RecipientsScreen>
const SizedBox(height: 16),
if (children != null)
...children
else
else ...[
_buildDetailRow(
icon: Icons.business_outlined,
label: label,
value: value ?? '',
),
_buildDetailRow(
icon: Icons.monetization_on,
label: "Currency",
value: transactionController.model.currency,
),
],
],
),
),

View File

@@ -14,6 +14,7 @@ class TransactionModel {
BillProduct? selectedProduct;
BillProvider? selectedProvider;
Map<String, dynamic>? receiptData;
String currency = 'USD';
FormData formData = FormData(
type: '',
billClientId: '',
@@ -118,6 +119,11 @@ class TransactionController extends ChangeNotifier {
notifyListeners();
}
updateCurrency(String currency) {
model.currency = currency;
notifyListeners();
}
clearFormData() {
model.formData = FormData(
id: null,

View File

@@ -46,13 +46,13 @@ class _TransactionItemWidgetState extends State<TransactionItemWidget> {
final statusIcon = isSuccess
? Icons.check_circle_rounded
: isPending
? Icons.pending_rounded
: Icons.cancel_rounded;
? Icons.pending_rounded
: Icons.cancel_rounded;
final statusColor = isSuccess
? const Color(0xFF10B981)
: isPending
? const Color(0xFFF59E0B)
: const Color(0xFFEF4444);
? const Color(0xFFF59E0B)
: const Color(0xFFEF4444);
return Skeletonizer(
enabled: widget.enabled,
@@ -66,8 +66,7 @@ class _TransactionItemWidgetState extends State<TransactionItemWidget> {
context.push("/receipt/${widget.transaction['id']}");
},
child: Container(
padding:
const EdgeInsets.symmetric(vertical: 12, horizontal: 14),
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF1A1A2E) : Colors.white,
borderRadius: BorderRadius.circular(16),
@@ -138,11 +137,7 @@ class _TransactionItemWidgetState extends State<TransactionItemWidget> {
),
),
const SizedBox(width: 8),
Icon(
statusIcon,
size: 16,
color: statusColor,
),
Icon(statusIcon, size: 16, color: statusColor),
],
),
const SizedBox(height: 4),
@@ -172,9 +167,7 @@ class _TransactionItemWidgetState extends State<TransactionItemWidget> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
(widget.transaction['amount'] as num?)
?.toStringAsFixed(2) ??
'',
'${(widget.transaction['amount'] as num?)?.toStringAsFixed(2) ?? ''} ${widget.transaction['debitCurrency']}',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
@@ -182,9 +175,7 @@ class _TransactionItemWidgetState extends State<TransactionItemWidget> {
),
),
Text(
(widget.transaction['totalAmount'] as num?)
?.toStringAsFixed(2) ??
'',
'${(widget.transaction['totalAmount'] as num?)?.toStringAsFixed(2) ?? ''} ${widget.transaction['debitCurrency']}',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
@@ -246,4 +237,4 @@ class _TransactionItemWidgetState extends State<TransactionItemWidget> {
),
);
}
}
}