custom filters
This commit is contained in:
@@ -34,28 +34,33 @@ Future<HomeFilterWidget?> composeHomeFilterWidget(
|
||||
HomeFilterWidget? existing,
|
||||
bool skipFilterDialog = false,
|
||||
}) async {
|
||||
// Capture before any modal opens — the caller's element may be rebuilt.
|
||||
final bank = context.read<BankController>();
|
||||
final meta = context.read<TxMetaController>();
|
||||
final navigator = Navigator.of(context);
|
||||
|
||||
TransactionFilterSelection? filter = initialFilter ?? existing?.filter;
|
||||
if (!skipFilterDialog || filter == null) {
|
||||
final bank = context.read<BankController>();
|
||||
final meta = context.read<TxMetaController>();
|
||||
filter = await showTransactionFilterDialog(
|
||||
context,
|
||||
navigator.context,
|
||||
available: meta.allLabels,
|
||||
transactions: bank.transactions,
|
||||
initial: filter ?? const TransactionFilterSelection(labelIds: {}),
|
||||
);
|
||||
if (filter == null || !context.mounted) return null;
|
||||
if (filter == null || !navigator.mounted) return null;
|
||||
}
|
||||
|
||||
if (!navigator.mounted) return null;
|
||||
final name = await promptFavouriteName(
|
||||
context,
|
||||
navigator.context,
|
||||
initial: existing?.name ?? '',
|
||||
title: existing == null ? 'Name this widget' : 'Rename widget',
|
||||
);
|
||||
if (name == null || !context.mounted) return null;
|
||||
if (name == null || !navigator.mounted) return null;
|
||||
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
if (!context.mounted) return null;
|
||||
// Let the name dialog finish popping before presenting options.
|
||||
await WidgetsBinding.instance.endOfFrame;
|
||||
if (!navigator.mounted) return null;
|
||||
|
||||
final draft = existing == null
|
||||
? HomeFilterWidget.fromFilter(name: name, filter: filter)
|
||||
@@ -68,7 +73,7 @@ Future<HomeFilterWidget?> composeHomeFilterWidget(
|
||||
monthOnly: filter.monthOnly,
|
||||
);
|
||||
|
||||
return _promptWidgetMetrics(context, draft);
|
||||
return _promptWidgetMetrics(navigator.context, draft);
|
||||
}
|
||||
|
||||
Future<HomeFilterWidget?> _promptWidgetMetrics(
|
||||
@@ -78,6 +83,7 @@ Future<HomeFilterWidget?> _promptWidgetMetrics(
|
||||
var showIn = draft.showAmountIn;
|
||||
var showOut = draft.showAmountOut;
|
||||
var showCount = draft.showCount;
|
||||
var halfWidth = draft.halfWidth;
|
||||
|
||||
return showDialog<HomeFilterWidget>(
|
||||
context: context,
|
||||
@@ -86,7 +92,7 @@ Future<HomeFilterWidget?> _promptWidgetMetrics(
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return AlertDialog(
|
||||
title: const Text('Widget metrics'),
|
||||
title: const Text('Widget options'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@@ -111,6 +117,13 @@ Future<HomeFilterWidget?> _promptWidgetMetrics(
|
||||
onChanged: (value) =>
|
||||
setState(() => showCount = value ?? false),
|
||||
),
|
||||
SwitchListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: const Text('Half width'),
|
||||
subtitle: const Text('Share the row with another widget'),
|
||||
value: halfWidth,
|
||||
onChanged: (value) => setState(() => halfWidth = value),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
@@ -126,6 +139,7 @@ Future<HomeFilterWidget?> _promptWidgetMetrics(
|
||||
showAmountIn: showIn,
|
||||
showAmountOut: showOut,
|
||||
showCount: showCount,
|
||||
halfWidth: halfWidth,
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -161,11 +175,11 @@ class _HomeWidgetsSheet extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Home widgets', style: theme.textTheme.titleLarge),
|
||||
Text('Home layout', style: theme.textTheme.titleLarge),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'Pin saved transaction filters on Home. Check Display to '
|
||||
'show a widget; edit metrics or remove anytime.',
|
||||
'Choose which built-in sections and filter widgets appear '
|
||||
'on Home. Check Display to show an item.',
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
@@ -174,12 +188,51 @@ class _HomeWidgetsSheet extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
|
||||
padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
|
||||
child: Column(
|
||||
children: [
|
||||
CheckboxListTile(
|
||||
value: prefs.homeBuiltInSections.showTransactions,
|
||||
onChanged: (value) {
|
||||
if (value == null) return;
|
||||
prefs.setHomeShowTransactions(value);
|
||||
},
|
||||
title: const Text('Transactions'),
|
||||
subtitle: const Text('Total transaction count'),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
),
|
||||
CheckboxListTile(
|
||||
value: prefs.homeBuiltInSections.showCategorized,
|
||||
onChanged: (value) {
|
||||
if (value == null) return;
|
||||
prefs.setHomeShowCategorized(value);
|
||||
},
|
||||
title: const Text('Categorized'),
|
||||
subtitle: const Text('Labeled transaction count'),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
),
|
||||
CheckboxListTile(
|
||||
value: prefs.homeBuiltInSections.showRecent,
|
||||
onChanged: (value) {
|
||||
if (value == null) return;
|
||||
prefs.setHomeShowRecent(value);
|
||||
},
|
||||
title: const Text('Recent transactions'),
|
||||
subtitle: const Text('Latest activity list'),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
final preferences = context.read<PreferencesController>();
|
||||
final created = await composeHomeFilterWidget(context);
|
||||
if (created == null || !context.mounted) return;
|
||||
await prefs.addHomeFilterWidget(created);
|
||||
if (created == null) return;
|
||||
await preferences.addHomeFilterWidget(created);
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
label: const Text('Add filter widget'),
|
||||
@@ -192,8 +245,8 @@ class _HomeWidgetsSheet extends StatelessWidget {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Text(
|
||||
'No widgets yet. Add a filter, or save one from '
|
||||
'Transactions.',
|
||||
'No filter widgets yet. Add one here, or save a '
|
||||
'filter from Transactions.',
|
||||
textAlign: TextAlign.center,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
@@ -216,17 +269,35 @@ class _HomeWidgetsSheet extends StatelessWidget {
|
||||
value,
|
||||
);
|
||||
},
|
||||
secondary: IconButton(
|
||||
tooltip: 'Edit',
|
||||
onPressed: () async {
|
||||
final updated = await composeHomeFilterWidget(
|
||||
context,
|
||||
existing: widgetItem,
|
||||
);
|
||||
if (updated == null || !context.mounted) return;
|
||||
await prefs.updateHomeFilterWidget(updated);
|
||||
secondary: PopupMenuButton<String>(
|
||||
tooltip: 'Widget actions',
|
||||
onSelected: (value) async {
|
||||
if (value == 'edit') {
|
||||
final preferences =
|
||||
context.read<PreferencesController>();
|
||||
final updated =
|
||||
await composeHomeFilterWidget(
|
||||
context,
|
||||
existing: widgetItem,
|
||||
);
|
||||
if (updated == null) return;
|
||||
await preferences
|
||||
.updateHomeFilterWidget(updated);
|
||||
} else if (value == 'delete') {
|
||||
await prefs
|
||||
.removeHomeFilterWidget(widgetItem.id);
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.edit_outlined),
|
||||
itemBuilder: (context) => const [
|
||||
PopupMenuItem(
|
||||
value: 'edit',
|
||||
child: Text('Edit'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'delete',
|
||||
child: Text('Delete'),
|
||||
),
|
||||
],
|
||||
),
|
||||
title: Text(widgetItem.name),
|
||||
subtitle: Text(
|
||||
@@ -254,6 +325,7 @@ class _HomeWidgetsSheet extends StatelessWidget {
|
||||
if (w.showAmountIn) 'In',
|
||||
if (w.showAmountOut) 'Out',
|
||||
if (w.showCount) 'Count',
|
||||
if (w.halfWidth) 'Half',
|
||||
];
|
||||
return parts.isEmpty ? 'All transactions' : parts.join(' · ');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user