Create Igel
This commit is contained in:
@@ -6,21 +6,21 @@ class IgelRepository {
|
||||
IgelRepository(this.api);
|
||||
|
||||
Future<List<Igel>> list() async {
|
||||
final res = await api.get('/igel');
|
||||
final res = await api.get<dynamic>('/igel');
|
||||
final list = (res as List).cast<Map<String, dynamic>>();
|
||||
return list.map(Igel.fromMap).toList();
|
||||
}
|
||||
|
||||
Future<Igel> create(String name, {String? note}) async {
|
||||
final res = await api.post('/igel', {'name': name, 'note': note});
|
||||
return Igel.fromMap(res);
|
||||
final res = await api.post<dynamic>('/igel', {'name': name, 'note': note});
|
||||
return Igel.fromMap(res as Map<String, dynamic>);
|
||||
}
|
||||
|
||||
Future<void> update(int id, String name, {String? note}) async {
|
||||
await api.put('/igel/$id', {'name': name, 'note': note});
|
||||
await api.put<dynamic>('/igel/$id', {'name': name, 'note': note});
|
||||
}
|
||||
|
||||
Future<void> delete(int id) async {
|
||||
await api.delete('/igel/$id');
|
||||
await api.delete<dynamic>('/igel/$id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:hedgehog/main.dart';
|
||||
import 'package:hedgehog/features/igel/data/igel_repository.dart';
|
||||
import 'package:hedgehog/features/igel/domain/igel.dart';
|
||||
import '../../../main.dart';
|
||||
import '../../igel/data/igel_repository.dart';
|
||||
import '../domain/igel.dart';
|
||||
|
||||
class IgelListScreen extends ConsumerStatefulWidget {
|
||||
const IgelListScreen({super.key});
|
||||
@@ -15,6 +15,12 @@ class _State extends ConsumerState<IgelListScreen> {
|
||||
List<Igel> items = [];
|
||||
bool busy = true;
|
||||
String? err;
|
||||
|
||||
// Inline-Form Steuerung
|
||||
bool showForm = false;
|
||||
final nameC = TextEditingController();
|
||||
final noteC = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -34,92 +40,170 @@ class _State extends ConsumerState<IgelListScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _create() async {
|
||||
final name = await showDialog<String>(
|
||||
context: context, builder: (_) => const _NewIgelDialog());
|
||||
if (name != null && name.isNotEmpty) {
|
||||
await repo.create(name);
|
||||
await _load();
|
||||
Future<void> _createInline() async {
|
||||
final name = nameC.text.trim();
|
||||
final note = noteC.text.trim().isEmpty ? null : noteC.text.trim();
|
||||
if (name.isEmpty) {
|
||||
_snack('Bitte einen Namen eingeben');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await repo.create(name, note: note);
|
||||
nameC.clear();
|
||||
noteC.clear();
|
||||
setState(() => showForm = false);
|
||||
await _load();
|
||||
_snack('Igel angelegt');
|
||||
} catch (e) {
|
||||
_snack('Fehler beim Anlegen: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void _snack(String msg) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg)));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext ctx) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Meine Igel')),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _create, child: const Icon(Icons.add)),
|
||||
body: busy
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: err != null
|
||||
? Center(child: Text(err!))
|
||||
: ListView.separated(
|
||||
itemCount: items.length,
|
||||
separatorBuilder: (_, __) => const Divider(height: 1),
|
||||
itemBuilder: (_, i) {
|
||||
final x = items[i];
|
||||
return ListTile(
|
||||
title: Text(x.name),
|
||||
subtitle: x.note != null ? Text(x.note!) : null,
|
||||
trailing: PopupMenuButton(
|
||||
itemBuilder: (_) => [
|
||||
const PopupMenuItem(
|
||||
value: 'edit', child: Text('Bearbeiten')),
|
||||
const PopupMenuItem(
|
||||
value: 'del', child: Text('Löschen')),
|
||||
: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
FilledButton.icon(
|
||||
onPressed: () => setState(() => showForm = !showForm),
|
||||
icon: Icon(showForm ? Icons.close : Icons.add),
|
||||
label: Text(showForm ? 'Abbrechen' : 'Neu'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
if (showForm)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextField(
|
||||
controller: nameC,
|
||||
decoration: const InputDecoration(labelText: 'Name'),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextField(
|
||||
controller: noteC,
|
||||
decoration: const InputDecoration(labelText: 'Notiz (optional)'),
|
||||
minLines: 1,
|
||||
maxLines: 3,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
FilledButton(onPressed: _createInline, child: const Text('Speichern')),
|
||||
const SizedBox(width: 8),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
nameC.clear();
|
||||
noteC.clear();
|
||||
setState(() => showForm = false);
|
||||
},
|
||||
child: const Text('Abbrechen'),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
onSelected: (v) async {
|
||||
if (v == 'del') {
|
||||
await repo.delete(x.id);
|
||||
await _load();
|
||||
}
|
||||
if (v == 'edit') {
|
||||
final name = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (_) =>
|
||||
_NewIgelDialog(initial: x.name));
|
||||
if (name != null) {
|
||||
await repo.update(x.id, name);
|
||||
await _load();
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
const Divider(height: 1),
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
itemCount: items.length,
|
||||
separatorBuilder: (_, __) => const Divider(height: 1),
|
||||
itemBuilder: (_, i) {
|
||||
final x = items[i];
|
||||
return ListTile(
|
||||
title: Text(x.name),
|
||||
subtitle: x.note != null ? Text(x.note!) : null,
|
||||
trailing: PopupMenuButton(
|
||||
itemBuilder: (_) => const [
|
||||
PopupMenuItem(value: 'edit', child: Text('Bearbeiten')),
|
||||
PopupMenuItem(value: 'del', child: Text('Löschen')),
|
||||
],
|
||||
onSelected: (v) async {
|
||||
if (v == 'del') {
|
||||
try {
|
||||
await repo.delete(x.id);
|
||||
await _load();
|
||||
_snack('Gelöscht');
|
||||
} catch (e) {
|
||||
_snack('Fehler beim Löschen: $e');
|
||||
}
|
||||
}
|
||||
if (v == 'edit') {
|
||||
final newName = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (_) => _EditDialog(initial: x.name),
|
||||
);
|
||||
if (newName != null && newName.trim().isNotEmpty) {
|
||||
try {
|
||||
await repo.update(x.id, newName.trim(), note: x.note);
|
||||
await _load();
|
||||
_snack('Gespeichert');
|
||||
} catch (e) {
|
||||
_snack('Fehler beim Speichern: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _NewIgelDialog extends StatefulWidget {
|
||||
final String? initial;
|
||||
const _NewIgelDialog({this.initial});
|
||||
class _EditDialog extends StatefulWidget {
|
||||
const _EditDialog({required this.initial});
|
||||
final String initial;
|
||||
@override
|
||||
State<_NewIgelDialog> createState() => _NewIgelDialogState();
|
||||
State<_EditDialog> createState() => _EditDialogState();
|
||||
}
|
||||
|
||||
class _NewIgelDialogState extends State<_NewIgelDialog> {
|
||||
class _EditDialogState extends State<_EditDialog> {
|
||||
late final TextEditingController c;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
c = TextEditingController(text: widget.initial ?? '');
|
||||
c = TextEditingController(text: widget.initial);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(widget.initial == null ? 'Neuer Igel' : 'Igel bearbeiten'),
|
||||
content: TextField(
|
||||
controller: c,
|
||||
decoration: const InputDecoration(labelText: 'Name')),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Abbrechen')),
|
||||
FilledButton(
|
||||
onPressed: () => Navigator.pop(context, c.text.trim()),
|
||||
child: const Text('Speichern')),
|
||||
]);
|
||||
title: const Text('Igel bearbeiten'),
|
||||
content: TextField(controller: c, decoration: const InputDecoration(labelText: 'Name')),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(context), child: const Text('Abbrechen')),
|
||||
FilledButton(onPressed: () => Navigator.pop(context, c.text), child: const Text('Speichern')),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user