add import export and items

This commit is contained in:
atseirjo
2026-10-28 08:10:55 +01:00
parent 07a1bb02fd
commit 4a1731f986
22 changed files with 1118 additions and 106 deletions
-28
View File
@@ -2,9 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controller/anwendung_controller.dart';
import '../helper/display_formats.dart';
import '../widgets/anwendung_form.dart';
import '../widgets/entry_panel.dart';
class AnwendungView extends GetView<AnwendungController> {
static const routeName = '/anwendung-view';
@@ -39,32 +37,6 @@ class AnwendungView extends GetView<AnwendungController> {
child: AnwendungForm(controller: controller),
),
),
SliverPadding(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 24),
sliver: Obx(() {
final entries = controller.anwendungen.toList();
return SavedEntries(
title: 'Gespeicherte Profile',
count: entries.length,
emptyText: 'Noch keine Verarbeitungsprofile für dieses Filament gespeichert.',
itemBuilder: (context, index) {
final entry = entries[index];
return SavedEntryCard(
key: ValueKey(entry.anwendungId),
title:
'Druck ${DisplayFormats.number(entry.druckTemperatur)} °C · Bett ${DisplayFormats.number(entry.druckbettTemperatur)} °C',
subtitle: DisplayFormats.date(entry.erstelltAm),
details: [
'Bauraum geschlossen: ${entry.bauraumGeschlossen ? "Ja" : "Nein"}',
if (entry.bauraumTemperatur != null)
'Bauraum ${DisplayFormats.number(entry.bauraumTemperatur!)} °C',
],
notes: entry.sonstiges,
);
},
);
}),
),
],
);
}),
+117
View File
@@ -0,0 +1,117 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controller/export_controller.dart';
import '../helper/storage_keys.dart';
import '../widgets/entry_panel.dart';
class ExportView extends GetView<ExportController> {
static const routeName = '/export-view';
const ExportView({super.key});
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('Datensicherung')),
body: SafeArea(
child: Align(
alignment: Alignment.topCenter,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 760),
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Obx(() {
final busy = controller.isBusy.value;
final backup = controller.pendingBackup.value;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
EntryPanel(
title: 'Daten exportieren',
subtitle: 'Sichere Filamente, Verarbeitungsprofile, Bauteile inklusive Fotos und Verbrauch sowie deine Materialarten in einer JSON-Datei.\n\nSpeichere die Datei außerhalb der App, zum Beispiel in Dokumente oder in deiner Cloud. So kannst du sie nach einer Neuinstallation wieder importieren.',
child: FilledButton.icon(
onPressed: busy ? null : controller.exportData,
icon: const Icon(Icons.file_download_outlined),
label: const Text('Als JSON exportieren'),
),
),
const SizedBox(height: 20),
EntryPanel(
title: 'Daten importieren',
subtitle: 'Wähle eine JSON-Datensicherung dieser App. Beim Import werden alle aktuell gespeicherten Daten durch den Inhalt der Datei ersetzt. Exportiere bei Bedarf vorher deinen aktuellen Bestand.',
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
OutlinedButton.icon(
onPressed: busy ? null : controller.selectImport,
icon: const Icon(Icons.file_upload_outlined),
label: const Text('JSON-Datei auswählen'),
),
if (backup != null) ...[
const SizedBox(height: 16),
const Text('Geprüfte Datensicherung:'),
Text(
'${backup.data[StorageKeys.filamente]!.length} Filamente',
),
Text(
'${backup.data[StorageKeys.anwendungen]!.length} Profile',
),
Text(
'${backup.data[StorageKeys.bauteile]!.length} Bauteile',
),
Text(
backup.data[StorageKeys.materialien]!.isEmpty
? 'Materialarten: Standardliste'
: '${(backup.data[StorageKeys.materialien]!.single['werte'] as List).length} Materialarten',
),
const SizedBox(height: 16),
FilledButton.icon(
onPressed: busy
? null
: () => _confirmImport(context),
icon: const Icon(Icons.restore),
label: const Text('Daten ersetzen und importieren'),
),
],
],
),
),
if (busy) ...[
const SizedBox(height: 20),
const LinearProgressIndicator(),
],
EntryMessage(
message: controller.message.value,
hasError: controller.hasError.value,
),
],
);
}),
),
),
),
),
);
Future<void> _confirmImport(BuildContext context) async {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Alle Daten ersetzen?'),
content: const Text(
'Der aktuelle Bestand wird vollständig durch die ausgewählte Datensicherung ersetzt. Auch eine leere Sicherung ersetzt den Bestand.',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('Abbrechen'),
),
FilledButton(
onPressed: () => Navigator.pop(context, true),
child: const Text('Daten ersetzen'),
),
],
),
);
if (confirmed == true) await controller.importData();
}
}
+19
View File
@@ -8,6 +8,7 @@ import '../widgets/filament_form.dart';
import '../widgets/filament_inventory.dart';
import 'anwendung_view.dart';
import 'bauteil_view.dart';
import 'export_view.dart';
class HomeView extends GetView<HomeController> {
static const routeName = '/home-view';
@@ -39,6 +40,24 @@ class HomeView extends GetView<HomeController> {
],
),
),
drawer: Drawer(
child: SafeArea(
child: ListView(
padding: const EdgeInsets.all(16),
children: [
const ListTile(title: Text('Filamentverwaltung')),
ListTile(
leading: const Icon(Icons.import_export),
title: const Text('Daten exportieren / importieren'),
onTap: () {
Navigator.of(context).pop();
Get.toNamed(ExportView.routeName);
},
),
],
),
),
),
body: SafeArea(
top: false,
child: Align(