Igelliste sortiert, Notification bei Änderungen auf Detail Screen
This commit is contained in:
@@ -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');
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user