211 lines
8.0 KiB
Dart
211 lines
8.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
|
|
import '../controller/bauteil_controller.dart';
|
|
import '../helper/display_formats.dart';
|
|
import '../helper/input_validators.dart';
|
|
import '../theme/app_theme.dart';
|
|
import 'entry_panel.dart';
|
|
|
|
class BauteilFilamentAuswahl extends StatelessWidget {
|
|
const BauteilFilamentAuswahl({super.key, required this.controller});
|
|
final BauteilController controller;
|
|
|
|
Future<void> _waehleFilament(BuildContext context, String id) async {
|
|
controller.toggleFilament(id, true);
|
|
final warnung = controller.pruefeFilamentVorrat();
|
|
if (warnung == null || !context.mounted) return;
|
|
await showDialog<void>(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (dialogContext) => PopScope(
|
|
canPop: false,
|
|
child: AlertDialog(
|
|
title: const Text('Achtung: zu wenig Filament'),
|
|
content: Text(warnung),
|
|
actions: [
|
|
FilledButton(
|
|
onPressed: () => Navigator.of(dialogContext).pop(),
|
|
child: const Text('OK'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) => Obx(() {
|
|
final ids = controller.selectedFilamente.keys.toList();
|
|
final availableFilamente = controller.filamente
|
|
.where(
|
|
(item) => !controller.selectedFilamente.containsKey(item.filamentId),
|
|
)
|
|
.toList();
|
|
if (controller.filamente.isEmpty && ids.isEmpty) {
|
|
return const Text(
|
|
'Erfasse zuerst ein Filament in der Einkaufsverwaltung.',
|
|
);
|
|
}
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
if (availableFilamente.isNotEmpty) ...[
|
|
DropdownButtonFormField<String>(
|
|
key: ValueKey(
|
|
availableFilamente
|
|
.map((item) => item.filamentId)
|
|
.toList()
|
|
.toString(),
|
|
),
|
|
isExpanded: true,
|
|
itemHeight: 64,
|
|
decoration: const InputDecoration(labelText: 'Filament hinzufügen'),
|
|
hint: const Text('Filament auswählen'),
|
|
items: availableFilamente
|
|
.map(
|
|
(item) => DropdownMenuItem(
|
|
value: item.filamentId,
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
controller.filamentLabel(item.filamentId),
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
controller.restgewichtGramm(item.filamentId) == null
|
|
? 'Restgewicht unbekannt'
|
|
: 'Restgewicht: ${DisplayFormats.number(controller.restgewichtGramm(item.filamentId)!)} g',
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(
|
|
color: AppTheme.teal,
|
|
fontSize: 13,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
)
|
|
.toList(),
|
|
onChanged: (id) {
|
|
if (id != null) _waehleFilament(context, id);
|
|
},
|
|
),
|
|
const SizedBox(height: 12),
|
|
] else if (controller.filamente.isNotEmpty) ...[
|
|
const Text('Alle Filamente wurden hinzugefügt.'),
|
|
const SizedBox(height: 12),
|
|
],
|
|
if (ids.isEmpty)
|
|
const Text(
|
|
'Noch keine Filamente hinzugefügt.',
|
|
style: TextStyle(color: AppTheme.muted),
|
|
),
|
|
...ids.map((id) {
|
|
final profiles = controller.profilesFor(id);
|
|
final available = controller.verfuegbarGramm(id);
|
|
final filament = ListTile(
|
|
key: ValueKey('filament-$id'),
|
|
title: Text(controller.filamentLabel(id)),
|
|
subtitle: Text(
|
|
available == null
|
|
? 'Startgewicht unbekannt'
|
|
: '${DisplayFormats.number(available)} g verfügbar',
|
|
),
|
|
trailing: IconButton(
|
|
tooltip: 'Filament entfernen',
|
|
icon: const Icon(Icons.close),
|
|
onPressed: () => controller.toggleFilament(id, false),
|
|
),
|
|
);
|
|
final consumption = Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: EntryNumberField(
|
|
controller: controller.verbrauchController(id),
|
|
label: 'Verbrauch',
|
|
unit: 'g',
|
|
validator: InputValidators.positive,
|
|
),
|
|
);
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 12),
|
|
child: Material(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
clipBehavior: Clip.antiAlias,
|
|
child: Column(
|
|
children: [
|
|
LayoutBuilder(
|
|
builder: (context, constraints) =>
|
|
constraints.maxWidth >= 500
|
|
? Row(
|
|
children: [
|
|
Expanded(child: filament),
|
|
SizedBox(width: 190, child: consumption),
|
|
],
|
|
)
|
|
: Column(children: [filament, consumption]),
|
|
),
|
|
if (profiles.isNotEmpty)
|
|
Padding(
|
|
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
|
child: DropdownButtonFormField<String>(
|
|
key: ValueKey(
|
|
'profile-$id-${controller.selectedFilamente[id]}',
|
|
),
|
|
initialValue:
|
|
profiles.any(
|
|
(item) =>
|
|
item.anwendungId ==
|
|
controller.selectedFilamente[id],
|
|
)
|
|
? controller.selectedFilamente[id]
|
|
: '',
|
|
isExpanded: true,
|
|
decoration: const InputDecoration(
|
|
labelText: 'Verarbeitungsprofil',
|
|
),
|
|
items: [
|
|
const DropdownMenuItem(
|
|
value: '',
|
|
child: Text('Ohne Profil'),
|
|
),
|
|
...profiles.map(
|
|
(profile) => DropdownMenuItem(
|
|
value: profile.anwendungId,
|
|
child: Text(
|
|
'${DisplayFormats.number(profile.druckTemperatur)} °C · Bett ${DisplayFormats.number(profile.druckbettTemperatur)} °C · ${DisplayFormats.date(profile.erstelltAm)}',
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
onChanged: (value) => controller.selectProfile(
|
|
id,
|
|
value == '' ? null : value,
|
|
),
|
|
),
|
|
),
|
|
if (profiles.isEmpty)
|
|
const Padding(
|
|
padding: EdgeInsets.fromLTRB(16, 0, 16, 16),
|
|
child: Text(
|
|
'Noch kein Profil. Einstellungen kannst du über das Filament in der Einkaufsübersicht erfassen.',
|
|
style: TextStyle(color: AppTheme.muted),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}),
|
|
],
|
|
);
|
|
});
|
|
}
|