Igelliste sortiert, Notification bei Änderungen auf Detail Screen

This commit is contained in:
2025-11-04 13:06:01 +01:00
parent 4cb18aba41
commit a3b8900047
5 changed files with 40 additions and 6 deletions
+4 -1
View File
@@ -9,7 +9,10 @@ class IgelRepository {
Future<List<Igel>> list() async {
final res = await api.get<dynamic>('/igel');
final list = (res as List).cast<Map<String, dynamic>>();
return list.map(Igel.fromMap).toList();
final items = list.map(Igel.fromMap).toList();
// Sortiere nach Name (fallunabhängig, A→Z)
items.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
return items;
}
Future<Igel> create(String name,
@@ -123,6 +123,15 @@ class _IgelDetailState extends ConsumerState<IgelDetailScreen> {
_shareService = ShareService(baseUrl: base);
_loadAll();
// Rebuild when text fields change so _basicsChanged updates and Save becomes enabled.
void onAnyTextChanged() {
if (mounted) setState(() {});
}
nameC.addListener(onAnyTextChanged);
featureC.addListener(onAnyTextChanged);
noteC.addListener(onAnyTextChanged);
locationC.addListener(onAnyTextChanged);
}
@override
@@ -24,6 +24,7 @@ class _ShareBottomSheetState extends State<ShareBottomSheet> {
final _emailCtrl = TextEditingController();
String _role = 'viewer';
Future<List<Share>>? _future;
final ScrollController _scrollCtrl = ScrollController();
@override
void initState() {
@@ -63,6 +64,13 @@ class _ShareBottomSheetState extends State<ShareBottomSheet> {
await _reload();
}
@override
void dispose() {
_scrollCtrl.dispose();
_emailCtrl.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final canInvite = widget.accessRole == AccessRole.owner;
@@ -111,10 +119,16 @@ class _ShareBottomSheetState extends State<ShareBottomSheet> {
if (items.isEmpty) {
return const Center(child: Text('Keine Freigaben'));
}
return ListView.separated(
return ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
child: Scrollbar(
controller: _scrollCtrl,
child: ListView.separated(
controller: _scrollCtrl,
primary: false,
itemCount: items.length,
separatorBuilder: (_, __) => const Divider(height: 1),
itemBuilder: (ctx, i) {
itemBuilder: (ctx, i) {
final s = items[i];
// Priorität: pending -> invitedEmail, accepted -> targetEmail
@@ -126,7 +140,7 @@ class _ShareBottomSheetState extends State<ShareBottomSheet> {
? s.targetEmail!
: 'Unbekannt');
return ListTile(
return ListTile(
leading: Icon(
s.status == 'accepted'
? Icons.check_circle_outline
@@ -158,7 +172,9 @@ class _ShareBottomSheetState extends State<ShareBottomSheet> {
],
),
);
},
},
),
),
);
},
),
@@ -69,7 +69,7 @@ class _SharedIgelPageState extends State<SharedIgelPage> {
subtitle: Text('Rolle: ${it.role} · Owner: $ownerLabel'),
onTap: () {
// Detail öffnen; 'from=shared' sorgt dafür, dass der Back-Button wieder hierher führt
context.push('/igel/${it.id}?role=${it.role}&from=shared}');
context.push('/igel/${it.id}?role=${it.role}&from=shared');
},
);
},
+6
View File
@@ -38,6 +38,12 @@ class AppScrollBehavior extends MaterialScrollBehavior {
PointerDeviceKind.trackpad,
PointerDeviceKind.unknown,
};
// Disable automatic Scrollbar wrapping; use explicit Scrollbars where needed.
@override
Widget buildScrollbar(BuildContext context, Widget child, ScrollableDetails details) {
return child;
}
}
void main() {