add detail view

This commit is contained in:
2026-01-23 08:33:58 +01:00
parent aeca07a5a3
commit 5f4f2c4379
10 changed files with 592 additions and 17 deletions

View File

@@ -0,0 +1,12 @@
import 'package:get/get.dart';
import '../models/tank_model.dart';
class DetailController extends GetxController {
late TankModel tank;
@override
void onInit() {
tank = Get.arguments as TankModel;
super.onInit();
}
}

View File

@@ -2,6 +2,7 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../models/tank_model.dart'; import '../models/tank_model.dart';
import '../pages/detail_view.dart';
import '../services/appwrite_service.dart'; import '../services/appwrite_service.dart';
class HomeController extends GetxController { class HomeController extends GetxController {
@@ -82,4 +83,8 @@ class HomeController extends GetxController {
); );
} }
} }
void viewTankDetails(TankModel tank) {
Get.toNamed(DetailPage.namedRoute, arguments: tank);
}
} }

View File

@@ -1,5 +1,6 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../controller/detail_controller.dart';
import '../controller/home_controller.dart'; import '../controller/home_controller.dart';
import '../controller/login_controller.dart'; import '../controller/login_controller.dart';
import '../controller/signin_controller.dart'; import '../controller/signin_controller.dart';
@@ -13,6 +14,7 @@ class SampleBindings extends Bindings {
Get.lazyPut<LoginController>(() => LoginController()); Get.lazyPut<LoginController>(() => LoginController());
Get.lazyPut<SigninController>(() => SigninController()); Get.lazyPut<SigninController>(() => SigninController());
Get.lazyPut<HomeController>(() => HomeController()); Get.lazyPut<HomeController>(() => HomeController());
Get.lazyPut<DetailController>(() => DetailController());
} }

View File

@@ -1,4 +1,5 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../pages/detail_view.dart';
import 'sample_bindings.dart'; import 'sample_bindings.dart';
import '../pages/home_view.dart'; import '../pages/home_view.dart';
import '../pages/signin_view.dart'; import '../pages/signin_view.dart';
@@ -22,6 +23,11 @@ class SampleRouts {
page: () => const HomePage(), page: () => const HomePage(),
binding: sampleBindings, binding: sampleBindings,
), ),
GetPage(
name: DetailPage.namedRoute,
page: () => const DetailPage(),
binding: sampleBindings,
),
]; ];
} }

177
lib/pages/detail_view.dart Normal file
View File

@@ -0,0 +1,177 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controller/detail_controller.dart';
import '../widgets/detail_header_widget.dart';
import '../widgets/detail_info_card_widget.dart';
import '../widgets/detail_stat_widget.dart';
class DetailPage extends GetView<DetailController> {
static const String namedRoute = '/tank-detail-page';
const DetailPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blueGrey,
foregroundColor: Colors.white,
title: const Text('Tank Details'),
centerTitle: true,
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Get.back(),
),
),
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.blueGrey[800]!,
Colors.blueGrey[600]!,
Colors.blueGrey[300]!,
Colors.blue[100]!,
],
),
),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header Card mit Datum und Gesamtpreis
DetailHeaderWidget(tank: controller.tank),
const SizedBox(height: 16),
// Hauptinformationen
DetailInfoCardWidget(
title: 'Tankstelleninformationen',
children: [
DetailStatWidget(
icon: Icons.location_on,
label: 'Standort',
value: controller.tank.szLocation.isNotEmpty
? controller.tank.szLocation
: 'Nicht angegeben',
iconColor: Colors.red,
),
const SizedBox(height: 16),
DetailStatWidget(
icon: Icons.calendar_today,
label: 'Datum',
value: controller.tank.szDate,
iconColor: Colors.blueGrey,
),
],
),
const SizedBox(height: 16),
// Tankdaten
DetailInfoCardWidget(
title: 'Tankdaten',
children: [
DetailStatWidget(
icon: Icons.local_gas_station,
label: 'Liter getankt',
value: '${controller.tank.szLiters} L',
iconColor: Colors.orange,
valueSize: 24,
valueWeight: FontWeight.bold,
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: DetailStatWidget(
icon: Icons.euro,
label: 'Preis pro Liter',
value: '${controller.tank.szPricePerLiter}',
iconColor: Colors.green,
),
),
const SizedBox(width: 16),
Expanded(
child: DetailStatWidget(
icon: Icons.receipt,
label: 'Gesamtpreis',
value: '${controller.tank.szPriceTotal}',
iconColor: Colors.green[700]!,
valueWeight: FontWeight.bold,
),
),
],
),
],
),
const SizedBox(height: 16),
// Fahrzeugdaten
DetailInfoCardWidget(
title: 'Fahrzeugdaten',
children: [
DetailStatWidget(
icon: Icons.speed,
label: 'Kilometerstand',
value: '${controller.tank.szOdometer} km',
iconColor: Colors.blue,
valueSize: 24,
valueWeight: FontWeight.bold,
),
],
),
const SizedBox(height: 24),
// Aktionsbuttons
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: () {
// Bearbeiten Funktion
},
icon: const Icon(Icons.edit),
label: const Text('Bearbeiten'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blueGrey[700],
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton.icon(
onPressed: () {
// Löschen Funktion
},
icon: const Icon(Icons.delete),
label: const Text('Löschen'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red[700],
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
],
),
],
),
),
),
),
);
}
}

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../controller/home_controller.dart'; import '../controller/home_controller.dart';
import '../widgets/my_styled_loading_indicator.dart';
class HomePage extends GetView<HomeController> { class HomePage extends GetView<HomeController> {
static const String namedRoute = '/home-page'; static const String namedRoute = '/home-page';
@@ -31,26 +32,178 @@ class HomePage extends GetView<HomeController> {
), ),
], ],
), ),
body: Obx( body: Container(
() => homCtrl.isLoading.value == false decoration: BoxDecoration(
? ListView.builder( gradient: LinearGradient(
itemBuilder: (context, index) { begin: Alignment.topCenter,
var tank = homCtrl.listTankModel[index]; end: Alignment.bottomCenter,
return ListTile( colors: [
title: Text( Colors.blueGrey[800]!,
'${tank.szDate} - ${tank.szLiters}L - ${tank.szPricePerLiter}€/L', Colors.blueGrey[600]!,
), Colors.blueGrey[300]!,
subtitle: Text( Colors.blue[100]!,
'Total: ${tank.szPriceTotal}€ - Odometer: ${tank.szOdometer}km', ],
), ),
); ),
}, child: Obx(
itemCount: homCtrl.listTankModel.length, () => homCtrl.isLoading.value == false
) ? homCtrl.listTankModel.isEmpty
: Center(child: CircularProgressIndicator()), ? MyStyledLoadingIndicator()
: ListView.builder(
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
var tank = homCtrl.listTankModel[index];
return Card(
elevation: 2,
margin: const EdgeInsets.only(bottom: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: InkWell(
borderRadius: BorderRadius.circular(12),
onTap: () {
// Tap action if needed
homCtrl.viewTankDetails(tank);
},
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(
8,
),
decoration: BoxDecoration(
color: Colors.blueGrey[50],
borderRadius:
BorderRadius.circular(
8,
),
),
child: Icon(
Icons.local_gas_station,
color: Colors.blueGrey[700],
size: 24,
),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
tank.szDate,
style: const TextStyle(
fontSize: 16,
fontWeight:
FontWeight.w600,
),
),
const SizedBox(height: 2),
Text(
'${tank.szLiters} Liter',
style: TextStyle(
fontSize: 14,
color: Colors.grey[600],
),
),
],
),
],
),
Container(
padding:
const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
color: Colors.green[50],
borderRadius:
BorderRadius.circular(20),
),
child: Text(
'${tank.szPriceTotal}',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.green[700],
),
),
),
],
),
const SizedBox(height: 12),
Divider(
color: Colors.grey[300],
height: 1,
),
const SizedBox(height: 12),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
_buildInfoChip(
icon: Icons.euro,
label: 'Preis/L',
value: '${tank.szPricePerLiter}',
),
_buildInfoChip(
icon: Icons.speed,
label: 'Km-Stand',
value: '${tank.szOdometer} km',
),
],
),
],
),
),
),
);
},
itemCount: homCtrl.listTankModel.length,
)
: Center(
child: CircularProgressIndicator(color: Colors.blueGrey),
),
),
), ),
), ),
), ),
); );
} }
Widget _buildInfoChip({
required IconData icon,
required String label,
required String value,
}) {
return Row(
children: [
Icon(icon, size: 16, color: Colors.blueGrey[600]),
const SizedBox(width: 4),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(fontSize: 11, color: Colors.grey[600]),
),
Text(
value,
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600),
),
],
),
],
);
}
} }

