add two views
This commit is contained in:
@@ -0,0 +1,234 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../models/kauf_filament_model.dart';
|
||||
import '../theme/app_theme.dart';
|
||||
import '../helper/display_formats.dart';
|
||||
|
||||
class FilamentCard extends StatelessWidget {
|
||||
const FilamentCard({
|
||||
super.key,
|
||||
required this.filament,
|
||||
required this.onTap,
|
||||
required this.onAction,
|
||||
this.verbrauchGramm = 0,
|
||||
});
|
||||
|
||||
final KaufFilamentModel filament;
|
||||
final VoidCallback onTap;
|
||||
final ValueChanged<String> onAction;
|
||||
final double verbrauchGramm;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final price = NumberFormat.currency(
|
||||
locale: 'de_AT',
|
||||
symbol: '€',
|
||||
).format(filament.preis);
|
||||
final weight = filament.gewichtGramm != null
|
||||
? '${DisplayFormats.number(filament.gewichtGramm!)} g'
|
||||
: RegExp(r'[a-zA-Z]').hasMatch(filament.gewicht)
|
||||
? filament.gewicht
|
||||
: '${filament.gewicht} g';
|
||||
final start = filament.gewichtGramm;
|
||||
final rest = start == null ? null : start - verbrauchGramm;
|
||||
return Card(
|
||||
margin: EdgeInsets.zero,
|
||||
color: Colors.white,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(18)),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppTheme.inputSurface,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.inventory_2_outlined,
|
||||
color: AppTheme.teal,
|
||||
size: 26,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
filament.marke.isEmpty
|
||||
? filament.material
|
||||
: filament.marke,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
filament.farbe,
|
||||
style: const TextStyle(
|
||||
color: AppTheme.muted,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuButton<String>(
|
||||
tooltip: 'Filament verwalten',
|
||||
onSelected: onAction,
|
||||
itemBuilder: (context) => const [
|
||||
PopupMenuItem(
|
||||
value: 'edit',
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.edit_outlined),
|
||||
title: Text('Bearbeiten'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'duplicate',
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.copy_outlined),
|
||||
title: Text('Duplizieren'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'delete',
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.delete_outline),
|
||||
title: Text('Löschen'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
rest == null
|
||||
? 'Restgewicht unbekannt'
|
||||
: '${DisplayFormats.number(rest)} g verbleibend',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: rest != null && rest < 0
|
||||
? Theme.of(context).colorScheme.error
|
||||
: AppTheme.teal,
|
||||
),
|
||||
),
|
||||
if (start != null && start > 0) ...[
|
||||
const SizedBox(height: 8),
|
||||
LinearProgressIndicator(
|
||||
value: (rest! / start).clamp(0.0, 1.0),
|
||||
minHeight: 6,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
backgroundColor: AppTheme.inputSurface,
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'${DisplayFormats.number(verbrauchGramm)} g verbraucht',
|
||||
style: const TextStyle(color: AppTheme.muted, fontSize: 12),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
_DetailPill(
|
||||
label: filament.material,
|
||||
icon: Icons.layers_outlined,
|
||||
),
|
||||
_DetailPill(label: weight, icon: Icons.scale_outlined),
|
||||
],
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 14),
|
||||
child: Divider(height: 1, color: Color(0xFFE7EDF0)),
|
||||
),
|
||||
Wrap(
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
spacing: 16,
|
||||
runSpacing: 12,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
filament.haendler,
|
||||
style: const TextStyle(color: AppTheme.muted),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
DateFormat('dd.MM.yyyy').format(filament.kaufdatum),
|
||||
style: const TextStyle(
|
||||
color: AppTheme.muted,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
price,
|
||||
style: const TextStyle(
|
||||
color: AppTheme.teal,
|
||||
fontSize: 21,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DetailPill extends StatelessWidget {
|
||||
const _DetailPill({required this.label, required this.icon});
|
||||
|
||||
final String label;
|
||||
final IconData icon;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF0F5F7),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 14, color: AppTheme.muted),
|
||||
const SizedBox(width: 6),
|
||||
Flexible(
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user