chart improvements, synch working

This commit is contained in:
davmar
2026-09-09 23:31:30 +02:00
parent e35a4d6893
commit 91d6dc9e54
4 changed files with 144 additions and 103 deletions
+88 -61
View File
@@ -491,9 +491,8 @@ class _TransactionFilterDialogState extends State<_TransactionFilterDialog> {
late final TextEditingController _queryController;
late final TextEditingController _labelSearchController;
late final TextEditingController _exactAmountController;
late double _amountCeil;
late RangeValues _amountRange;
var _rangeTouched = false;
late final TextEditingController _minAmountController;
late final TextEditingController _maxAmountController;
@override
void initState() {
@@ -505,28 +504,20 @@ class _TransactionFilterDialogState extends State<_TransactionFilterDialog> {
_queryController = TextEditingController(text: initial.query);
_labelSearchController = TextEditingController();
var maxAbs = 0.0;
for (final tx in widget.transactions) {
final abs = tx.amount.abs();
if (abs > maxAbs) maxAbs = abs;
}
_amountCeil = maxAbs <= 0 ? 100.0 : (maxAbs * 1.05).ceilToDouble();
if (_amountCeil < 1) _amountCeil = 1;
final exact = initial.amountRange.exact;
_exactAmountController = TextEditingController(
text: exact == null ? '' : _formatAmountInput(exact),
);
final min = initial.amountRange.min ?? 0;
final max = initial.amountRange.max ?? _amountCeil;
_amountRange = RangeValues(
min.clamp(0, _amountCeil),
max.clamp(0, _amountCeil),
_minAmountController = TextEditingController(
text: exact == null && initial.amountRange.min != null
? _formatAmountInput(initial.amountRange.min!)
: '',
);
_maxAmountController = TextEditingController(
text: exact == null && initial.amountRange.max != null
? _formatAmountInput(initial.amountRange.max!)
: '',
);
_rangeTouched =
exact == null && (initial.amountRange.min != null ||
initial.amountRange.max != null);
}
@override
@@ -534,6 +525,8 @@ class _TransactionFilterDialogState extends State<_TransactionFilterDialog> {
_queryController.dispose();
_labelSearchController.dispose();
_exactAmountController.dispose();
_minAmountController.dispose();
_maxAmountController.dispose();
super.dispose();
}
@@ -544,12 +537,21 @@ class _TransactionFilterDialogState extends State<_TransactionFilterDialog> {
return value.toStringAsFixed(2);
}
double? _parseExactAmount() {
final raw = _exactAmountController.text.trim().replaceAll(',', '.');
double? _parseAmountField(TextEditingController controller) {
final raw = controller.text.trim().replaceAll(',', '.');
if (raw.isEmpty) return null;
return double.tryParse(raw);
}
void _clearRangeFields() {
_minAmountController.clear();
_maxAmountController.clear();
}
void _clearExactField() {
_exactAmountController.clear();
}
void _toggle(String id, bool selected) {
setState(() {
if (selected) {
@@ -561,15 +563,18 @@ class _TransactionFilterDialogState extends State<_TransactionFilterDialog> {
}
TxAmountRange get _amountResult {
final exact = _parseExactAmount();
final exact = _parseAmountField(_exactAmountController);
if (exact != null && exact >= 0) {
return TxAmountRange(exact: exact);
}
if (!_rangeTouched) return const TxAmountRange();
final start = _amountRange.start <= 0.009 ? null : _amountRange.start;
final end =
_amountRange.end >= _amountCeil - 0.009 ? null : _amountRange.end;
return TxAmountRange(min: start, max: end);
final min = _parseAmountField(_minAmountController);
final max = _parseAmountField(_maxAmountController);
if (min == null && max == null) return const TxAmountRange();
// Swap if user typed max < min.
if (min != null && max != null && min > max) {
return TxAmountRange(min: max, max: min);
}
return TxAmountRange(min: min, max: max);
}
void _apply() {
@@ -721,7 +726,7 @@ class _TransactionFilterDialogState extends State<_TransactionFilterDialog> {
: IconButton(
tooltip: 'Clear exact',
onPressed: () {
_exactAmountController.clear();
_clearExactField();
setState(() {});
},
icon: const Icon(Icons.clear, size: 18),
@@ -729,8 +734,9 @@ class _TransactionFilterDialogState extends State<_TransactionFilterDialog> {
),
onChanged: (_) {
setState(() {
_rangeTouched = false;
_amountRange = RangeValues(0, _amountCeil);
if (_exactAmountController.text.trim().isNotEmpty) {
_clearRangeFields();
}
});
},
),
@@ -742,43 +748,64 @@ class _TransactionFilterDialogState extends State<_TransactionFilterDialog> {
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 4),
Text(
_rangeTouched
? '${_amountRange.start.toStringAsFixed(0)} – ${_amountRange.end.toStringAsFixed(0)}'
: 'Any amount',
style: theme.textTheme.bodySmall?.copyWith(
color: colors.onSurfaceVariant,
),
),
RangeSlider(
values: _amountRange,
min: 0,
max: _amountCeil,
labels: RangeLabels(
_amountRange.start.toStringAsFixed(0),
_amountRange.end.toStringAsFixed(0),
),
onChanged: (values) {
setState(() {
_exactAmountController.clear();
_rangeTouched = true;
_amountRange = values;
});
},
const SizedBox(height: 6),
Row(
children: [
Expanded(
child: TextField(
controller: _minAmountController,
keyboardType: const TextInputType.numberWithOptions(
decimal: true,
),
decoration: const InputDecoration(
isDense: true,
labelText: 'Min',
hintText: '0',
),
onChanged: (_) {
setState(() {
if (_minAmountController.text.trim().isNotEmpty ||
_maxAmountController.text.trim().isNotEmpty) {
_clearExactField();
}
});
},
),
),
const SizedBox(width: 12),
Expanded(
child: TextField(
controller: _maxAmountController,
keyboardType: const TextInputType.numberWithOptions(
decimal: true,
),
decoration: const InputDecoration(
isDense: true,
labelText: 'Max',
hintText: '∞',
),
onChanged: (_) {
setState(() {
if (_minAmountController.text.trim().isNotEmpty ||
_maxAmountController.text.trim().isNotEmpty) {
_clearExactField();
}
});
},
),
),
],
),
Align(
alignment: Alignment.centerRight,
child: TextButton(
onPressed: !_rangeTouched
onPressed: _minAmountController.text.trim().isEmpty &&
_maxAmountController.text.trim().isEmpty
? null
: () {
setState(() {
_rangeTouched = false;
_amountRange = RangeValues(0, _amountCeil);
});
setState(_clearRangeFields);
},
child: const Text('Reset range'),
child: const Text('Clear range'),
),
),
const SizedBox(height: 8),