This commit is contained in:
2026-03-25 01:13:28 +03:00
parent ceea63812b
commit 76cffc4960
2 changed files with 569 additions and 328 deletions
+49 -13
View File
@@ -77,10 +77,16 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen>
_noteController.text = widget.initial!.note ?? '';
} else {
WidgetsBinding.instance.addPostFrameCallback((_) {
final activeAccount = ref.read(activeAccountProvider);
final curr = ref.read(currencyProvider);
// Use active account's currency if available, otherwise use global currency
final currencyCode = activeAccount?.currency ?? curr.code;
final currencySymbol = currencyMap[currencyCode]?.symbol ?? curr.symbol;
ref
.read(addTransactionProvider(null).notifier)
.setCurrency(curr.symbol, curr.code);
.setCurrency(currencySymbol, currencyCode);
});
}
}
@@ -155,18 +161,24 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen>
try {
print('--- SUBMIT CLICKED ---');
print('Amount: $amount, Category: ${state.category}, Type: ${state.type.name}');
print(
'Amount: $amount, Category: ${state.category}, Type: ${state.type.name}',
);
final activeAccount = ref.read(activeAccountProvider);
int accountId;
if (activeAccount != null) {
print('Using active account ID: ${activeAccount.id}, Name: ${activeAccount.name}');
print(
'Using active account ID: ${activeAccount.id}, Name: ${activeAccount.name}',
);
accountId = activeAccount.id;
} else {
print('No active account. Fetching main account...');
final mainAccount = await ref.read(accountRepositoryProvider).getMain();
print('Main account fetched: ID=${mainAccount.id}, Name: ${mainAccount.name}');
print(
'Main account fetched: ID=${mainAccount.id}, Name: ${mainAccount.name}',
);
accountId = mainAccount.id;
}
@@ -190,7 +202,9 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen>
print('Update completed');
} else {
final res = await ref.read(transactionsProvider.notifier).add(tx);
print('Add completed. Result: ${res.isSuccess ? "SUCCESS" : "FAILURE"}');
print(
'Add completed. Result: ${res.isSuccess ? "SUCCESS" : "FAILURE"}',
);
if (res.isFailure) {
print('!!! Provider returned failure: ${res.errorOrNull}');
@@ -343,8 +357,12 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen>
Expanded(
child: accountsAsync.when(
data: (accounts) {
final displayAccount = activeAccount ??
accounts.firstWhere((a) => a.isMain, orElse: () => accounts.first);
final displayAccount =
activeAccount ??
accounts.firstWhere(
(a) => a.isMain,
orElse: () => accounts.first,
);
return Container(
height: 56,
@@ -369,7 +387,8 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen>
Flexible(
child: Text(
displayAccount.name,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
style: Theme.of(context).textTheme.bodyMedium
?.copyWith(
color: const Color(0xFF7C6DED),
fontWeight: FontWeight.w600,
fontSize: 14,
@@ -408,14 +427,21 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen>
borderRadius: BorderRadius.circular(12),
border: isDark
? null
: Border.all(color: const Color(0xFFDDDDEE), width: 1),
: Border.all(
color: const Color(0xFFDDDDEE),
width: 1,
),
),
child: Row(
children: [
Expanded(
child: GestureDetector(
onTap: () => ref
.read(addTransactionProvider(widget.initial).notifier)
.read(
addTransactionProvider(
widget.initial,
).notifier,
)
.setType(TransactionType.income),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
@@ -430,7 +456,10 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen>
Icons.arrow_downward_rounded,
color: state.type == TransactionType.income
? AppColors.income
: Theme.of(context).colorScheme.onSurface.withOpacity(0.4),
: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.4),
size: 20,
),
),
@@ -440,7 +469,11 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen>
Expanded(
child: GestureDetector(
onTap: () => ref
.read(addTransactionProvider(widget.initial).notifier)
.read(
addTransactionProvider(
widget.initial,
).notifier,
)
.setType(TransactionType.expense),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
@@ -455,7 +488,10 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen>
Icons.arrow_upward_rounded,
color: state.type == TransactionType.expense
? AppColors.expense
: Theme.of(context).colorScheme.onSurface.withOpacity(0.4),
: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.4),
size: 20,
),
),
@@ -6,6 +6,7 @@ import '../../../core/l10n/app_strings.dart';
import '../../../core/l10n/locale_provider.dart';
import '../../../core/services/card_color_service.dart';
import '../../../core/services/haptic_service.dart';
import '../../../shared/models/account.dart';
import '../../../shared/models/transaction.dart';
import '../../settings/provider.dart';
import '../provider.dart';
@@ -37,6 +38,7 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
bool _showCurrencyDropdown = false;
bool _showLimitError = false;
bool _showDeleteDialog = false;
bool _showDuplicateError = false;
@override
void initState() {
@@ -77,6 +79,17 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
super.dispose();
}
bool _isDuplicateName(List<Account> accounts, String name) {
final trimmed = name.trim().toLowerCase();
return accounts.any((a) {
// When editing: ignore the account being edited itself
if (dash.editingAccount != null && a.id == dash.editingAccount!.id) {
return false;
}
return a.name.trim().toLowerCase() == trimmed;
});
}
@override
Widget build(BuildContext context) {
final mq = MediaQuery.of(widget.context);
@@ -96,14 +109,18 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
if (!dash.isAddingAccount) {
if (dash.editingAccount != null) {
final txs = ref.watch(accountFilteredTransactionsProvider);
final accountTxs = txs.where((t) => t.accountId == dash.editingAccount!.id);
final accountTxs = txs.where(
(t) => t.accountId == dash.editingAccount!.id,
);
previewBalance = accountTxs.fold(0.0, (sum, t) {
final converted = exchangeService.convert(
t.amount,
t.currencyCode,
dash.tempAccountCurrency, // convert directly from tx currency to selected dropdown currency
);
return t.type == TransactionType.income ? sum + converted : sum - converted;
return t.type == TransactionType.income
? sum + converted
: sum - converted;
});
} else {
// Fallback for edge cases
@@ -136,6 +153,57 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
child: const SizedBox.expand(),
),
),
// Duplicate Name Error Alert (Top)
if (_showDuplicateError)
Positioned(
top: mq.padding.top + 8,
left: 20,
right: 20,
child: GestureDetector(
onTap: () {}, // Prevent tap-through
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
decoration: BoxDecoration(
color: Colors.red.shade50,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: Colors.red.shade400,
width: 2,
),
boxShadow: [
BoxShadow(
color: Colors.red.withOpacity(0.3),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
Icon(
Icons.error_outline_rounded,
color: Colors.red.shade700,
size: 22,
),
const SizedBox(width: 12),
Expanded(
child: Text(
'Account name already exists',
style: TextStyle(
fontSize: 13,
color: Colors.red.shade900,
fontWeight: FontWeight.w600,
),
),
),
],
),
),
),
),
// Preview Card
Positioned(
top: cardTop,
@@ -187,7 +255,9 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
Positioned(
top: editorPanelTop + 62,
right: 34, // 20 (panel padding) + 14 (inner padding)
width: (MediaQuery.of(context).size.width - 68) * 0.25, // 25% of the inner row width
width:
(MediaQuery.of(context).size.width - 68) *
0.25, // 25% of the inner row width
child: Material(
elevation: 12,
borderRadius: BorderRadius.circular(12),
@@ -196,13 +266,16 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
color: Theme.of(widget.context).colorScheme.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.15),
color: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.15),
width: 1.5,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
children:
[
('USD', '\$'),
('EUR', ''),
('BYN', 'Br'),
@@ -223,7 +296,10 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
},
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
@@ -232,7 +308,9 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: isSelected ? const Color(0xFF7C6DED) : null,
color: isSelected
? const Color(0xFF7C6DED)
: null,
),
),
const SizedBox(width: 6),
@@ -242,7 +320,10 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
fontSize: 11,
color: isSelected
? const Color(0xFF7C6DED)
: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.6),
: Theme.of(widget.context)
.colorScheme
.onSurface
.withOpacity(0.6),
),
),
const SizedBox(width: 4),
@@ -263,12 +344,17 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
),
// Top Right Buttons (Delete & Close)
Positioned(
top: cardTop - 20, // Center buttons exactly on the top edge of the card
top:
cardTop -
20, // Center buttons exactly on the top edge of the card
right: 20,
child: Row( // REMOVED SafeArea to fix the vertical offset
child: Row(
// REMOVED SafeArea to fix the vertical offset
mainAxisSize: MainAxisSize.min,
children: [
if (!dash.isAddingAccount && (ref.watch(accountsProvider).valueOrNull?.length ?? 0) > 1) ...[
if (!dash.isAddingAccount &&
(ref.watch(accountsProvider).valueOrNull?.length ?? 0) >
1) ...[
GestureDetector(
onTap: () => setState(() => _showDeleteDialog = true),
child: Container(
@@ -329,7 +415,9 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
child: Material(
color: Colors.transparent,
child: AlertDialog(
backgroundColor: Theme.of(widget.context).colorScheme.surface,
backgroundColor: Theme.of(
widget.context,
).colorScheme.surface,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
@@ -339,7 +427,8 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
),
actions: [
TextButton(
onPressed: () => setState(() => _showDeleteDialog = false),
onPressed: () =>
setState(() => _showDeleteDialog = false),
child: const Text('Cancel'),
),
TextButton(
@@ -350,19 +439,32 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
dash.closeAccountOverlay(apply: false);
final txs = ref.read(transactionsProvider).valueOrNull ?? [];
final accountTxs = txs.where((t) => t.accountId == accountId).toList();
final txs =
ref
.read(transactionsProvider)
.valueOrNull ??
[];
final accountTxs = txs
.where((t) => t.accountId == accountId)
.toList();
for (final t in accountTxs) {
await ref.read(transactionsProvider.notifier).delete(t.id);
await ref
.read(transactionsProvider.notifier)
.delete(t.id);
}
await ref.read(accountRepositoryProvider).delete(accountId);
await ref
.read(accountRepositoryProvider)
.delete(accountId);
if (ref.read(hapticEnabledProvider)) {
HapticService.medium();
}
},
child: const Text('Delete', style: TextStyle(color: Colors.red)),
child: const Text(
'Delete',
style: TextStyle(color: Colors.red),
),
),
],
),
@@ -386,7 +488,9 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
color: Theme.of(widget.context).colorScheme.surface,
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.1),
color: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.1),
width: 1.5,
),
boxShadow: [
@@ -408,7 +512,9 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.6),
color: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.6),
),
),
const SizedBox(height: 8),
@@ -420,41 +526,70 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
flex: 3,
child: TextField(
controller: _nameController,
buildCounter: (context, {required currentLength, required isFocused, maxLength}) => null,
buildCounter:
(
context, {
required currentLength,
required isFocused,
maxLength,
}) => null,
style: const TextStyle(fontSize: 13),
decoration: InputDecoration(
hintText: 'Account name',
hintStyle: TextStyle(fontSize: 13, color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.4)),
hintStyle: TextStyle(
fontSize: 13,
color: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.4),
),
filled: true,
fillColor: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.05),
fillColor: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.05),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: (_showLimitError || _nameController.text.trim().isEmpty)
color:
(_showLimitError ||
_showDuplicateError ||
_nameController.text.trim().isEmpty)
? Colors.red
: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.15),
: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.15),
width: 1.5,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: (_showLimitError || _nameController.text.trim().isEmpty)
color:
(_showLimitError ||
_showDuplicateError ||
_nameController.text.trim().isEmpty)
? Colors.red
: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.15),
: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.15),
width: 1.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color: (_showLimitError || _nameController.text.trim().isEmpty)
color:
(_showLimitError ||
_showDuplicateError ||
_nameController.text.trim().isEmpty)
? Colors.red
: const Color(0xFF7C6DED),
width: 1.5,
),
),
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12),
contentPadding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 12,
),
),
),
),
@@ -469,12 +604,16 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
child: Container(
height: double.infinity,
decoration: BoxDecoration(
color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.05),
color: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.05),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: _showCurrencyDropdown
? const Color(0xFF7C6DED)
: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.15),
: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.15),
width: 1.5,
),
),
@@ -483,15 +622,30 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
[('USD', '\$'), ('EUR', ''), ('BYN', 'Br'), ('RUB', '')]
.firstWhere((c) => c.$1 == _selectedCurrency).$2,
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
[
('USD', '\$'),
('EUR', ''),
('BYN', 'Br'),
('RUB', ''),
]
.firstWhere(
(c) => c.$1 == _selectedCurrency,
)
.$2,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: 4),
Icon(
_showCurrencyDropdown ? Icons.arrow_drop_up : Icons.arrow_drop_down,
_showCurrencyDropdown
? Icons.arrow_drop_up
: Icons.arrow_drop_down,
size: 20,
color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.6),
color: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.6),
),
],
),
@@ -516,7 +670,9 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
color: Theme.of(widget.context).colorScheme.surface,
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: Theme.of(widget.context).colorScheme.onSurface.withOpacity(0.1),
color: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.1),
width: 1.5,
),
boxShadow: [
@@ -569,7 +725,9 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
isDimmed: isSolid,
onTap: () {
dash.setState(() {
if (isSolid) dash.tempGradientType = CardColorService.defaultGradient;
if (isSolid)
dash.tempGradientType =
CardColorService.defaultGradient;
dash.editingPrimary = true;
});
setPanelState(() {});
@@ -586,7 +744,9 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
isDimmed: isSolid,
onTap: () {
dash.setState(() {
if (isSolid) dash.tempGradientType = CardColorService.defaultGradient;
if (isSolid)
dash.tempGradientType =
CardColorService.defaultGradient;
dash.editingPrimary = false;
});
setPanelState(() {});
@@ -598,16 +758,17 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Container(
width: 1,
color: Theme.of(widget.context)
.colorScheme
.onSurface
.withOpacity(0.15),
color: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.15),
margin: const EdgeInsets.symmetric(vertical: 4),
),
),
Expanded(
child: GestureDetector(
onTap: isSolid ? null : () {
onTap: isSolid
? null
: () {
dash.setState(() {
dash.tempGradientType = GradientType.solid;
dash.editingPrimary = true;
@@ -618,7 +779,9 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
child: Container(
height: double.infinity,
padding: const EdgeInsets.symmetric(
horizontal: 4, vertical: 6),
horizontal: 4,
vertical: 6,
),
decoration: BoxDecoration(
color: isSolid
? const Color(0xFF7C6DED).withOpacity(0.15)
@@ -627,10 +790,9 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
border: Border.all(
color: isSolid
? const Color(0xFF7C6DED)
: Theme.of(widget.context)
.colorScheme
.onSurface
.withOpacity(0.2),
: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.2),
width: 1.5,
),
),
@@ -662,9 +824,8 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
child: LayoutBuilder(
builder: (lbCtx, constraints) {
const reservedBelow = 78.0;
final spectrumH =
(constraints.maxHeight - reservedBelow).clamp(
40.0, double.infinity);
final spectrumH = (constraints.maxHeight - reservedBelow)
.clamp(40.0, double.infinity);
return Column(
mainAxisSize: MainAxisSize.min,
@@ -704,7 +865,8 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
GestureDetector(
onTap: () {
dash.setState(
() => dash.editingPrimary = true);
() => dash.editingPrimary = true,
);
setPanelState(() {});
dash.overlayEntry?.markNeedsBuild();
},
@@ -721,10 +883,12 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
border: dash.editingPrimary
? Border.all(
color: Colors.white,
width: 2)
width: 2,
)
: Border.all(
color: Colors.transparent,
width: 2),
width: 2,
),
),
),
const SizedBox(width: 5),
@@ -742,7 +906,8 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
.withOpacity(
dash.editingPrimary
? 0.8
: 0.4),
: 0.4,
),
),
),
],
@@ -752,8 +917,9 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
if (!isSolid)
GestureDetector(
onTap: () {
dash.setState(() =>
dash.editingPrimary = false);
dash.setState(
() => dash.editingPrimary = false,
);
setPanelState(() {});
dash.overlayEntry?.markNeedsBuild();
},
@@ -774,7 +940,8 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
.withOpacity(
!dash.editingPrimary
? 0.8
: 0.4),
: 0.4,
),
),
),
const SizedBox(width: 5),
@@ -788,11 +955,13 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
border: !dash.editingPrimary
? Border.all(
color: Colors.white,
width: 2)
width: 2,
)
: Border.all(
color:
Colors.transparent,
width: 2),
width: 2,
),
),
),
],
@@ -827,10 +996,12 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
GradientType.solid => '',
};
final icon = switch (type) {
GradientType.linear => Icons.trending_flat_rounded,
GradientType.linear =>
Icons.trending_flat_rounded,
GradientType.linearReverse =>
Icons.swap_horiz_rounded,
GradientType.radial => Icons.blur_circular_rounded,
GradientType.radial =>
Icons.blur_circular_rounded,
GradientType.sweep => Icons.rotate_right_rounded,
GradientType.solid => Icons.square_rounded,
};
@@ -840,18 +1011,21 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
child: GestureDetector(
onTap: () {
dash.setState(
() => dash.tempGradientType = type);
() => dash.tempGradientType = type,
);
setPanelState(() {});
dash.overlayEntry?.markNeedsBuild();
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 150),
padding:
const EdgeInsets.symmetric(vertical: 5),
padding: const EdgeInsets.symmetric(
vertical: 5,
),
decoration: BoxDecoration(
color: isSelected
? const Color(0xFF7C6DED)
.withOpacity(0.15)
? const Color(
0xFF7C6DED,
).withOpacity(0.15)
: Theme.of(widget.context)
.colorScheme
.onSurface
@@ -867,14 +1041,16 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon,
Icon(
icon,
size: 15,
color: isSelected
? const Color(0xFF7C6DED)
: Theme.of(widget.context)
.colorScheme
.onSurface
.withOpacity(0.45)),
.withOpacity(0.45),
),
const SizedBox(height: 2),
Text(
label,
@@ -897,7 +1073,8 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
),
),
);
}).toList(),
})
.toList(),
),
),
),
@@ -921,28 +1098,30 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
dash.tempSecondary = defS;
dash.tempPrimaryHSV = HSVColor.fromColor(defP);
dash.tempSecondaryHSV = HSVColor.fromColor(defS);
dash.tempGradientType = CardColorService.defaultGradient;
dash.tempGradientType =
CardColorService.defaultGradient;
});
setPanelState(() {});
dash.overlayEntry?.markNeedsBuild();
},
icon: const Icon(Icons.restart_alt_rounded, size: 15),
label: Text(s.reset,
style: const TextStyle(fontSize: 13)),
label: Text(
s.reset,
style: const TextStyle(fontSize: 13),
),
style: OutlinedButton.styleFrom(
foregroundColor: Theme.of(widget.context)
.colorScheme
.onSurface
.withOpacity(0.7),
foregroundColor: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.7),
side: BorderSide(
color: Theme.of(widget.context)
.colorScheme
.onSurface
.withOpacity(0.2),
color: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.2),
),
padding: const EdgeInsets.symmetric(vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
borderRadius: BorderRadius.circular(12),
),
),
),
),
@@ -953,29 +1132,53 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
onPressed: _nameController.text.trim().isEmpty
? null
: () {
// Add haptic feedback here
final accounts =
ProviderScope.containerOf(
widget.context,
).read(accountsProvider).valueOrNull ??
[];
if (_isDuplicateName(
accounts,
_nameController.text,
)) {
setState(() => _showDuplicateError = true);
Future.delayed(
const Duration(seconds: 3),
() {
if (mounted)
setState(
() => _showDuplicateError = false,
);
},
);
return; // block saving
}
HapticService.light();
dash.closeAccountOverlay(apply: true);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF7C6DED),
foregroundColor: Colors.white,
disabledBackgroundColor: Theme.of(widget.context)
.colorScheme
.onSurface
.withOpacity(0.12),
disabledForegroundColor: Theme.of(widget.context)
.colorScheme
.onSurface
.withOpacity(0.38),
disabledBackgroundColor: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.12),
disabledForegroundColor: Theme.of(
widget.context,
).colorScheme.onSurface.withOpacity(0.38),
padding: const EdgeInsets.symmetric(vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
borderRadius: BorderRadius.circular(12),
),
),
child: Text(
dash.isAddingAccount ? s.addAccount : s.apply,
style: const TextStyle(
fontWeight: FontWeight.w700, fontSize: 14)),
fontWeight: FontWeight.w700,
fontSize: 14,
),
),
),
),
],
@@ -1013,7 +1216,9 @@ class PanelTab extends StatelessWidget {
: (isDark ? Colors.white24 : const Color(0xFFCCCCDD));
final textColor = isSelected
? const Color(0xFF7C6DED)
: (isDark ? Colors.white60 : Theme.of(context).colorScheme.onSurface.withOpacity(0.5));
: (isDark
? Colors.white60
: Theme.of(context).colorScheme.onSurface.withOpacity(0.5));
return GestureDetector(
onTap: onTap,
@@ -1025,12 +1230,11 @@ class PanelTab extends StatelessWidget {
width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
decoration: BoxDecoration(
color: isSelected ? const Color(0xFF7C6DED).withOpacity(0.15) : Colors.transparent,
color: isSelected
? const Color(0xFF7C6DED).withOpacity(0.15)
: Colors.transparent,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: borderColor,
width: 1.5,
),
border: Border.all(color: borderColor, width: 1.5),
),
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -1057,8 +1261,9 @@ class PanelTab extends StatelessWidget {
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight:
isSelected ? FontWeight.w600 : FontWeight.normal,
fontWeight: isSelected
? FontWeight.w600
: FontWeight.normal,
color: textColor,
),
),