feat: Refactor routing and navigation structure with StatefulShellRoute
Test Suite / test (24.15.0) (push) Has been cancelled

- Introduced a new function `_shellBranchIndexForPath` to determine the index of the shell branch based on the path.
- Replaced `ShellRoute` with `StatefulShellRoute.indexedStack` for better state management during navigation.
- Updated `AppShell` to handle navigation path changes and integrate with the new routing structure.
- Organized routes into `StatefulShellBranch` for better modularity and clarity.
- Enhanced admin panel functionality with improved alias management and UI updates.
- Added new methods in `ReceiptImportSessionNotifier` for managing selected items and edits more efficiently.
- Improved UI components in receipt import and admin panels for better performance and user experience.
- Added PageStorageKeys to various ListViews to maintain scroll positions across navigation.
- Documented performance goals and profiling strategies in a new PERFORMANCE.md file.
This commit is contained in:
Nils-Johan Gynther
2026-05-08 12:51:38 +02:00
parent 73309cb110
commit 0873fa42bb
12 changed files with 625 additions and 285 deletions
@@ -166,6 +166,49 @@ class _AdminAliasesPanelState extends ConsumerState<AdminAliasesPanel> {
alias.displayProductName.toLowerCase().contains(query);
}).toList();
Widget buildAliasCard(ReceiptAlias alias) {
return Card(
child: ListTile(
leading: const Icon(Icons.link_outlined),
title: Text(alias.receiptName, style: const TextStyle(fontWeight: FontWeight.w500)),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'${alias.displayProductName}',
style: const TextStyle(fontWeight: FontWeight.w400),
),
Text(
'Produkt-ID: ${alias.productId}',
style: TextStyle(
fontSize: 11,
color: Theme.of(context).colorScheme.outline,
),
),
],
),
trailing: IconButton(
onPressed: () => _removeAlias(alias),
icon: const Icon(Icons.delete_outline),
tooltip: 'Ta bort alias',
color: Theme.of(context).colorScheme.error,
),
),
);
}
Widget buildAliasList({EdgeInsetsGeometry padding = EdgeInsets.zero}) {
return ListView.builder(
padding: padding,
itemCount: filteredAliases.length,
itemBuilder: (context, index) {
final alias = filteredAliases[index];
return buildAliasCard(alias);
},
);
}
final content = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -230,51 +273,37 @@ class _AdminAliasesPanelState extends ConsumerState<AdminAliasesPanel> {
onChanged: (value) => setState(() => _search = value),
),
const SizedBox(height: 12),
if (filteredAliases.isEmpty)
const Text('Inga alias hittades.')
else
...filteredAliases.map(
(alias) => Card(
child: ListTile(
leading: const Icon(Icons.link_outlined),
title: Text(alias.receiptName, style: const TextStyle(fontWeight: FontWeight.w500)),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'${alias.displayProductName}',
style: const TextStyle(fontWeight: FontWeight.w400),
),
Text(
'Produkt-ID: ${alias.productId}',
style: TextStyle(
fontSize: 11,
color: Theme.of(context).colorScheme.outline,
),
),
],
),
trailing: IconButton(
onPressed: () => _removeAlias(alias),
icon: const Icon(Icons.delete_outline),
tooltip: 'Ta bort alias',
color: Theme.of(context).colorScheme.error,
),
),
),
),
if (filteredAliases.isEmpty) const Text('Inga alias hittades.'),
],
);
if (!widget.embedded) {
if (filteredAliases.isEmpty) {
return ListView(
padding: const EdgeInsets.all(16),
children: [content],
);
}
return ListView(
padding: const EdgeInsets.all(16),
children: [content],
children: [
content,
const SizedBox(height: 8),
...filteredAliases.map(buildAliasCard),
],
);
}
return content;
if (filteredAliases.isEmpty) return content;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
content,
const SizedBox(height: 8),
Expanded(child: buildAliasList()),
],
);
}
}
@@ -93,8 +93,8 @@ class _AdminPendingProductsPanelState
}
final content = ListView.builder(
shrinkWrap: widget.embedded,
physics: widget.embedded ? const NeverScrollableScrollPhysics() : null,
shrinkWrap: false,
physics: null,
itemCount: _products.length,
itemBuilder: (context, index) {
final product = _products[index];
@@ -148,7 +148,7 @@ class _AdminPendingProductsPanelState
style: theme.textTheme.bodyMedium,
),
const SizedBox(height: 12),
content,
Expanded(child: content),
],
);
}
@@ -738,7 +738,10 @@ class _AdminProductsPanelState extends ConsumerState<AdminProductsPanel> {
);
}
return content;
return ListView(
padding: EdgeInsets.zero,
children: [content],
);
}
}
@@ -328,10 +328,8 @@ class _AdminUsersPanelState extends ConsumerState<AdminUsersPanel> {
}
final list = ListView.builder(
shrinkWrap: widget.embedded,
physics: widget.embedded
? const NeverScrollableScrollPhysics()
: null,
shrinkWrap: false,
physics: null,
padding: widget.embedded
? EdgeInsets.zero
: const EdgeInsets.fromLTRB(16, 8, 16, 80),
@@ -376,7 +374,7 @@ class _AdminUsersPanelState extends ConsumerState<AdminUsersPanel> {
label: Text(context.l10n.adminNewUser),
),
const SizedBox(height: 16),
list,
Expanded(child: list),
],
);
}