View File

@@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import '../models/tank_model.dart';
class DetailHeaderWidget extends StatelessWidget {
final TankModel tank;
const DetailHeaderWidget({super.key, required this.tank});
@override
Widget build(BuildContext context) {
return Card(
elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.green[600]!, Colors.green[800]!],
),
borderRadius: BorderRadius.circular(16),
),
padding: const EdgeInsets.all(24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Tankvorgang',
style: TextStyle(
color: Colors.white.withAlpha(230),
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 4),
Text(
tank.szDate,
style: const TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'Gesamtpreis',
style: TextStyle(
color: Colors.white.withAlpha(230),
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 4),
Text(
'${tank.szPriceTotal}',
style: const TextStyle(
color: Colors.white,
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
],
),
],
),
),
);
}
}

View File

@@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
class DetailInfoCardWidget extends StatelessWidget {
final String title;
final List<Widget> children;
const DetailInfoCardWidget({
super.key,
required this.title,
required this.children,
});
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 4,
height: 20,
decoration: BoxDecoration(
color: Colors.blueGrey[700],
borderRadius: BorderRadius.circular(2),
),
),
const SizedBox(width: 8),
Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.blueGrey[800],
),
),
],
),
const SizedBox(height: 16),
...children,
],
),
),
);
}
}

View File

@@ -0,0 +1,61 @@
import 'package:flutter/material.dart';
class DetailStatWidget extends StatelessWidget {
final IconData icon;
final String label;
final String value;
final Color iconColor;
final double valueSize;
final FontWeight valueWeight;
const DetailStatWidget({
super.key,
required this.icon,
required this.label,
required this.value,
this.iconColor = Colors.blueGrey,
this.valueSize = 18,
this.valueWeight = FontWeight.w600,
});
@override
Widget build(BuildContext context) {
return Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: iconColor.withAlpha(100),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, color: iconColor, size: 28),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
fontSize: 13,
color: Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 4),
Text(
value,
style: TextStyle(
fontSize: valueSize,
fontWeight: valueWeight,
color: Colors.grey[900],
),
),
],
),
),
],
);
}
}

View File

@@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
class MyStyledLoadingIndicator extends StatelessWidget {
const MyStyledLoadingIndicator({
super.key,
});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.local_gas_station_outlined,
size: 80,
color: Colors.grey[400],
),
const SizedBox(height: 16),
Text(
'Keine Tankeinträge vorhanden',
style: TextStyle(
fontSize: 18,
color: Colors.grey[600],
fontWeight: FontWeight.w500,
),
),
],
),
);
}
}