first draft
This commit is contained in:
224
lib/pages/layouts.dart
Normal file
224
lib/pages/layouts.dart
Normal file
@@ -0,0 +1,224 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:listenmeister/main.dart';
|
||||
import 'package:listenmeister/models/models.dart';
|
||||
import 'package:listenmeister/pages/layout_detail.dart';
|
||||
|
||||
class LayoutsPage extends StatefulWidget {
|
||||
const LayoutsPage({super.key});
|
||||
|
||||
@override
|
||||
State<LayoutsPage> createState() => _LayoutsPageState();
|
||||
}
|
||||
|
||||
class _LayoutsPageState extends State<LayoutsPage> {
|
||||
late Future<List<StoreLayout>> _layoutsFuture;
|
||||
StreamSubscription? _layoutsSubscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadLayouts();
|
||||
_layoutsSubscription = apiService.watchStoreLayouts().listen((_) {
|
||||
_loadLayouts();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_layoutsSubscription?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _loadLayouts() {
|
||||
_layoutsFuture = apiService.getStoreLayouts();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _createLayout() async {
|
||||
final TextEditingController nameCtl = TextEditingController();
|
||||
final TextEditingController addressCtl = TextEditingController();
|
||||
final TextEditingController latCtl = TextEditingController();
|
||||
final TextEditingController lonCtl = TextEditingController();
|
||||
bool isPublic = false;
|
||||
|
||||
final bool? ok = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return StatefulBuilder(
|
||||
builder: (context, setDialogState) {
|
||||
return AlertDialog(
|
||||
title: const Text('Neues Ladenlayout'),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
TextField(
|
||||
controller: nameCtl,
|
||||
decoration: const InputDecoration(labelText: 'Name'),
|
||||
autofocus: true,
|
||||
),
|
||||
TextField(
|
||||
controller: addressCtl,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Adresse (optional)',
|
||||
),
|
||||
),
|
||||
TextField(
|
||||
controller: latCtl,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Breitengrad (optional)',
|
||||
),
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
decimal: true,
|
||||
signed: true,
|
||||
),
|
||||
),
|
||||
TextField(
|
||||
controller: lonCtl,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Längengrad (optional)',
|
||||
),
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
decimal: true,
|
||||
signed: true,
|
||||
),
|
||||
),
|
||||
SwitchListTile.adaptive(
|
||||
value: isPublic,
|
||||
onChanged: (value) {
|
||||
setDialogState(() => isPublic = value);
|
||||
},
|
||||
title: const Text('Für andere sichtbar'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text('Abbrechen'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: const Text('Erstellen'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (ok != true) return;
|
||||
|
||||
final String name = nameCtl.text.trim();
|
||||
if (name.isEmpty) return;
|
||||
|
||||
double? latitude;
|
||||
double? longitude;
|
||||
final String latText = latCtl.text.trim();
|
||||
final String lonText = lonCtl.text.trim();
|
||||
|
||||
if (latText.isNotEmpty) {
|
||||
latitude = double.tryParse(latText.replaceAll(',', '.'));
|
||||
if (latitude == null) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Ungültiger Breitengrad.')),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (lonText.isNotEmpty) {
|
||||
longitude = double.tryParse(lonText.replaceAll(',', '.'));
|
||||
if (longitude == null) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Ungültiger Längengrad.')),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await apiService.createStoreLayout(
|
||||
name: name,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
address: addressCtl.text.trim().isEmpty ? null : addressCtl.text.trim(),
|
||||
isPublic: isPublic,
|
||||
);
|
||||
_loadLayouts();
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Fehler: $e')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _openLayout(StoreLayout layout) async {
|
||||
await Navigator.of(context).push<StoreLayout>(
|
||||
MaterialPageRoute(builder: (_) => LayoutDetailPage(layout: layout)),
|
||||
);
|
||||
if (mounted) {
|
||||
_loadLayouts();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Ladenlayouts')),
|
||||
body: FutureBuilder<List<StoreLayout>>(
|
||||
future: _layoutsFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
if (snapshot.hasError) {
|
||||
return Center(child: Text('Fehler: ${snapshot.error}'));
|
||||
}
|
||||
|
||||
final layouts = snapshot.data ?? [];
|
||||
if (layouts.isEmpty) {
|
||||
return const Center(child: Text('Noch keine Layouts vorhanden.'));
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: layouts.length,
|
||||
itemBuilder: (context, index) {
|
||||
final layout = layouts[index];
|
||||
return ListTile(
|
||||
title: Text(layout.name),
|
||||
subtitle: layout.address != null && layout.address!.isNotEmpty
|
||||
? Text(layout.address!)
|
||||
: null,
|
||||
leading: Icon(
|
||||
layout.owner == apiService.userId
|
||||
? Icons.store_mall_directory
|
||||
: Icons.share,
|
||||
),
|
||||
trailing: layout.isPublic
|
||||
? const Icon(Icons.public, size: 20)
|
||||
: null,
|
||||
onTap: () => _openLayout(layout),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _createLayout,
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user