commit 170825 daham Rechner
This commit is contained in:
211
lib/pages/login/login_controller.dart
Normal file
211
lib/pages/login/login_controller.dart
Normal file
@@ -0,0 +1,211 @@
|
||||
import 'package:appwrite/appwrite.dart';
|
||||
import 'package:appwrite/models.dart' as models;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../data/repository/appwrite_repository.dart';
|
||||
import '../../utils/extensions/static_helper.dart';
|
||||
import '../tank/tank_view.dart';
|
||||
|
||||
class LoginController extends GetxController {
|
||||
final AppwriteRepository _authRepository = AppwriteRepository();
|
||||
|
||||
final isVisible = false.obs;
|
||||
//Form Key
|
||||
final formKey = GlobalKey<FormState>();
|
||||
bool isFormValid = false;
|
||||
|
||||
final emailController = TextEditingController();
|
||||
final passwordController = TextEditingController();
|
||||
final nameController = TextEditingController();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
// Initialize any necessary data or state here
|
||||
print('LoginController initialized');
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
// This method is called when the controller is ready
|
||||
print('LoginController is ready');
|
||||
if (StaticHelper.hasData()) {
|
||||
// If session ID exists, navigate to TankPage
|
||||
print('Session ID found, navigating to TankPage');
|
||||
goToTankPage();
|
||||
} else {
|
||||
// If no session ID, initialize the login controller
|
||||
print('No session ID found, initializing LoginController');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
// Clean up any resources or listeners here
|
||||
emailController.dispose();
|
||||
passwordController.dispose();
|
||||
nameController.dispose();
|
||||
// emailFocusNode.dispose();
|
||||
// passwordFocusNode.dispose();
|
||||
// nameFocusNode.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
void clearTextEditingController() {
|
||||
emailController.clear();
|
||||
passwordController.clear();
|
||||
nameController.clear();
|
||||
}
|
||||
|
||||
String? validateEmail(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Bitte geben Sie eine E-Mail-Adresse ein';
|
||||
}
|
||||
if (!GetUtils.isEmail(value)) {
|
||||
return 'Bitte geben Sie eine gültige E-Mail-Adresse ein';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? validatePassword(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Bitte geben Sie ein Passwort ein';
|
||||
}
|
||||
if (value.length < 8) {
|
||||
return 'Das Passwort muss mindestens 8 Zeichen lang sein';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? validateName(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Bitte geben Sie Ihren Namen ein';
|
||||
}
|
||||
if (value.length < 3) {
|
||||
return 'Der Name muss mindestens 3 Zeichen lang sein';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
var message = 'NoMessage!';
|
||||
var isError = false;
|
||||
|
||||
// Registrierung
|
||||
Future<void> register() async {
|
||||
isError = false;
|
||||
message = 'NoMessage!';
|
||||
isFormValid = formKey.currentState!.validate();
|
||||
if (!isFormValid) {
|
||||
print('Formular ist ungültig');
|
||||
return;
|
||||
} else {
|
||||
print('Formular ist gültig');
|
||||
formKey.currentState!.save();
|
||||
try {
|
||||
await _authRepository.signup({
|
||||
'email': emailController.text,
|
||||
'password': passwordController.text,
|
||||
'name': nameController.text,
|
||||
}).then((models.User userValue) {
|
||||
// GetStorage data storage
|
||||
isVisible(false);
|
||||
print(
|
||||
'User was stored and Loggedin: ${userValue.name}, ${userValue.$id}, ${userValue.email}',
|
||||
);
|
||||
// Store session ID in GetStorage
|
||||
StaticHelper.dataBox.write('userId', userValue.$id);
|
||||
StaticHelper.dataBox.write('userName', userValue.name);
|
||||
StaticHelper.dataBox.write('userEmail', userValue.email);
|
||||
// Go to TankPage
|
||||
goToTankPage();
|
||||
message = 'Sie wurden registriert und Angemeldet!!';
|
||||
StaticHelper.getMySnackeBar('Erfolg', message, Colors.green);
|
||||
}).catchError((error) {
|
||||
if (error is AppwriteException) {
|
||||
// Handle specific Appwrite exceptions
|
||||
print('Appwrite Fehler: ${error.message}');
|
||||
message = 'Appwrite Fehler: ${error.message}';
|
||||
isError = true;
|
||||
} else {
|
||||
// Handle other types of errors
|
||||
message = 'Allgemeiner Fehler: $error';
|
||||
print('Allgemeiner Fehler: $error');
|
||||
isError = true;
|
||||
}
|
||||
message = 'Fehler bei der Registrierung: $error';
|
||||
print('Fehler bei der Registrierung: $error');
|
||||
//Clear GetStorage session ID
|
||||
StaticHelper.removeFromStorage();
|
||||
isError = true;
|
||||
});
|
||||
} catch (e) {
|
||||
message = 'Fehler bei Registrierung: $e';
|
||||
print('Fehler bei Registrierung: $e');
|
||||
//Clear GetStorage session ID
|
||||
StaticHelper.removeFromStorage();
|
||||
isError = true;
|
||||
}
|
||||
if (isError) StaticHelper.getMySnackeBar('Fehler', message, Colors.red);
|
||||
}
|
||||
}
|
||||
|
||||
// Login
|
||||
Future<void> login() async {
|
||||
message = 'NoMessage!';
|
||||
isError = false;
|
||||
|
||||
isFormValid = formKey.currentState!.validate();
|
||||
if (!isFormValid) {
|
||||
print('Formular ist ungültig');
|
||||
return;
|
||||
} else {
|
||||
print('Formular ist gültig');
|
||||
formKey.currentState!.save();
|
||||
try {
|
||||
await _authRepository.login({
|
||||
'email': emailController.text,
|
||||
'password': passwordController.text,
|
||||
}).then((models.Session session) {
|
||||
// Store session ID in GetStorage
|
||||
StaticHelper.dataBox.write('userId', session.$id);
|
||||
print('Session ID stored: ${StaticHelper.dataBox.read('sessinId')}');
|
||||
print(
|
||||
'Erfolgreich eingeloggt: ${session.$id}\n${session.providerUid}',
|
||||
);
|
||||
// Go to TankPage
|
||||
goToTankPage();
|
||||
StaticHelper.getMySnackeBar(
|
||||
'Erfolg', 'Sie wurden Angemeldet!!', Colors.green);
|
||||
}).catchError((error) {
|
||||
if (error is AppwriteException) {
|
||||
// Handle specific Appwrite exceptions
|
||||
isError = true;
|
||||
message = 'Appwrite Fehler: ${error.message}';
|
||||
print('Appwrite Fehler: ${error.message}');
|
||||
} else {
|
||||
// Handle other types of errors
|
||||
print('Allgemeiner Fehler: $error');
|
||||
isError = true;
|
||||
message = 'Allgemeiner Fehler: $error';
|
||||
}
|
||||
print('Fehler beim Login: $error');
|
||||
isError = true;
|
||||
message = 'Fehler beim Login: $error';
|
||||
_authRepository.logout();
|
||||
});
|
||||
} catch (e) {
|
||||
print('Fehler beim Login: $e');
|
||||
isError = true;
|
||||
message = 'Fehler beim Login: $e';
|
||||
_authRepository.logout();
|
||||
}
|
||||
if (isError) StaticHelper.getMySnackeBar('Fehler', message, Colors.red);
|
||||
}
|
||||
}
|
||||
|
||||
void goToTankPage() {
|
||||
Get.offAndToNamed(TankPage.namedRoute);
|
||||
}
|
||||
}
|
||||
162
lib/pages/login/login_view.dart
Normal file
162
lib/pages/login/login_view.dart
Normal file
@@ -0,0 +1,162 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'login_controller.dart';
|
||||
|
||||
class LoginPage extends GetView<LoginController> {
|
||||
static const namedRoute = '/login-page';
|
||||
const LoginPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
headerTextTankGuru(),
|
||||
const SizedBox(height: 5),
|
||||
headerTextDescription(),
|
||||
const SizedBox(height: 10),
|
||||
loadingImage(),
|
||||
inputFields(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
//the structure of the login page
|
||||
SizedBox loadingImage() {
|
||||
return SizedBox(
|
||||
width: 300,
|
||||
height: 300,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(40),
|
||||
child: Image.asset('lib/images/gasolineGuru.jpg', fit: BoxFit.cover),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Text headerTextTankGuru() {
|
||||
return Text(
|
||||
'Tank GURU',
|
||||
style: TextStyle(
|
||||
fontSize: 35,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.teal[600],
|
||||
letterSpacing: 2,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Text headerTextDescription() {
|
||||
return Text(
|
||||
'Das ultimative Tanken',
|
||||
style: TextStyle(fontSize: 20, color: Colors.grey[300]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget inputFields() {
|
||||
return Obx(
|
||||
() => SizedBox(
|
||||
width: 300,
|
||||
child: Form(
|
||||
key: controller.formKey,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(height: 20),
|
||||
TextFormField(
|
||||
validator: (value) => controller.validateEmail(value),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
controller: controller.emailController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Email',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
TextFormField(
|
||||
validator: (value) => controller.validatePassword(value),
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
controller: controller.passwordController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Password',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
obscureText: true,
|
||||
),
|
||||
if (controller.isVisible.value) ...[
|
||||
SizedBox(height: 20),
|
||||
TextFormField(
|
||||
validator: (value) => controller.validateName(value),
|
||||
keyboardType: TextInputType.name,
|
||||
controller: controller.nameController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Name',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
],
|
||||
SizedBox(height: 20),
|
||||
SizedBox(
|
||||
width: 300,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
controller.isVisible.value == false
|
||||
? controller.login()
|
||||
: controller.register();
|
||||
},
|
||||
child: controller.isVisible.value == false
|
||||
? Text('Login')
|
||||
: Text('Registrieren'),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('Noch kein Konto? '),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (!controller.isVisible.value) {
|
||||
controller.isVisible.value = true;
|
||||
} else {
|
||||
controller.isVisible.value = false;
|
||||
}
|
||||
},
|
||||
child: controller.isVisible.value == false
|
||||
? Text(
|
||||
'Sign Up',
|
||||
style: TextStyle(
|
||||
color: Colors.teal,
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
'Sign In',
|
||||
style: TextStyle(
|
||||
color: Colors.teal,
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
266
lib/pages/tank/tank_controller.dart
Normal file
266
lib/pages/tank/tank_controller.dart
Normal file
@@ -0,0 +1,266 @@
|
||||
import 'package:appwrite/appwrite.dart';
|
||||
import 'package:appwrite/models.dart' as models;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:tankguru_flutter_app_appwrite/utils/extensions/static_helper.dart';
|
||||
|
||||
import '../../data/repository/location_repository.dart';
|
||||
import '../../data/repository/appwrite_repository.dart';
|
||||
import '../login/login_view.dart';
|
||||
|
||||
class TankController extends GetxController {
|
||||
final _dataBox = GetStorage('MyUserStorage');
|
||||
//AppWrite API-REST get Data
|
||||
final AppwriteRepository _authRepository = AppwriteRepository();
|
||||
// GEOLOCATING Services
|
||||
final LocationRepository _locationRepository = LocationRepository();
|
||||
|
||||
// Rx-Variablen für die UI, um auf Änderungen zu reagieren
|
||||
final circleAvatarUserChar = 'A'.obs;
|
||||
final userNameToDisplay = 'Test User'.obs;
|
||||
final Rx<Position?> currentPosition = Rx<Position?>(null);
|
||||
final Rx<bool> isLoading = false.obs;
|
||||
final Rx<String?> errorMessage = Rx<String?>(null);
|
||||
final rxOrtString = '?'.obs;
|
||||
final rxSessionIdString = '?'.obs;
|
||||
final rxSummePreisString = '0.00'.obs;
|
||||
// TextEditingController für die Formulareingaben
|
||||
final formKeyTank = GlobalKey<FormState>();
|
||||
bool isFormValid = false;
|
||||
DateTime? _selectedDateTime;
|
||||
final dateController = TextEditingController();
|
||||
final f = DateFormat('yyyy-MM-dd');
|
||||
final kilometerStandEdittingController = TextEditingController();
|
||||
final mengeController = TextEditingController();
|
||||
final pricePerLiterController = TextEditingController();
|
||||
|
||||
final FocusNode firstFocusNode = FocusNode(); // Deklariere den FocusNode
|
||||
|
||||
// Methode für das ausgewählte Datum
|
||||
Future<void> selectDateTime(BuildContext context) async {
|
||||
// 1. Datum auswählen
|
||||
final DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: _selectedDateTime ?? DateTime.now(),
|
||||
firstDate: DateTime(2000),
|
||||
lastDate: DateTime(2101),
|
||||
);
|
||||
_selectedDateTime = pickedDate;
|
||||
if (_selectedDateTime != null) {
|
||||
dateController.text = _selectedDateTime!.toIso8601String().substring(
|
||||
0,
|
||||
10,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Methode zum Abrufen des Standorts.
|
||||
Future<void> fetchCurrentLocation() async {
|
||||
isLoading.value = true;
|
||||
errorMessage.value = null;
|
||||
try {
|
||||
final Position position = await _locationRepository.getCurrentPosition();
|
||||
currentPosition.value = position;
|
||||
final double latitude = position.latitude;
|
||||
final double longitude = position.longitude;
|
||||
// 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);
|
||||
// Print Standortinformationen in der Konsole
|
||||
print('Nearby Location: ${rxOrtString.value}');
|
||||
print('Current Position: Latitude: $latitude, Longitude: $longitude');
|
||||
} catch (e) {
|
||||
// Hier fängst du die Fehler aus dem Repository auf
|
||||
errorMessage.value = e.toString();
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void clearTextEditingController() {
|
||||
formKeyTank.currentState!.reset();
|
||||
// Den Fokus wieder auf das erste Feld legen
|
||||
FocusScope.of(Get.context!).requestFocus(firstFocusNode);
|
||||
// TextEditingController zurücksetzen
|
||||
_selectedDateTime = null; // Datum zurücksetzen
|
||||
dateController.clear();
|
||||
kilometerStandEdittingController.clear();
|
||||
mengeController.clear();
|
||||
pricePerLiterController.clear();
|
||||
}
|
||||
|
||||
String? validateKilometerStand(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Bitte Kilometerstand eingeben';
|
||||
}
|
||||
if (!GetUtils.isNum(value)) {
|
||||
return 'Bitte geben Sie eine gültige Zahl ein';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? validateMenge(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Bitte Menge eingeben';
|
||||
}
|
||||
if (!GetUtils.isNum(value)) {
|
||||
return 'Bitte geben Sie eine gültige Zahl ein';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? validatePricePerLiter(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Bitte Preis pro Liter eingeben';
|
||||
}
|
||||
if (!GetUtils.isNum(value)) {
|
||||
return 'Bitte geben Sie eine gültige Zahl ein';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//Go to Login Page
|
||||
void logoutSessionAndGoToLoginPage() async {
|
||||
// Handle logout logic here
|
||||
print('Logout session and go to login page');
|
||||
// Clear GetStorage session ID
|
||||
StaticHelper.removeFromStorage();
|
||||
print('Session ID removed from GetStorage');
|
||||
await _authRepository.logout();
|
||||
Get.offAndToNamed(LoginPage.namedRoute);
|
||||
StaticHelper.getMySnackeBar(
|
||||
'Logout', 'Sie wurden abgemeldet!', Colors.blue);
|
||||
}
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
if (_dataBox.hasData('sessinId')) {
|
||||
rxSessionIdString(_dataBox.read('sessinId').toString());
|
||||
}
|
||||
// Rufe den Standort direkt beim Initialisieren des Controllers ab, falls gewünscht.
|
||||
fetchCurrentLocation();
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() async {
|
||||
super.onReady();
|
||||
await getCurrentLoggedinUser();
|
||||
FocusScope.of(Get.context!).requestFocus(firstFocusNode);
|
||||
print('TankController is ready');
|
||||
}
|
||||
|
||||
Future<void> getCurrentLoggedinUser() async {
|
||||
await _authRepository.getCurrentUser.then((models.User user) {
|
||||
// Hier kannst du den Benutzernamen und das Avatar-Zeichen setzen
|
||||
userNameToDisplay.value = user.name;
|
||||
circleAvatarUserChar.value = user.name.substring(0, 1).toUpperCase();
|
||||
}).catchError((error) {
|
||||
print('Fehler beim Abrufen des Benutzers: $error');
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
dateController.dispose();
|
||||
kilometerStandEdittingController.dispose();
|
||||
mengeController.dispose();
|
||||
pricePerLiterController.dispose();
|
||||
firstFocusNode.dispose(); // Dispose den FocusNode
|
||||
}
|
||||
|
||||
void updateSumPrice() {
|
||||
final double menge = double.tryParse(mengeController.text) ?? 0.0;
|
||||
final double pricePerLiter =
|
||||
double.tryParse(pricePerLiterController.text) ?? 0.0;
|
||||
final double sumPrice = menge * pricePerLiter;
|
||||
rxSummePreisString.value = sumPrice.toStringAsFixed(
|
||||
2,
|
||||
); // Formatieren auf 2 Dezimalstellen
|
||||
}
|
||||
|
||||
void saveTankstopp() async {
|
||||
bool isErrorBySaving = false;
|
||||
String messageToUser = '';
|
||||
isFormValid = formKeyTank.currentState!.validate();
|
||||
if (!isFormValid) {
|
||||
print('Formular ist ungültig');
|
||||
messageToUser = 'Bitte überprüfen Sie Ihre Eingaben.';
|
||||
Get.snackbar(
|
||||
'Fehler',
|
||||
messageToUser,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
print('Formular ist gültig');
|
||||
formKeyTank.currentState!.save();
|
||||
try {
|
||||
//creatiung a tank stop
|
||||
await _authRepository.createTankStop({
|
||||
'userId': _dataBox.read('userId'),
|
||||
'date': f.format(DateTime.parse(dateController.text)),
|
||||
'odometer': double.parse(
|
||||
double.parse(
|
||||
kilometerStandEdittingController.text,
|
||||
).toStringAsFixed(0),
|
||||
),
|
||||
'liters': double.parse(
|
||||
double.parse(mengeController.text).toStringAsFixed(2),
|
||||
),
|
||||
'pricePerLiter': double.parse(
|
||||
double.parse(pricePerLiterController.text).toStringAsFixed(2),
|
||||
),
|
||||
'location': rxOrtString.value,
|
||||
}).then((models.Document document) {
|
||||
print('Tankstopp erfolgreich gespeichert: ${document.data}');
|
||||
messageToUser = 'Tankstopp erfolgreich gespeichert!';
|
||||
isErrorBySaving = false;
|
||||
})
|
||||
// Handle specific Appwrite exceptions
|
||||
.catchError((error) {
|
||||
isErrorBySaving = true;
|
||||
if (error is AppwriteException) {
|
||||
// Handle specific Appwrite exceptions
|
||||
messageToUser = 'Appwrite Fehler: ${error.message}';
|
||||
print('Appwrite Fehler: ${error.message}');
|
||||
} else {
|
||||
// Handle other types of errors
|
||||
messageToUser = 'Allgemeiner Fehler: $error';
|
||||
print('Allgemeiner Fehler: $error');
|
||||
}
|
||||
print('Fehler bei der speicherung: $error');
|
||||
});
|
||||
}
|
||||
// Handle any other exceptions that might occur
|
||||
catch (e) {
|
||||
isErrorBySaving = true;
|
||||
messageToUser = 'Fehler beim Speichern des Tankstopps: $e';
|
||||
print('Fehler beim speichern: $e');
|
||||
}
|
||||
if (!isErrorBySaving) {
|
||||
clearTextEditingController();
|
||||
}
|
||||
// Handle button press logic here
|
||||
String title = isErrorBySaving ? 'Fehler' : 'Erfolg';
|
||||
Get.snackbar(
|
||||
title,
|
||||
messageToUser,
|
||||
backgroundColor: isErrorBySaving ? Colors.red : Colors.green,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
duration: const Duration(seconds: 4),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
goToTankStopsView() async {
|
||||
clearTextEditingController();
|
||||
//await Get.offAndToNamed(TanklistPage.namedRoute);
|
||||
}
|
||||
}
|
||||
277
lib/pages/tank/tank_view.dart
Normal file
277
lib/pages/tank/tank_view.dart
Normal file
@@ -0,0 +1,277 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import './tank_controller.dart';
|
||||
|
||||
class TankPage extends GetView<TankController> {
|
||||
static const namedRoute = '/tank-stop-page';
|
||||
const TankPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
'Tankstopp erfassen',
|
||||
style: TextStyle(color: Colors.grey.shade300),
|
||||
),
|
||||
backgroundColor: Colors.transparent, // Mach die AppBar transparent
|
||||
elevation: 0, // Entferne den Schatten unter der AppBar
|
||||
centerTitle: true,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.list, color: Colors.grey.shade300),
|
||||
onPressed: () async {
|
||||
// Handle logout logic here
|
||||
controller.goToTankStopsView();
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.logout, color: Colors.grey.shade300),
|
||||
onPressed: () async {
|
||||
// Handle logout logic here
|
||||
controller.logoutSessionAndGoToLoginPage();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
extendBodyBehindAppBar: true, // Erweitere den Body hinter die AppBar
|
||||
body: Stack(
|
||||
children: [
|
||||
Container(
|
||||
height: double.infinity,
|
||||
width: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('lib/images/backgroundPitstopBlack.png'),
|
||||
fit: BoxFit
|
||||
.cover, // Skaliert das Bild, um den Container zu füllen
|
||||
alignment: Alignment
|
||||
.bottomCenter, // Richte das Bild am unteren Rand aus
|
||||
),
|
||||
),
|
||||
),
|
||||
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>[
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget inputFields() {
|
||||
return SizedBox(
|
||||
width: 320,
|
||||
child: Form(
|
||||
key: controller.formKeyTank,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
child: Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
focusNode: controller.firstFocusNode, // Setze den FocusNode
|
||||
readOnly: true,
|
||||
onTap: () => controller.selectDateTime(Get.context!),
|
||||
controller: controller.dateController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Datum',
|
||||
labelStyle: const TextStyle(color: Colors.white, fontSize: 20),
|
||||
errorStyle: const TextStyle(color: Colors.red, fontSize: 16),
|
||||
filled: true,
|
||||
fillColor: Colors.black.withValues(alpha: 0.9),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: Colors.white, width: 2),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide.none, // Entferne den Rand
|
||||
),
|
||||
),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
TextFormField(
|
||||
validator: (value) => controller.validateKilometerStand(value),
|
||||
keyboardType: TextInputType.number,
|
||||
controller: controller.kilometerStandEdittingController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Kilometerstand',
|
||||
labelStyle: const TextStyle(color: Colors.white, fontSize: 20),
|
||||
errorStyle: const TextStyle(color: Colors.red, fontSize: 16),
|
||||
filled: true,
|
||||
fillColor: Colors.black.withValues(alpha: 0.9),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: Colors.white, width: 2),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide.none, // Entferne den Rand
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
TextFormField(
|
||||
validator: (value) => controller.validateMenge(value),
|
||||
keyboardType: TextInputType.number,
|
||||
controller: controller.mengeController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Menge (in Litern)',
|
||||
labelStyle: const TextStyle(color: Colors.white, fontSize: 20),
|
||||
errorStyle: const TextStyle(color: Colors.red, fontSize: 16),
|
||||
filled: true,
|
||||
fillColor: Colors.black.withValues(alpha: 0.9),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: Colors.white, width: 2),
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide.none, // Entferne den Rand
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
TextFormField(
|
||||
onChanged: (value) {
|
||||
// Update the sum price when the price per liter changes
|
||||
controller.updateSumPrice();
|
||||
},
|
||||
validator: (value) => controller.validatePricePerLiter(value),
|
||||
keyboardType: TextInputType.number,
|
||||
controller: controller.pricePerLiterController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Preis pro Liter',
|
||||
labelStyle: const TextStyle(color: Colors.white, fontSize: 20),
|
||||
errorStyle: const TextStyle(color: Colors.red, fontSize: 16),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: Colors.white, width: 2),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: Colors.black.withValues(alpha: 0.9),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
//borderSide: BorderSide.none, // Entferne den Rand
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user