import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:fforte/addCam/cam_widgets.dart'; import 'package:fforte/db_helper.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:geolocator/geolocator.dart'; import 'package:intl/intl.dart'; import 'package:latlong2/latlong.dart'; import 'package:animations/animations.dart'; import 'package:dio/dio.dart'; import 'package:shared_preferences/shared_preferences.dart'; class AddCamMain extends StatefulWidget { final bool isTemplate; final Map? templateData; const AddCamMain({super.key, this.isTemplate = false, this.templateData}); @override State createState() => _AddCamMainState(); } class _AddCamMainState extends State { // var declaration int currentStep = 0; bool isTemplate = false; TextEditingController id = TextEditingController(); TextEditingController rudelC = TextEditingController(); TextEditingController addresse1C = TextEditingController(); TextEditingController addresse2C = TextEditingController(); TextEditingController addresse3C = TextEditingController(); TextEditingController bLandC = TextEditingController(); TextEditingController lkrC = TextEditingController(); TextEditingController beiOrtC = TextEditingController(); TextEditingController ortInfoC = TextEditingController(); TextEditingController ffTypC = TextEditingController(); TextEditingController kSchloNrC = TextEditingController(); TextEditingController auftragC = TextEditingController(); TextEditingController absprachenC = TextEditingController(); TextEditingController sonstBemerkungenC = TextEditingController(); TextEditingController fKontakt1C = TextEditingController(); TextEditingController fKontakt2C = TextEditingController(); TextEditingController fKontakt3C = TextEditingController(); TextEditingController altStOrtC = TextEditingController(); TextEditingController kTage1C = TextEditingController(); TextEditingController kTage2C = TextEditingController(); TextEditingController intKommC = TextEditingController(); TextEditingController betreuungC = TextEditingController(); String selectedStatus = 'aktiv'; String selectedSTTyp = 'opportunistisch'; String selectedFotoFilm = 'Foto'; String selectedMEZ = 'Sommerzeit'; String selectedPlatzung = 'Kirrung'; String selectedBearsafe = 'Nein'; Position currentPosition = Position( longitude: 10.0, latitude: 51.0, timestamp: DateTime.now(), accuracy: 0.0, altitude: 0.0, heading: 0.0, speed: 0.0, speedAccuracy: 0.0, altitudeAccuracy: 0.0, headingAccuracy: 0.0); String datumS = DateFormat('yyyy-MM-dd').format(DateTime.now()); String kontDatS = DateFormat('yyyy-MM-dd').format(DateTime.now()); String abbauDatS = DateFormat('yyyy-MM-dd').format(DateTime.now()); String ausVonS = DateFormat('yyyy-MM-dd').format(DateTime(2000)); String ausBisS = DateFormat('yyyy-MM-dd').format(DateTime(2000)); String protoAmS = DateFormat('yyyy-MM-dd').format(DateTime.now()); DateTime? ausBis; DateTime? ausVon; DateTime abbauDat = DateTime.now(); DateTime datum = DateTime.now(); DateTime kontDat = DateTime.now(); DateTime protoAm = DateTime.now(); Map getPlace() { Map place = { 'CID': id.text, 'Rudel': rudelC.text, 'Datum': datumS, 'Adresse1': addresse1C.text, 'Adresse2': addresse2C.text, 'Adresse3': addresse3C.text, 'BLand': bLandC.text, 'Lkr': lkrC.text, 'BeiOrt': beiOrtC.text, 'OrtInfo': ortInfoC.text, 'Status': selectedStatus, 'STTyp': selectedSTTyp, 'FFTyp': ffTypC.text, 'FotoFilm': selectedFotoFilm, 'MEZ': selectedMEZ, 'Platzung': selectedPlatzung, 'KSchloNr': kSchloNrC.text, 'Bearsafe': selectedBearsafe, 'KontDat': kontDatS, 'AbbauDat': abbauDatS, 'Auftrag': auftragC.text, 'KontAbsp': absprachenC.text, 'SonstBem': sonstBemerkungenC.text, 'FKontakt1': fKontakt1C.text, 'FKontakt2': fKontakt2C.text, 'FKontakt3': fKontakt3C.text, 'Standort': altStOrtC.text, 'AusVon': ausVonS, 'AusBis': ausBisS, 'KTage1': kTage1C.text, 'KTage2': kTage2C.text, 'ProtoAm': protoAmS, 'IntKomm': intKommC.text, 'Betreuung': betreuungC.text, 'DECLNG': currentPosition.longitude, 'DECLAT': currentPosition.latitude, }; return place; } bool empty = false; // determine live position with checks for denied permission and turned off location service Future _deteterminePosition() async { bool locationEnabled; LocationPermission permissionGiven; locationEnabled = await Geolocator.isLocationServiceEnabled(); if (!locationEnabled) { // ignore: use_build_context_synchronously ScaffoldMessenger.of(context).showSnackBar(SnackBar( // ignore: use_build_context_synchronously content: Text(AppLocalizations.of(context)!.locationDisabled))); return currentPosition; } permissionGiven = await Geolocator.checkPermission(); if (permissionGiven == LocationPermission.denied) { permissionGiven = await Geolocator.requestPermission(); if (permissionGiven == LocationPermission.denied) { // ignore: use_build_context_synchronously ScaffoldMessenger.of(context).showSnackBar(SnackBar( // ignore: use_build_context_synchronously content: Text(AppLocalizations.of(context)!.locationForbidden))); return currentPosition; } } if (permissionGiven == LocationPermission.deniedForever) { // ignore: use_build_context_synchronously ScaffoldMessenger.of(context).showSnackBar(SnackBar( // ignore: use_build_context_synchronously content: Text(AppLocalizations.of(context)!.locationForbidden))); return currentPosition; } return currentPosition = await Geolocator.getCurrentPosition(); } @override void initState() { super.initState(); // updates the currentPosition var after the _determine position has finished. Means user view updates with his live location _deteterminePosition().then((position) { setState(() { currentPosition = position; }); }); // If a template is edited this fills in the existing values if (widget.isTemplate && widget.templateData != null) { id.text = widget.templateData!['CID']; rudelC.text = widget.templateData!['Rudel']; addresse1C.text = widget.templateData!['Addresse1']; addresse2C.text = widget.templateData!['Addresse2']; addresse3C.text = widget.templateData!['Addresse3']; bLandC.text = widget.templateData!['BLand']; lkrC.text = widget.templateData!['Lkr']; beiOrtC.text = widget.templateData!['BeiOrt']; ortInfoC.text = widget.templateData!['OrtInfo']; selectedStatus = widget.templateData!['Status']; selectedSTTyp = widget.templateData!['STTyp']; ffTypC.text = widget.templateData!['FFTyp']; selectedFotoFilm = widget.templateData!['FotoFilm']; selectedMEZ = widget.templateData!['MEZ']; selectedPlatzung = widget.templateData!['Platzung']; kSchloNrC.text = widget.templateData!['KSchloNr']; selectedBearsafe = widget.templateData!['Bearsafe']; datumS = widget.templateData!['Datum']; kontDatS = widget.templateData!['KontDat']; abbauDatS = widget.templateData!['AbbauDat']; auftragC.text = widget.templateData!['Auftrag']; absprachenC.text = widget.templateData!['Absprachen']; sonstBemerkungenC.text = widget.templateData!['SonstBemerkungen']; fKontakt1C.text = widget.templateData!['FKontakt1']; fKontakt2C.text = widget.templateData!['FKontakt2']; fKontakt3C.text = widget.templateData!['FKontakt3']; altStOrtC.text = widget.templateData!['AltStOrt']; ausVonS = widget.templateData!['AusVon']; ausBisS = widget.templateData!['AusBis']; kTage1C.text = widget.templateData!['KTage1'].toString(); kTage2C.text = widget.templateData!['KTage2'].toString(); protoAmS = widget.templateData!['ProtoAm']; intKommC.text = widget.templateData!['IntKomm']; betreuungC.text = widget.templateData!['Betreuung']; } } // Function to show the dialog where the user has to choose if he want to safe his values as a template Future showTemplateDialog(String emptyField) async { return showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: Text(AppLocalizations.of(context)!.fieldEmpty), content: SingleChildScrollView( child: ListBody(children: [Text(emptyField)]), ), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); }, child: Text(AppLocalizations.of(context)!.cancel)), TextButton( onPressed: () { saveTemplate(); Navigator.pushNamedAndRemoveUntil( context, '/home', (route) => false); }, child: Text(AppLocalizations.of(context)!.template)) ], ); }); } Future showSaveOptionsDialog() async { return showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text(AppLocalizations.of(context)!.savemethod), actions: [ TextButton( onPressed: () async { _httpRequest(); // Navigator.pushNamedAndRemoveUntil(context, '/home', (route) => false); }, child: Text(AppLocalizations.of(context)!.sendtoserver)), TextButton( onPressed: () async { await saveFile(); // ignore: use_build_context_synchronously Navigator.pushNamedAndRemoveUntil( context, '/home', (route) => false); }, child: Text(AppLocalizations.of(context)!.saveasfile)) ], ); }); } void _httpRequest() async { Map place = getPlace(); /* final response = await http.post( Uri.parse('http://192.168.1.106/www.dbb-wolf.de/data/_app24.php'), headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: '{"test": "test"}' ); */ print(jsonEncode(place)); final dio = Dio(); final SharedPreferences prefs = await SharedPreferences.getInstance(); dio.options.responseType = ResponseType.plain; Response response = Response(requestOptions: RequestOptions(path: ''), statusCode: 400); try { response = await dio.post(prefs.getString('apiAddress') ?? "", data: jsonEncode(place)); } on DioException catch (e) { if (e.response?.statusCode == 500) { print('-------------------------'); print('code 500'); return; } } if (response.statusCode == 201) { print(response.statusCode); // TODO Hackal setzen } else { print(response.statusCode); } } Future saveFile() async { String? selectedDirectory = await FilePicker.platform.getDirectoryPath(); Map place = getPlace(); String jsonPlace = jsonEncode(place); if (selectedDirectory == null) { return; } File file = File('$selectedDirectory/cam-${id.text}.txt'); await file.writeAsString(jsonPlace); // ignore: use_build_context_synchronously ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Datei gespeichert in $selectedDirectory'))); } // checks if required fields are not empty. If one is the name will be returned String validateData() { Map fields = { 'CID': id, 'Rudel': rudelC, 'Addresse1': addresse1C, 'BLand': bLandC, 'Lkr': lkrC, 'BeiOrt': beiOrtC, 'Status': TextEditingController(text: selectedStatus), 'STTyp': TextEditingController(text: selectedSTTyp), 'FFTyp': ffTypC, 'FotoFilm': TextEditingController(text: selectedFotoFilm), 'MEZ': TextEditingController(text: selectedMEZ), 'Platzung': TextEditingController(text: selectedPlatzung), 'KTage1': kTage1C, 'KTage2': kTage2C, 'Standort': altStOrtC, }; for (var entry in fields.entries) { if (entry.value.text.isEmpty) { empty = true; return entry.key; } else { empty = false; } } return ""; } // If the user decides to safe his values as a template this function is called to save the values in the database // If the user already edits a template this template will be upadted otherwise a new one will be created void saveTemplate() async { var placeDB = DBHelper(); Map templates = getPlace(); if (widget.isTemplate) { await placeDB.updateTemplate(templates); } else { await placeDB.addTemplate(templates); } } // If the user has filled all needed values this function will be called to safe them in the database // * also creates a json string to send it to the server later void saveData() async { var placeDB = DBHelper(); Map place = getPlace(); await placeDB.addPlace(place); } // The widget tree which gets the shown widget from the ./cam_widgets.dart file // The names of the widgets should be self-explaining @override Widget build(BuildContext context) { // List with the steps. The steps itself will be "shown" later List getSteps() => [ Step( title: Text(AppLocalizations.of(context)!.firstStep), content: Column( children: [ Align( alignment: Alignment.bottomLeft, child: AltStOrt(altStOrtC: altStOrtC), ), const SizedBox( height: 1, ), STTyp( initialSTTyp: selectedSTTyp, onSTTypChanged: (sttyp) { setState(() { selectedSTTyp = sttyp; }); }, ), const SizedBox( height: 5, ), Align( alignment: Alignment.bottomLeft, child: Row( children: [ Text(AppLocalizations.of(context)!.status), const Text( '*', style: TextStyle(color: Colors.red), ), ], )), Status( initialStatus: selectedStatus, onStatusChanged: (status) { setState(() { selectedStatus = status; }); }, ), Betreuung(betreuungC: betreuungC), const SizedBox( height: 20, ), CamId(id: id), Align( alignment: Alignment.bottomLeft, child: FFTyp(ffTypC: ffTypC), ), const SizedBox( height: 15, ), Align( alignment: Alignment.bottomLeft, child: Row( children: [ Text(AppLocalizations.of(context)!.zeiteinstellung), const Text( '*', style: TextStyle(color: Colors.red), ) ], )), MEZ( initialMEZ: selectedMEZ, onMEZChanged: (mez) { setState(() { selectedMEZ = mez; }); }, ), Align( alignment: Alignment.bottomLeft, child: Row( children: [ Text(AppLocalizations.of(context)!.bearsafe), ], )), Bearsafe( initialBearsafe: selectedBearsafe, onBearsafeChanged: (bearsafe) { setState(() { selectedBearsafe = bearsafe; }); }, ), const SizedBox( height: 15, ), Align( alignment: Alignment.bottomLeft, child: KSchloNr(kSchloNrC: kSchloNrC), ), const SizedBox( height: 5, ), Rudel(rudelC: rudelC), const SizedBox( height: 15, ), ], )), // Second step (location) Step( title: Text(AppLocalizations.of(context)!.locations), content: Column( children: [ Align( alignment: Alignment.bottomLeft, child: Text(currentPosition.latitude.toString())), Align( alignment: Alignment.bottomLeft, child: Text(currentPosition.longitude.toString())), Align( alignment: Alignment.bottomLeft, child: ElevatedButton( onPressed: () async { final result = await Navigator.of(context) .push( MaterialPageRoute(builder: (context) { return Standort( currentPosition: currentPosition, onPositionChange: (updatedPosition) { setState(() { currentPosition = updatedPosition; }); }, ); })); if (result != null) { setState(() { currentPosition = Position( latitude: result.latitude, longitude: result.longitude, timestamp: DateTime.now(), accuracy: 0.0, altitude: 0.0, altitudeAccuracy: 0.0, heading: 0.0, headingAccuracy: 0.0, speed: 0.0, speedAccuracy: 0.0, ); }); } }, child: Text(AppLocalizations.of(context)!.openMap)), ), Align( alignment: Alignment.bottomLeft, child: BLand(bLandC: bLandC), ), Align( alignment: Alignment.bottomLeft, child: Lkr(lkrC: lkrC), ), Align( alignment: Alignment.bottomLeft, child: BeiOrt(beiOrtC: beiOrtC), ), Align( alignment: Alignment.bottomLeft, child: OrtInfo(ortInfoC: ortInfoC), ), const SizedBox( height: 15, ), Align( alignment: Alignment.bottomLeft, child: Row( children: [ Text(AppLocalizations.of(context)!.platzung), const Text( '*', style: TextStyle(color: Colors.red), ) ], )), Platzung( initialPlatzung: selectedPlatzung, onPlatzungChanged: (platzung) { setState(() { selectedPlatzung = platzung; }); }, ), ], )), // Date Step Step( title: Text(AppLocalizations.of(context)!.date), content: Column( children: [ Datum( datum: datum, ), KontDat(kontDat: kontDat), const SizedBox( height: 20, ), Align( alignment: Alignment.bottomLeft, child: Row( children: [ Text(AppLocalizations.of(context)!.ktage1), const Text( '*', style: TextStyle(color: Colors.red), ), ], ), ), KTage1(kTage1C: kTage1C), const SizedBox( height: 20, ), Align( alignment: Alignment.bottomLeft, child: Row( children: [ Text(AppLocalizations.of(context)!.ktage2), const Text( '*', style: TextStyle(color: Colors.red), ) ], )), KTage2(kTage2C: kTage2C), const SizedBox( height: 20, ), AbbauDat(abbauDat: abbauDat), AusVon(ausVon: ausVon), AusBis(ausBis: ausBis), const SizedBox( height: 20, ), Align( alignment: Alignment.bottomLeft, child: Auftrag(auftragC: auftragC), ), Align( alignment: Alignment.bottomLeft, child: Absprachen(absprachenC: absprachenC), ), Align( alignment: Alignment.bottomLeft, child: SonstBemerkungen(sonstBemerkungenC: sonstBemerkungenC), ), ], )), Step( title: Text(AppLocalizations.of(context)!.kontakt), content: Column( children: [ const SizedBox( height: 15, ), Align( alignment: Alignment.bottomLeft, child: Addresse1(addresse1C: addresse1C)), Align( alignment: Alignment.bottomLeft, child: Addresse2(addresse2C: addresse2C), ), Align( alignment: Alignment.bottomLeft, child: Addresse3(addresse3C: addresse3C), ), const SizedBox( height: 15, ), Align( alignment: Alignment.bottomLeft, child: FKontakt1(fKontakt1C: fKontakt1C), ), Align( alignment: Alignment.bottomLeft, child: FKontakt2(fKontakt2C: fKontakt2C), ), Align( alignment: Alignment.bottomLeft, child: FKontakt3(fKontakt3C: fKontakt3C), ), Align( alignment: Alignment.bottomLeft, child: IntKomm(intKommC: intKommC), ), ], )) ]; // Here the site is built inclusive the steps from above return Scaffold( appBar: AppBar(title: Text(AppLocalizations.of(context)!.addplace)), body: PageTransitionSwitcher( duration: const Duration(milliseconds: 800), transitionBuilder: (Widget child, Animation animation, Animation secondaryAnimation) { return SharedAxisTransition( animation: animation, secondaryAnimation: secondaryAnimation, transitionType: SharedAxisTransitionType.vertical, child: child, ); }, child: Stepper( key: ValueKey(currentStep), type: StepperType.vertical, steps: getSteps(), // Functions that handle the navigation through the steps currentStep: currentStep, onStepTapped: (value) { setState(() { currentStep = value; }); }, onStepContinue: () async { final isLastStep = currentStep == getSteps().length - 1; if (!isLastStep) { // ! saveTemplate(); setState(() { currentStep += 1; }); } else { String emptyField = validateData(); // ! always fileed out empty = false; if (empty == true) { showTemplateDialog(emptyField); (); return; } else if (empty == false) { // throw const FormatException(("Speichert")); saveData(); await showSaveOptionsDialog(); Navigator.pushNamedAndRemoveUntil( // ignore: use_build_context_synchronously context, '/home', (route) => false); } } }, onStepCancel: () { if (currentStep == 0) { Navigator.pop(context); } else { setState(() { currentStep -= 1; }); } }))); } }