chart improvements, synch working
This commit is contained in:
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user