Files
listenmeister/lib/pages/lists.dart
2025-10-26 19:55:33 +01:00

251 lines
6.7 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:listenmeister/main.dart';
import 'package:listenmeister/models/models.dart';
import 'package:listenmeister/pages/list_detail.dart';
import 'package:listenmeister/providers/theme.dart';
class ListsPage extends StatefulWidget {
const ListsPage({super.key});
@override
State<ListsPage> createState() => _ListsPageState();
}
class _ListsPageState extends State<ListsPage> {
late Future<List<Liste>> _listsFuture;
StreamSubscription? _listsSubscription;
@override
void initState() {
super.initState();
_loadLists();
_listsSubscription = apiService.watchLists().listen((_) {
_loadLists();
});
}
@override
void dispose() {
_listsSubscription?.cancel();
super.dispose();
}
void _loadLists() {
_listsFuture = apiService.getLists();
setState(() {});
}
Future<void> _showListMenu(Liste list) async {
final String? result = await showModalBottomSheet<String>(
context: context,
builder: (c) => Wrap(
children: <Widget>[
ListTile(
leading: const Icon(Icons.edit),
title: const Text('Umbenennen'),
onTap: () => Navigator.pop(c, 'edit'),
),
ListTile(
leading: const Icon(Icons.delete),
title: const Text('Löschen'),
onTap: () => Navigator.pop(c, 'delete'),
),
],
),
);
if (result == 'edit') {
_editList(list);
} else if (result == 'delete') {
_deleteList(list);
}
}
Future<void> _editList(Liste list) async {
final TextEditingController titleCtl = TextEditingController(
text: list.title,
);
final bool? ok = await showDialog<bool>(
context: context,
builder: (c) => AlertDialog(
title: const Text('Liste umbenennen'),
content: TextField(
controller: titleCtl,
decoration: const InputDecoration(labelText: 'Titel'),
autofocus: true,
),
actions: [
TextButton(
onPressed: () => Navigator.pop(c, false),
child: const Text('Abbrechen'),
),
TextButton(
onPressed: () => Navigator.pop(c, true),
child: const Text('Speichern'),
),
],
),
);
if (ok != true || titleCtl.text.trim().isEmpty) return;
try {
await apiService.updateList(list.id, title: titleCtl.text.trim());
_loadLists();
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Fehler: $e')));
}
}
}
Future<void> _deleteList(Liste list) async {
final bool? ok = await showDialog<bool>(
context: context,
builder: (c) => AlertDialog(
title: const Text('Liste löschen?'),
content: Text(
'Möchtest du die Liste "${list.title}" wirklich löschen?',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(c, false),
child: const Text('Abbrechen'),
),
TextButton(
onPressed: () => Navigator.pop(c, true),
child: const Text('Löschen'),
),
],
),
);
if (ok != true) return;
try {
await apiService.deleteList(list.id);
_loadLists();
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Fehler: $e')));
}
}
}
Future<void> _createList() async {
final TextEditingController titleCtl = TextEditingController();
final bool? ok = await showDialog<bool>(
context: context,
builder: (c) => AlertDialog(
title: const Text('Neue Liste'),
content: TextField(
controller: titleCtl,
decoration: const InputDecoration(labelText: 'Titel'),
autofocus: true,
),
actions: [
TextButton(
onPressed: () => Navigator.pop(c, false),
child: const Text('Abbrechen'),
),
TextButton(
onPressed: () => Navigator.pop(c, true),
child: const Text('Erstellen'),
),
],
),
);
if (ok != true || titleCtl.text.trim().isEmpty) return;
try {
await apiService.createList(titleCtl.text.trim());
_loadLists();
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Fehler: $e')));
}
}
}
void _openList(Liste list) {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => ListDetailPage(list: list)),
).then((_) => _loadLists());
}
@override
Widget build(BuildContext context) {
final ThemeProvider themeProvider = Provider.of<ThemeProvider>(
context,
listen: false,
);
return Scaffold(
appBar: AppBar(
title: const Text('Deine Listen'),
actions: [
PopupMenuButton<ThemeMode>(
onSelected: (mode) => themeProvider.setThemeMode(mode),
itemBuilder: (context) => [
const PopupMenuItem(value: ThemeMode.light, child: Text('Hell')),
const PopupMenuItem(value: ThemeMode.dark, child: Text('Dunkel')),
const PopupMenuItem(
value: ThemeMode.system,
child: Text('System'),
),
],
icon: const Icon(Icons.brightness_6_outlined),
),
IconButton(
tooltip: 'Ausloggen',
icon: const Icon(Icons.logout),
onPressed: () {
apiService.logout();
},
),
],
),
body: FutureBuilder<List<Liste>>(
future: _listsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(child: Text('Fehler: ${snapshot.error}'));
}
final lists = snapshot.data ?? [];
if (lists.isEmpty) {
return const Center(child: Text('Keine Listen gefunden.'));
}
return ListView.builder(
itemCount: lists.length,
itemBuilder: (c, i) {
final l = lists[i];
return ListTile(
title: Text(l.title),
onTap: () => _openList(l),
onLongPress: () => _showListMenu(l),
);
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: _createList,
child: const Icon(Icons.add),
),
);
}
}