From c183d84a86dc65679f05464c379b692bb8d25ed5 Mon Sep 17 00:00:00 2001 From: josiadmin Date: Sun, 24 Aug 2025 19:54:36 +0200 Subject: [PATCH] final beta Version --- analysis_options.yaml | 1 + lib/pages/graph/graph_controller.dart | 264 ++++++++++++++++++ lib/pages/graph/graph_view.dart | 148 ++++++++++ lib/pages/graph/widgets/bar_chart_widget.dart | 124 ++++++++ lib/pages/graph/widgets/chart_desc.dart | 43 +++ lib/pages/graph/widgets/chart_lines.dart | 49 ++++ .../graph/widgets/chart_single_lines.dart | 36 +++ .../graph/widgets/my_list_tile_card.dart | 46 +++ lib/pages/tank/tank_controller.dart | 8 +- lib/pages/tank/tank_view.dart | 207 +++++++------- lib/pages/tanklist/tanklist_controller.dart | 9 +- lib/pages/tanklist/tanklist_view.dart | 164 ++++++----- lib/utils/extensions/sample_bindings.dart | 3 +- lib/utils/extensions/sample_routes.dart | 11 +- pubspec.lock | 18 +- pubspec.yaml | 1 + 16 files changed, 923 insertions(+), 209 deletions(-) create mode 100644 lib/pages/graph/graph_controller.dart create mode 100644 lib/pages/graph/graph_view.dart create mode 100644 lib/pages/graph/widgets/bar_chart_widget.dart create mode 100644 lib/pages/graph/widgets/chart_desc.dart create mode 100644 lib/pages/graph/widgets/chart_lines.dart create mode 100644 lib/pages/graph/widgets/chart_single_lines.dart create mode 100644 lib/pages/graph/widgets/my_list_tile_card.dart diff --git a/analysis_options.yaml b/analysis_options.yaml index 974f832..d891bdb 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -11,6 +11,7 @@ analyzer: errors: unused_field: ignore avoid_print: ignore + unnecessary_overrides: ignore include: package:flutter_lints/flutter.yaml linter: diff --git a/lib/pages/graph/graph_controller.dart b/lib/pages/graph/graph_controller.dart new file mode 100644 index 0000000..0c1ab94 --- /dev/null +++ b/lib/pages/graph/graph_controller.dart @@ -0,0 +1,264 @@ +import 'package:appwrite/appwrite.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:get_storage/get_storage.dart'; + +import '../../data/models/chart_model.dart'; +import '../../data/models/tank_model.dart'; +import '../../data/repository/appwrite_repository.dart'; +import '../../utils/extensions/static_helper.dart'; + + + +class GraphController extends GetxController { + + //AppWrite API-REST get Data + final AppwriteRepository _authRepository = AppwriteRepository(); + + final _dataBox = GetStorage('MyUserStorage'); + final szRxUserId = 'NoUser'.obs; + final szBarTitle = ''.obs; + final listYearModel = [].obs; + late List tankListOriginal; + final tankList = [].obs; + final yearValue = DateTime.now().year.obs; + final blIsLoading = false.obs; + final dataPointsEuro = [].obs; + final dataPointsGasoline = [].obs; + final pointsEuro = [].obs; + final pointsGasoline = [].obs; + final sumListData = [].obs; + final mnCurrentAveragePerLiter = 0.00.obs; + final pointsPerLiter = [].obs; + final dataPointsPerLiter = [].obs; + final mnCurrentSummEuroYear = 0.0.obs; + final mnCurrentSummLiterYear = 0.0.obs; + + @override + void onInit() { + szRxUserId(_dataBox.read('userId')); + _getTankList(); + super.onInit(); + } + + @override + void onReady() { + super.onReady(); + } + + @override + void onClose() {} + + void _getTankList() async { + blIsLoading(true); + bool isErrorByLoading = false; + String message = ''; + try { + await _authRepository + .listTankStops(szRxUserId.value) + .then((tankListData) { + if (tankListData.documents.isEmpty) { + blIsLoading(false); + isErrorByLoading = true; + message = 'Leere Liste keine Daten vorhanden'; + return; + } + tankList.clear(); + var data = tankListData.toMap(); + List d = data['documents'].toList(); + tankList.value = d + .map((e) => AppWriteTankModel.fromMap(e['data'])) + .toList(); + tankList.sort((a, b) { + final DateTime dateA = DateTime.parse(a.date); + final DateTime dateB = DateTime.parse(b.date); + return dateB.compareTo(dateA); + }); + message = 'Liste wurde erfolgreich geladen'; + }) + .catchError((error) { + blIsLoading(true); + isErrorByLoading = true; + if (error is AppwriteException) { + message = error.message!; + } else { + message = 'Uuups da ist was schief gelaufen'; + } + }); + } catch (e) { + blIsLoading(true); + isErrorByLoading = true; + message = 'Fehler beim Laden der Tankliste'; + print('Error fetching tank list: $e'); + } + String title = isErrorByLoading ? 'Fehler' : 'Erfolg'; + Get.snackbar( + title, + message, + backgroundColor: isErrorByLoading ? Colors.red : Colors.green, + snackPosition: SnackPosition.BOTTOM, + duration: const Duration(seconds: 4), + ); + tankListOriginal = tankList; + update(); + setListMapYear(); + getTankListPerYear(); + } + + List getHeadDescription() { + List localListString = []; + tankList.sort((a, b) { + final DateTime dateA = DateTime.parse(a.date); + final DateTime dateB = DateTime.parse(b.date); + return dateB.compareTo(dateA); + }); + if (tankList.isNotEmpty) { + for (var benzinItem in tankList) { + String szDay = ''; + var dateDay = DateTime.parse(benzinItem.date); + if (dateDay.day <= 9) { + szDay = '0${dateDay.day}'; + } else { + szDay = '${dateDay.day}'; + } + String szMonth = ''; + if (dateDay.month <= 9) { + szMonth = '0${dateDay.month}'; + } else { + szMonth = '${dateDay.month}'; + } + String szData = '$szDay$szMonth'; + localListString.add(szData); + } + } + update(); + return localListString; + } + + void setListMapYear() { + int year = 2022; + for (var i = year; i <= DateTime.now().year; i++) { + listYearModel.add(YearModel('Jahr $year', year)); + year = year + 1; + } + update(); + } + + void getTankListPerYear() { + blIsLoading.value = true; + tankList + .where((e) => DateTime.parse(e.date).year == yearValue.value) + .toList(); + getDataPointsForGraph(); + getMonthListData(); + blIsLoading.value = false; + update(); + } + + void getDataPointsForGraph() { + //daten ermitteln für Jahr xxxx + //debugPrint('${yearValue.value}'); + List mnDataPointsEuro = []; + List mnDataPointsGasoline = []; + // 08.08.23 Mod. add Price per Liter + List mnDataPointsPerLiter = []; + var mnSummPerLiter = 0.0; + var mnTankListCount = tankList.length; + for (var item in tankList) { + var mnLiterGesamt = double.parse(item.liters); + var mnEuroGesamt = + double.parse(item.liters) * double.parse(item.pricePerLiter); + var mnPerLiter = double.parse(item.pricePerLiter); + mnDataPointsEuro.add(mnEuroGesamt); + mnDataPointsGasoline.add(mnLiterGesamt); + mnDataPointsPerLiter.add(mnPerLiter.toPrecision(2)); + mnSummPerLiter += mnPerLiter; + } + if (mnSummPerLiter > 0 && mnTankListCount > 0) { + mnCurrentAveragePerLiter.value = (mnSummPerLiter / mnTankListCount) + .toPrecision(2); + } + dataPointsEuro.value = mnDataPointsEuro; + dataPointsGasoline.value = mnDataPointsGasoline; + dataPointsPerLiter.value = mnDataPointsPerLiter; + getPricePointsEuro(); + getPricePointsGasoline(); + getPricePointsPerLiter(); + update(); + } + + void getMonthListData() { + if (tankList.isNotEmpty) { + sumListData.clear(); + List tankListPerYear = tankList; + List monthList = StaticHelper.listMonth; + for (var monthMap in monthList) { + var szMonth = monthMap['month'].toString(); + var szValueMonth = monthMap['value']; + var result = StaticHelper.staticListGetDaysInBetween( + tankListPerYear, + szValueMonth, + yearValue.value, + ); + var tankListPerMon = result as List; + if (tankListPerMon.isNotEmpty) { + SumDataModel sumDataModel = SumDataModel('', '', '', 0); + sumDataModel.szMonth = szMonth; + sumDataModel.mnTankungen = tankListPerMon.length; + double mnSumEuro = 0.0; + double mnSumBenzin = 0.0; + for (var tankungModel in tankListPerMon) { + var mnEuroGesamt = + double.parse(tankungModel.liters) * + double.parse(tankungModel.pricePerLiter); + mnSumBenzin += double.parse(tankungModel.liters); + mnSumEuro += mnEuroGesamt; + } + sumDataModel.szVerbrauch = mnSumBenzin.toStringAsFixed(2); + sumDataModel.szSumme = mnSumEuro.toStringAsFixed(2); + sumListData.add(sumDataModel); + } + } + _currentSumForYear(); + } + } + + void getPricePointsEuro() { + var listPoints = dataPointsEuro.indexed + .map((e) => PricePoints(x: e.$1.toDouble(), y: e.$2)) + .toList(); + pointsEuro.value = listPoints; + update(); + } + + void getPricePointsGasoline() { + var listPoints = dataPointsGasoline.indexed + .map((e) => PricePoints(x: e.$1.toDouble(), y: e.$2)) + .toList(); + pointsGasoline.value = listPoints; + update(); + } + + void getPricePointsPerLiter() { + var listPoints = dataPointsPerLiter.indexed + .map((e) => PricePoints(x: e.$1.toDouble(), y: e.$2)) + .toList(); + pointsPerLiter.value = listPoints; + update(); + } + + void _currentSumForYear() { + mnCurrentSummEuroYear.value = 0.0; + mnCurrentSummLiterYear.value = 0.0; + if (sumListData.isNotEmpty) { + for (var rxListItem in sumListData) { + mnCurrentSummEuroYear.value = + mnCurrentSummEuroYear.value + double.parse(rxListItem.szSumme!); + mnCurrentSummLiterYear.value = + mnCurrentSummLiterYear.value + + double.parse(rxListItem.szVerbrauch!); + } + } + update(); + } +} diff --git a/lib/pages/graph/graph_view.dart b/lib/pages/graph/graph_view.dart new file mode 100644 index 0000000..b06aded --- /dev/null +++ b/lib/pages/graph/graph_view.dart @@ -0,0 +1,148 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../../utils/extensions/constants.dart'; +import './graph_controller.dart'; +import './widgets/chart_desc.dart'; +import './widgets/chart_lines.dart'; +import './widgets/chart_single_lines.dart'; +import './widgets/my_list_tile_card.dart'; +import '../../pages/tanklist/tanklist_view.dart'; + +class GraphPage extends GetView { + static const namedRoute = '/graph-statistic-page'; + const GraphPage({super.key}); + + @override + Widget build(BuildContext context) { + Size queryDisplaySize = MediaQuery.of(context).size; + var graphCtrl = controller; + return PopScope( + canPop: false, + child: SafeArea( + child: Scaffold( + body: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox( + width: 50, + child: IconButton( + onPressed: () => + Get.offAndToNamed(TanklistPage.namedRoute), + icon: const Icon(Icons.list), + ), + ), + SizedBox( + width: queryDisplaySize.width - 70, + child: Obx(() => _displayDropDownMenue(graphCtrl)), + ), + ], + ), + ), + const SizedBox(height: 8.0), + Obx( + () => Container( + color: Colors.grey.shade900, + padding: const EdgeInsets.symmetric(horizontal: 40), + width: MediaQuery.of(context).size.width, + child: Column( + children: [ + ChartDescription( + backgroundColor: kColorEuroChart, + mnWidth: double.infinity, + szDescText: '€ Jahressumme', + szCurrentSumData: + graphCtrl.mnCurrentSummEuroYear.toStringAsFixed(2), + ), + ChartDescription( + backgroundColor: kColorBenzinChart, + mnWidth: double.infinity, + szDescText: 'L Jahresverbrauch', + szCurrentSumData: + graphCtrl.mnCurrentSummLiterYear.toStringAsFixed(2), + ), + ], + ), + ), + ), + const Divider(), + LineChartLines( + firstLineColor: kColorEuroChart, + secondLineColor: kColorBenzinChart, + ), + Obx( + () => graphCtrl.blIsLoading.value + ? const Center( + child: CircularProgressIndicator(color: Colors.cyan), + ) + : const SizedBox.shrink(), + ), + const Divider(), + Obx( + () => Container( + color: Colors.grey.shade900, + padding: const EdgeInsets.symmetric(horizontal: 40), + child: ChartDescription( + backgroundColor: kColorPerLiterChart, + mnWidth: queryDisplaySize.width, + szDescText: '€/L Durchschnitt', + szCurrentSumData: graphCtrl.mnCurrentAveragePerLiter.value + .toStringAsFixed(2), + ), + ), + ), + const Divider(), + LineChartSingleLine(singleLineColor: kColorPerLiterChart), + const Divider(), + Obx( + () => graphCtrl.blIsLoading.value + ? const Center( + child: CircularProgressIndicator(color: Colors.cyan), + ) + : const SizedBox.shrink(), + ), + Obx( + () => Expanded( + child: ListView.builder( + itemCount: graphCtrl.sumListData.length, + itemBuilder: (context, index) { + return MyListTileCard(graphCtrl: graphCtrl, index: index); + }, + ), + ), + ), + ], + ), + ), + ), + ); + } + + DropdownButtonFormField _displayDropDownMenue(GraphController controller) { + var graphCtrl = controller; + var list = graphCtrl.listYearModel; + var valueOfList = graphCtrl.yearValue.value; + return DropdownButtonFormField( + decoration: kInputDecorationDropDownMenueYear, + items: list.map((map) { + return DropdownMenuItem( + value: map.mnYear, + child: Text(map.szDescription), + ); + }).toList(), + isDense: true, + isExpanded: true, + value: valueOfList, + onChanged: ((value) { + valueOfList = value; + graphCtrl.yearValue.value = valueOfList; + if (graphCtrl.yearValue.value > 0) { + graphCtrl.getTankListPerYear(); + } + }), + ); + } +} diff --git a/lib/pages/graph/widgets/bar_chart_widget.dart b/lib/pages/graph/widgets/bar_chart_widget.dart new file mode 100644 index 0000000..950c2c6 --- /dev/null +++ b/lib/pages/graph/widgets/bar_chart_widget.dart @@ -0,0 +1,124 @@ +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; +import '../../../utils/extensions/constants.dart'; +import '../graph_controller.dart'; + +class BarChartWidget extends StatelessWidget { + final GraphController graphCtrl; + const BarChartWidget({super.key, required this.graphCtrl}); + + @override + Widget build(BuildContext context) { + return AspectRatio( + aspectRatio: 2, + child: BarChart( + BarChartData( + //alignment: BarChartAlignment.center, + maxY: 50, + baselineY: 0, + barTouchData: barTouchData, + titlesData: titlesData, + borderData: borderData, + barGroups: barGroups, + gridData: const FlGridData(show: false), + ), + ), + ); + } + + BarTouchData get barTouchData => BarTouchData( + enabled: false, + touchTooltipData: BarTouchTooltipData( + //tooltipBgColor: Colors.transparent, + tooltipPadding: EdgeInsets.zero, + tooltipMargin: 8, + getTooltipItem: + ( + BarChartGroupData group, + int groupIndex, + BarChartRodData rod, + int rodIndex, + ) { + return BarTooltipItem( + rod.toY.round().toString(), + const TextStyle(color: Colors.cyan, fontWeight: FontWeight.bold), + ); + }, + ), + ); + + Widget getTitles(double value, TitleMeta meta) { + List dayMonListString = graphCtrl.getHeadDescription(); + for (var i = 0; i < dayMonListString.length; i++) { + if (i == value.toInt()) { + graphCtrl.szBarTitle.value = dayMonListString[i]; + } + } + return SideTitleWidget( + fitInside: SideTitleFitInsideData.fromTitleMeta(meta), + space: 4, + meta: meta, + child: Text(graphCtrl.szBarTitle.value, style: kBarTitleStyle), + ); + } + + FlTitlesData get titlesData => FlTitlesData( + show: true, + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: 30, + getTitlesWidget: getTitles, + ), + ), + leftTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), + topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), + rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), + ); + + FlBorderData get borderData => FlBorderData(show: false); + + LinearGradient get _barsGradient => LinearGradient( + colors: [Colors.blue.shade400, Colors.red.shade300], + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + ); + + List get barGroups => [ + BarChartGroupData( + x: 0, + barRods: [BarChartRodData(toY: 8, gradient: _barsGradient)], + showingTooltipIndicators: [0], + ), + BarChartGroupData( + x: 1, + barRods: [BarChartRodData(toY: 10, gradient: _barsGradient)], + showingTooltipIndicators: [0], + ), + BarChartGroupData( + x: 2, + barRods: [BarChartRodData(toY: 14, gradient: _barsGradient)], + showingTooltipIndicators: [0], + ), + BarChartGroupData( + x: 3, + barRods: [BarChartRodData(toY: 15, gradient: _barsGradient)], + showingTooltipIndicators: [0], + ), + BarChartGroupData( + x: 4, + barRods: [BarChartRodData(toY: 13, gradient: _barsGradient)], + showingTooltipIndicators: [0], + ), + BarChartGroupData( + x: 5, + barRods: [BarChartRodData(toY: 10, gradient: _barsGradient)], + showingTooltipIndicators: [0], + ), + BarChartGroupData( + x: 6, + barRods: [BarChartRodData(toY: 16, gradient: _barsGradient)], + showingTooltipIndicators: [0], + ), + ]; +} diff --git a/lib/pages/graph/widgets/chart_desc.dart b/lib/pages/graph/widgets/chart_desc.dart new file mode 100644 index 0000000..21d07c5 --- /dev/null +++ b/lib/pages/graph/widgets/chart_desc.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +import '../../../utils/extensions/constants.dart'; + + + +class ChartDescription extends StatelessWidget { + final double mnWidth; + final Color backgroundColor; + final String szDescText; + final String szCurrentSumData; + const ChartDescription({ + super.key, + required this.mnWidth, + required this.backgroundColor, + required this.szDescText, + required this.szCurrentSumData, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: mnWidth, + child: Row( + children: [ + Container( + decoration: BoxDecoration( + color: backgroundColor, + borderRadius: BorderRadius.circular(15), + ), + width: 70, + height: 20, + child: Center( + child: Text(szCurrentSumData, style: kChartDescriptionFontStyle), + ), + ), + const SizedBox(width: 10), + Text(szDescText, style: kTextStyle), + ], + ), + ); + } +} diff --git a/lib/pages/graph/widgets/chart_lines.dart b/lib/pages/graph/widgets/chart_lines.dart new file mode 100644 index 0000000..448ee4a --- /dev/null +++ b/lib/pages/graph/widgets/chart_lines.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:get/get.dart'; +import '../graph_controller.dart'; + +// 08.08.23 Mod. Line Chart with tree Lines +class LineChartLines extends GetView { + final Color firstLineColor; + final Color secondLineColor; + + const LineChartLines({ + super.key, + required this.firstLineColor, + required this.secondLineColor, + }); + + @override + Widget build(BuildContext context) { + final GraphController gCtrl = controller; + return AspectRatio( + aspectRatio: 3, + child: Obx( + () => LineChart( + LineChartData( + lineBarsData: [ + LineChartBarData( + color: firstLineColor, + spots: gCtrl.pointsEuro + .map((point) => FlSpot(point.x, point.y)) + .toList(), + isCurved: false, + dotData: const FlDotData(show: true), + ), + LineChartBarData( + isStepLineChart: false, + color: secondLineColor, + spots: gCtrl.pointsGasoline + .map((point) => FlSpot(point.x, point.y)) + .toList(), + isCurved: false, + dotData: const FlDotData(show: true), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/graph/widgets/chart_single_lines.dart b/lib/pages/graph/widgets/chart_single_lines.dart new file mode 100644 index 0000000..1aa8851 --- /dev/null +++ b/lib/pages/graph/widgets/chart_single_lines.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:get/get.dart'; + +import '../graph_controller.dart'; + +// 08.08.23 Mod. Line Chart with tree Lines +class LineChartSingleLine extends GetView { + final Color singleLineColor; + + const LineChartSingleLine({super.key, required this.singleLineColor}); + + @override + Widget build(BuildContext context) { + final GraphController gCtrl = controller; + return AspectRatio( + aspectRatio: 3.55, + child: Obx( + () => LineChart( + LineChartData( + lineBarsData: [ + LineChartBarData( + color: singleLineColor, + spots: gCtrl.pointsPerLiter + .map((point) => FlSpot(point.x, point.y)) + .toList(), + isCurved: false, + dotData: const FlDotData(show: true), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/graph/widgets/my_list_tile_card.dart b/lib/pages/graph/widgets/my_list_tile_card.dart new file mode 100644 index 0000000..3d10127 --- /dev/null +++ b/lib/pages/graph/widgets/my_list_tile_card.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +import '../../../utils/extensions/constants.dart'; +import '../graph_controller.dart'; + + +class MyListTileCard extends StatelessWidget { + const MyListTileCard({ + super.key, + required this.graphCtrl, + required this.index, + }); + + final GraphController graphCtrl; + final int index; + + @override + Widget build(BuildContext context) { + return Card( + shadowColor: Colors.grey.shade200, + child: ListTile( + title: Text( + '${graphCtrl.sumListData[index].szMonth}', + style: kTextStyle, + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Monatsausgaben: ${graphCtrl.sumListData[index].szSumme} €', + style: kTextStyleSub, + ), + Text( + 'Monatsverbrauch: ${graphCtrl.sumListData[index].szVerbrauch} L', + style: kTextStyleSub, + ), + Text( + 'Tankungen: ${graphCtrl.sumListData[index].mnTankungen}', + style: kTextStyleSub, + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/tank/tank_controller.dart b/lib/pages/tank/tank_controller.dart index 18fedf0..139a9b6 100644 --- a/lib/pages/tank/tank_controller.dart +++ b/lib/pages/tank/tank_controller.dart @@ -24,7 +24,7 @@ class TankController extends GetxController { final Rx currentPosition = Rx(null); final Rx isLoading = false.obs; final Rx errorMessage = Rx(null); - final rxOrtString = '?'.obs; + //final rxOrtString = '?'.obs; final rxSessionIdString = '?'.obs; final rxSummePreisString = '0.00'.obs; // TextEditingController für die Formulareingaben @@ -70,10 +70,10 @@ class TankController extends GetxController { // Hier kannst du die Logik hinzufügen, um den Standort zu verwenden, z.B. // den Standort in der UI anzuzeigen oder an einen Server zu senden. var map = {'lat': latitude, 'lng': longitude}; - rxOrtString.value = await _locationRepository.getNearbyLocation(map); - ortController.text = rxOrtString.value; + var loc = await _locationRepository.getNearbyLocation(map); + ortController.text = loc; // Print Standortinformationen in der Konsole - print('Nearby Location: ${rxOrtString.value}'); + print('Nearby Location: $loc'); print('Current Position: Latitude: $latitude, Longitude: $longitude'); } catch (e) { // Hier fängst du die Fehler aus dem Repository auf diff --git a/lib/pages/tank/tank_view.dart b/lib/pages/tank/tank_view.dart index a97b1a8..e5cff72 100644 --- a/lib/pages/tank/tank_view.dart +++ b/lib/pages/tank/tank_view.dart @@ -52,117 +52,112 @@ class TankPage extends GetView { ), ), Obx( - () => SingleChildScrollView( - padding: EdgeInsets.only( - top: MediaQuery.of(context).padding.top + kToolbarHeight + 20, - left: 20, - right: 20, - bottom: 20, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - CircleAvatar( - backgroundColor: Colors.blue, - radius: 40, - child: Text( - controller.circleAvatarUserChar.value, - style: const TextStyle( - color: Colors.black, - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), - ), - const SizedBox(height: 5), - Text( - controller.userNameToDisplay.value, - style: const TextStyle( - fontSize: 20, - backgroundColor: Colors.black87, - color: Colors.white, - fontWeight: FontWeight.bold, - letterSpacing: 3, - ), - ), - controller.isLoading.value - ? Text('Location is loading...') - : Text( - controller.rxOrtString.value, - style: const TextStyle( - fontSize: 15, - backgroundColor: Colors.black87, - color: Colors.blue, - ), - ), - const SizedBox(height: 20), - inputFields(), - const SizedBox(height: 20), - Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: Colors.black.withValues(alpha: 0.9), - ), - child: Text.rich( - TextSpan( - text: 'Summe: ', - style: TextStyle( - fontSize: 18, - color: Colors.grey.shade300, - ), - children: [ - TextSpan( - // Hier kommt der Wert als separater TextSpan - text: controller.rxSummePreisString.value, - // Doppelt so groß wie der Standardtext', - style: TextStyle( - fontSize: 30, // Doppelt so groß wie 18 - color: Colors.blue, // Blaue Farbe - fontWeight: FontWeight - .bold, // Optional: Fetter Text, um ihn hervorzuheben - ), - ), - ], - ), - ), - ), - const SizedBox(height: 20), - SizedBox( - width: 350, - height: 50, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue.withValues( - alpha: 0.9, - ), // Button-Farbe - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - 10, - ), // Abgerundete Ecken - ), - ), - onPressed: () { - controller.saveTankstopp(); - }, + () => PopScope( + canPop: false, + child: SingleChildScrollView( + padding: EdgeInsets.only( + top: MediaQuery.of(context).padding.top + + kToolbarHeight + + 20, + left: 20, + right: 20, + bottom: 20, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CircleAvatar( + backgroundColor: Colors.blue, + radius: 40, child: Text( - 'Tankstop erfassen', - style: TextStyle(color: Colors.white, fontSize: 20), + controller.circleAvatarUserChar.value, + style: const TextStyle( + color: Colors.black, + fontSize: 24, + fontWeight: FontWeight.bold, + ), ), ), - ), - Container( - margin: EdgeInsets.only(top: 20, bottom: 50), - child: Text( - 'Hinweis: Alle Felder sind Pflichtfelder.', - style: TextStyle( - fontSize: 16, - color: Colors.red.shade300, + const SizedBox(height: 5), + Text( + controller.userNameToDisplay.value, + style: const TextStyle( + fontSize: 20, + backgroundColor: Colors.black87, + color: Colors.white, + fontWeight: FontWeight.bold, + letterSpacing: 3, ), ), - ), - ], + const SizedBox(height: 20), + inputFields(), + const SizedBox(height: 20), + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.black.withValues(alpha: 0.9), + ), + child: Text.rich( + TextSpan( + text: 'Summe: ', + style: TextStyle( + fontSize: 18, + color: Colors.grey.shade300, + ), + children: [ + TextSpan( + // Hier kommt der Wert als separater TextSpan + text: controller.rxSummePreisString.value, + // Doppelt so groß wie der Standardtext', + style: TextStyle( + fontSize: 30, // Doppelt so groß wie 18 + color: Colors.blue, // Blaue Farbe + fontWeight: FontWeight + .bold, // Optional: Fetter Text, um ihn hervorzuheben + ), + ), + ], + ), + ), + ), + const SizedBox(height: 20), + SizedBox( + width: 350, + height: 50, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue.withValues( + alpha: 0.9, + ), // Button-Farbe + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 10, + ), // Abgerundete Ecken + ), + ), + onPressed: () { + controller.saveTankstopp(); + }, + child: Text( + 'Tankstop erfassen', + style: TextStyle(color: Colors.white, fontSize: 20), + ), + ), + ), + Container( + margin: EdgeInsets.only(top: 20, bottom: 50), + child: Text( + 'Hinweis: Alle Felder sind Pflichtfelder.', + style: TextStyle( + fontSize: 16, + color: Colors.red.shade300, + ), + ), + ), + ], + ), ), ), ), diff --git a/lib/pages/tanklist/tanklist_controller.dart b/lib/pages/tanklist/tanklist_controller.dart index 2e60ef1..7824dcc 100644 --- a/lib/pages/tanklist/tanklist_controller.dart +++ b/lib/pages/tanklist/tanklist_controller.dart @@ -6,6 +6,7 @@ import 'package:get_storage/get_storage.dart'; import '../../data/models/tank_model.dart'; import '../../data/repository/appwrite_repository.dart'; import '../../pages/tank/tank_view.dart'; +import '../graph/graph_view.dart'; import '../login/login_view.dart'; class TanklistController extends GetxController { @@ -68,7 +69,7 @@ class TanklistController extends GetxController { message = 'Fehler beim Laden der Tankliste'; print('Error fetching tank list: $e'); } finally { - if (!isErrorByLoading){ + if (!isErrorByLoading) { isloadingList(false); } } @@ -104,10 +105,6 @@ class TanklistController extends GetxController { } void goToChartView() { - //Get.offAndToNamed(GraphPage.namedRoute); - } - - void goToTrackingPage() { - //Get.offAndToNamed(TrackingPage.namedRoute); + Get.offAndToNamed(GraphPage.namedRoute); } } diff --git a/lib/pages/tanklist/tanklist_view.dart b/lib/pages/tanklist/tanklist_view.dart index ddb7bef..e0fa0af 100644 --- a/lib/pages/tanklist/tanklist_view.dart +++ b/lib/pages/tanklist/tanklist_view.dart @@ -10,94 +10,86 @@ class TanklistPage extends GetView { @override Widget build(BuildContext context) { var tankListCtrl = controller; - return SafeArea( - child: Scaffold( - appBar: AppBar( - title: Text('Tankstops'), - centerTitle: true, - //backgroundColor: Colors.grey.shade600, - actions: [ - IconButton( - icon: Icon(Icons.add_chart, color: Colors.grey.shade300), - onPressed: () async { - // Handle go to Chart View - tankListCtrl.goToChartView(); - }, - ), - IconButton( - icon: Icon(Icons.logout, color: Colors.grey.shade300), - onPressed: () async { - // Handle logout logic here - tankListCtrl.logoutSessionAndGoToLoginPage(); - }, - ), - ], - ), - floatingActionButton: Column( - mainAxisAlignment: - MainAxisAlignment.end, // Positioniere die Buttons am unteren Ende - crossAxisAlignment: CrossAxisAlignment.end, // Richte sie rechts aus - mainAxisSize: MainAxisSize.min, // Nimm nur den benötigten Platz ein - children: [ - FloatingActionButton( - onPressed: () => tankListCtrl.goToInputPage(), - backgroundColor: Colors.blue, - child: Icon(Icons.add), - ), - const SizedBox(width: 16), - FloatingActionButton( - onPressed: () => tankListCtrl.goToTrackingPage(), - backgroundColor: Colors.green, - child: Icon(Icons.gps_fixed_rounded), - ), - ], - ), - body: Obx(() - => tankListCtrl.isloadingList.value == false ? Padding( - padding: EdgeInsetsGeometry.only(left: 25, right: 25), - child: ListView.builder( - padding: EdgeInsets.only(top: 8, bottom: 8), - physics: const BouncingScrollPhysics(), - itemBuilder: ((BuildContext context, int index) { - var item = tankListCtrl.tankList[index]; - return Column( - children: [ - Container( - decoration: BoxDecoration( - color: Colors.blue.withValues(alpha: 0.2), - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(20), - bottomRight: Radius.circular(20), - ), - boxShadow: [ - BoxShadow( - color: Colors.grey.withValues( - alpha: 0.7, - ), // Die Farbe des Schattens - spreadRadius: - 2, // Wie weit sich der Schatten ausbreitet - blurRadius: - 3, // Wie stark der Schatten verschwommen ist - offset: const Offset( - 0, - 3, - ), // Der Versatz des Schattens (x, y) + return PopScope( + canPop: false, + child: SafeArea( + child: Scaffold( + appBar: AppBar( + title: Text('Tankstops'), + centerTitle: true, + //backgroundColor: Colors.grey.shade600, + actions: [ + IconButton( + icon: Icon(Icons.add_chart, color: Colors.grey.shade300), + onPressed: () async { + // Handle go to Chart View + tankListCtrl.goToChartView(); + }, + ), + IconButton( + icon: Icon(Icons.logout, color: Colors.grey.shade300), + onPressed: () async { + // Handle logout logic here + tankListCtrl.logoutSessionAndGoToLoginPage(); + }, + ), + ], + ), + floatingActionButton: Column( + mainAxisAlignment: MainAxisAlignment + .end, // Positioniere die Buttons am unteren Ende + crossAxisAlignment: CrossAxisAlignment.end, // Richte sie rechts aus + mainAxisSize: MainAxisSize.min, // Nimm nur den benötigten Platz ein + children: [ + FloatingActionButton( + onPressed: () => tankListCtrl.goToInputPage(), + backgroundColor: Colors.blue, + child: Icon(Icons.add), + ), + ], + ), + body: Obx(() => tankListCtrl.isloadingList.value == false + ? Padding( + padding: EdgeInsetsGeometry.only(left: 25, right: 25), + child: ListView.builder( + padding: EdgeInsets.only(top: 8, bottom: 8), + physics: const BouncingScrollPhysics(), + itemBuilder: ((BuildContext context, int index) { + var item = tankListCtrl.tankList[index]; + return Column( + children: [ + Container( + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.blue.withValues( + alpha: 0.35, + ), // Die Farbe des Schattens + spreadRadius: + 1, // Wie weit sich der Schatten ausbreitet + blurRadius: + 1, // Wie stark der Schatten verschwommen ist + offset: const Offset( + 0, + 3, + ), // Der Versatz des Schattens (x, y) + ), + ], + ), + child: MyListTileItem(listItem: item), ), + SizedBox(height: 15), ], - ), - child: MyListTileItem(listItem: item), - ), - SizedBox(height: 15), - ], - ); - }), - itemCount: tankListCtrl.tankList.length, - ), - ):Center( - child: CircularProgressIndicator( - color: Colors.blue, - ), - ) + ); + }), + itemCount: tankListCtrl.tankList.length, + ), + ) + : Center( + child: CircularProgressIndicator( + color: Colors.blue, + ), + )), ), ), ); diff --git a/lib/utils/extensions/sample_bindings.dart b/lib/utils/extensions/sample_bindings.dart index 0ea1507..ccb269b 100644 --- a/lib/utils/extensions/sample_bindings.dart +++ b/lib/utils/extensions/sample_bindings.dart @@ -1,5 +1,6 @@ import 'package:get/get.dart'; +import '../../pages/graph/graph_controller.dart'; import '../../pages/login/login_controller.dart'; import '../../pages/tank/tank_controller.dart'; import '../../pages/tanklist/tanklist_controller.dart'; @@ -13,7 +14,7 @@ class SampleBindings extends Bindings { Get.lazyPut(() => LoginController()); Get.lazyPut(() => TankController()); Get.lazyPut(() => TanklistController()); - // Get.lazyPut(() => GraphController(AuthRepository(AppWriteProvider()))); + Get.lazyPut(() => GraphController()); // Get.lazyPut(() => TrackingController(AuthRepository(AppWriteProvider()))); // Get.lazyPut(() => MapController(AuthRepository(AppWriteProvider()), LocationRepository(LocationProvider()))); } diff --git a/lib/utils/extensions/sample_routes.dart b/lib/utils/extensions/sample_routes.dart index 439ce0e..a67d941 100644 --- a/lib/utils/extensions/sample_routes.dart +++ b/lib/utils/extensions/sample_routes.dart @@ -1,5 +1,6 @@ import 'package:get/get.dart'; +import '../../pages/graph/graph_view.dart'; import '../../pages/login/login_view.dart'; import '../../pages/tank/tank_view.dart'; import '../../pages/tanklist/tanklist_view.dart'; @@ -25,11 +26,11 @@ class SampleRouts { page: () => const TanklistPage(), binding: sampleBindings, ), - // GetPage( - // name: GraphPage.namedRoute, - // page: () => const GraphPage(), - // binding: sampleBindings, - // ), + GetPage( + name: GraphPage.namedRoute, + page: () => const GraphPage(), + binding: sampleBindings, + ), // GetPage( // name: TrackingPage.namedRoute, // page: () => const TrackingPage(), diff --git a/pubspec.lock b/pubspec.lock index 608621e..4fb8e19 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -137,6 +137,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.2" + equatable: + dependency: transitive + description: + name: equatable + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + url: "https://pub.dev" + source: hosted + version: "2.0.7" ffi: dependency: transitive description: @@ -161,6 +169,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + fl_chart: + dependency: "direct main" + description: + name: fl_chart + sha256: "577aeac8ca414c25333334d7c4bb246775234c0e44b38b10a82b559dd4d764e7" + url: "https://pub.dev" + source: hosted + version: "1.0.0" flutter: dependency: "direct main" description: flutter @@ -730,4 +746,4 @@ packages: version: "3.1.3" sdks: dart: ">=3.7.0-0 <4.0.0" - flutter: ">=3.27.0" + flutter: ">=3.27.4" diff --git a/pubspec.yaml b/pubspec.yaml index 9ed47c8..4546c0c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: get_storage: ^2.1.1 http: ^1.4.0 latlong2: ^0.9.1 + fl_chart: ^1.0.0 dependency_overrides: flutter_web_auth_2: 4.1.0