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