add support for badges and notifications, adjust nav pages
All checks were successful
Release / meta (push) Successful in 7s
Release / linux-build (push) Successful in 6m49s
Release / android-build (push) Successful in 15m55s
Release / release-master (push) Successful in 24s
Release / release-dev (push) Successful in 26s

This commit is contained in:
2025-12-26 18:36:37 +00:00
parent 44d79e7c28
commit 4bd6f0bbed
16 changed files with 1161 additions and 144 deletions

View File

@@ -244,47 +244,7 @@ class _TractionPageState extends State<TractionPage> {
],
),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
tooltip: 'Refresh',
onPressed: _refreshTraction,
icon: const Icon(Icons.refresh),
),
if (_hasClassQuery) ...[
const SizedBox(width: 8),
FilledButton.tonalIcon(
onPressed: _toggleClassStatsPanel,
icon: Icon(
_showClassStatsPanel ? Icons.bar_chart : Icons.insights,
),
label: Text(
_showClassStatsPanel ? 'Hide class stats' : 'Class stats',
),
),
],
const SizedBox(width: 8),
FilledButton.icon(
onPressed: () async {
final createdClass = await context.push<String>(
'/traction/new',
);
if (createdClass != null && createdClass.isNotEmpty) {
_classController.text = createdClass;
_selectedClass = createdClass;
if (mounted) {
_refreshTraction();
}
} else if (mounted && createdClass == '') {
_refreshTraction();
}
},
icon: const Icon(Icons.add),
label: const Text('New Traction'),
),
],
),
_buildHeaderActions(context, isMobile),
],
),
const SizedBox(height: 12),
@@ -546,6 +506,78 @@ class _TractionPageState extends State<TractionPage> {
return (_selectedClass ?? _classController.text).trim().isNotEmpty;
}
Widget _buildHeaderActions(BuildContext context, bool isMobile) {
final refreshButton = IconButton(
tooltip: 'Refresh',
onPressed: _refreshTraction,
icon: const Icon(Icons.refresh),
);
final classStatsButton = !_hasClassQuery
? null
: FilledButton.tonalIcon(
onPressed: _toggleClassStatsPanel,
icon: Icon(
_showClassStatsPanel ? Icons.bar_chart : Icons.insights,
),
label: Text(
_showClassStatsPanel ? 'Hide class stats' : 'Class stats',
),
);
final newTractionButton = FilledButton.icon(
onPressed: () async {
final createdClass = await context.push<String>(
'/traction/new',
);
if (!mounted) return;
if (createdClass != null && createdClass.isNotEmpty) {
_classController.text = createdClass;
_selectedClass = createdClass;
_refreshTraction();
} else if (createdClass == '') {
_refreshTraction();
}
},
icon: const Icon(Icons.add),
label: const Text('New Traction'),
);
final desktopActions = [
refreshButton,
if (classStatsButton != null) classStatsButton,
newTractionButton,
];
final mobileActions = [
newTractionButton,
if (classStatsButton != null) classStatsButton,
refreshButton,
];
if (isMobile) {
return Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
for (var i = 0; i < mobileActions.length; i++) ...[
if (i > 0) const SizedBox(height: 8),
Align(
alignment: Alignment.centerRight,
child: mobileActions[i],
),
],
],
);
}
return Wrap(
spacing: 8,
runSpacing: 8,
crossAxisAlignment: WrapCrossAlignment.center,
children: desktopActions,
);
}
Future<void> _toggleClassStatsPanel() async {
if (!_hasClassQuery) return;
final targetState = !_showClassStatsPanel;