change file structure
This commit is contained in:
@@ -1,892 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:fforte/addCam/cam_widgets.dart';
|
||||
import 'package:fforte/other/db_helper.dart';
|
||||
import 'package:fforte/methods/http_request.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:latlong2/latlong.dart';
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class AddCamMain extends StatefulWidget {
|
||||
final bool isTemplate;
|
||||
final bool isFinished;
|
||||
final bool isSent;
|
||||
final Map<String, dynamic>? existingData;
|
||||
|
||||
const AddCamMain(
|
||||
{super.key,
|
||||
this.isTemplate = false,
|
||||
this.existingData,
|
||||
this.isFinished = false,
|
||||
this.isSent = false});
|
||||
|
||||
@override
|
||||
State<AddCamMain> createState() => _AddCamMainState();
|
||||
}
|
||||
|
||||
class _AddCamMainState extends State<AddCamMain> {
|
||||
// var declaration
|
||||
int currentStep = 0;
|
||||
// bool isTemplate = false;
|
||||
|
||||
TextEditingController cid = TextEditingController();
|
||||
TextEditingController rudelC = TextEditingController();
|
||||
TextEditingController adresse1C = TextEditingController();
|
||||
TextEditingController adresse2C = TextEditingController();
|
||||
TextEditingController adresse3C = TextEditingController();
|
||||
TextEditingController bLandC = TextEditingController();
|
||||
TextEditingController lkrC = TextEditingController();
|
||||
TextEditingController beiOrtC = TextEditingController();
|
||||
TextEditingController ortInfoC = TextEditingController();
|
||||
TextEditingController ffTypC = TextEditingController();
|
||||
TextEditingController kSchloNrC = TextEditingController();
|
||||
TextEditingController auftragC = TextEditingController();
|
||||
TextEditingController kontAbspC = TextEditingController();
|
||||
TextEditingController sonstBemC = TextEditingController();
|
||||
TextEditingController fKontakt1C = TextEditingController();
|
||||
TextEditingController fKontakt2C = TextEditingController();
|
||||
TextEditingController fKontakt3C = TextEditingController();
|
||||
TextEditingController standortC = TextEditingController();
|
||||
TextEditingController kTage1C = TextEditingController();
|
||||
TextEditingController kTage2C = TextEditingController();
|
||||
TextEditingController intKommC = TextEditingController();
|
||||
TextEditingController betreuungC = TextEditingController();
|
||||
|
||||
String selectedStatus = 'aktiv';
|
||||
String selectedFotoFilm = 'Foto';
|
||||
String selectedMEZ = 'Sommerzeit';
|
||||
String selectedPlatzung = '';
|
||||
|
||||
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);
|
||||
|
||||
DateTime? abbauDat;
|
||||
DateTime datum = DateTime.now();
|
||||
DateTime? kontDat = DateTime.now();
|
||||
DateTime? protoAm = DateTime.now();
|
||||
|
||||
Map<String, dynamic> getPlace() {
|
||||
Map<String, dynamic> place = {
|
||||
'ID': widget.existingData?['ID'],
|
||||
'CID': cid.text,
|
||||
'Rudel': rudelC.text,
|
||||
'Datum': datum.toString().split(" ").first,
|
||||
'Adresse1': adresse1C.text,
|
||||
'Adresse2': adresse2C.text,
|
||||
'Adresse3': adresse3C.text,
|
||||
'BLand': bLandC.text,
|
||||
'Lkr': lkrC.text,
|
||||
'BeiOrt': beiOrtC.text,
|
||||
'OrtInfo': ortInfoC.text,
|
||||
'Status': selectedStatus,
|
||||
'FFTyp': ffTypC.text,
|
||||
'FotoFilm': selectedFotoFilm,
|
||||
'MEZ': selectedMEZ,
|
||||
'Platzung': selectedPlatzung,
|
||||
'KSchloNr': kSchloNrC.text,
|
||||
'KontDat': kontDat.toString().split(" ").first,
|
||||
'AbbauDat': abbauDat.toString().split(" ").first.replaceAll("null", ""),
|
||||
'Auftrag': auftragC.text,
|
||||
'KontAbsp': kontAbspC.text,
|
||||
'SonstBem': sonstBemC.text,
|
||||
'FKontakt1': fKontakt1C.text,
|
||||
'FKontakt2': fKontakt2C.text,
|
||||
'FKontakt3': fKontakt3C.text,
|
||||
'Standort': standortC.text,
|
||||
'KTage1': kTage1C.text,
|
||||
'KTage2': kTage2C.text,
|
||||
'ProtoAm': protoAm.toString().split(" ").first,
|
||||
'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<Position> _deteterminePosition() async {
|
||||
bool locationEnabled;
|
||||
LocationPermission permissionGiven;
|
||||
|
||||
locationEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!locationEnabled && mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.locationDisabled)));
|
||||
return currentPosition;
|
||||
}
|
||||
|
||||
permissionGiven = await Geolocator.checkPermission();
|
||||
if (permissionGiven == LocationPermission.denied) {
|
||||
permissionGiven = await Geolocator.requestPermission();
|
||||
if (permissionGiven == LocationPermission.denied && mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.locationForbidden)));
|
||||
|
||||
return currentPosition;
|
||||
}
|
||||
}
|
||||
|
||||
if (permissionGiven == LocationPermission.deniedForever && mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
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.isFinished && widget.existingData != null) {
|
||||
cid.text = widget.existingData!['CID'] ?? "";
|
||||
rudelC.text = widget.existingData!['Rudel'] ?? "";
|
||||
adresse1C.text = widget.existingData!['Adresse1'] ?? "";
|
||||
adresse2C.text = widget.existingData!['Adresse2'] ?? "";
|
||||
adresse3C.text = widget.existingData!['Adresse3'] ?? "";
|
||||
bLandC.text = widget.existingData!['BLand'] ?? "";
|
||||
lkrC.text = widget.existingData!['Lkr'] ?? "";
|
||||
beiOrtC.text = widget.existingData!['BeiOrt'] ?? "";
|
||||
ortInfoC.text = widget.existingData!['OrtInfo'] ?? "";
|
||||
selectedStatus = widget.existingData!['Status'] ?? "";
|
||||
ffTypC.text = widget.existingData!['FFTyp'] ?? "";
|
||||
selectedFotoFilm = widget.existingData!['FotoFilm'] ?? "";
|
||||
selectedMEZ = widget.existingData!['MEZ'] ?? "";
|
||||
selectedPlatzung = widget.existingData!['Platzung'] ?? "";
|
||||
kSchloNrC.text = widget.existingData!['KSchloNr'] ?? "";
|
||||
datum = DateTime.parse(widget.existingData!['Datum']);
|
||||
kontDat = widget.existingData!['KontDat'] == ""
|
||||
? null
|
||||
: DateTime.parse(widget.existingData!['KontDat']);
|
||||
abbauDat = widget.existingData!['AbbauDat'] == ""
|
||||
? null
|
||||
: DateTime.parse(widget.existingData!['AbbauDat']);
|
||||
auftragC.text = widget.existingData!['Auftrag'] ?? "";
|
||||
kontAbspC.text = widget.existingData!['KontAbsp'] ?? "";
|
||||
sonstBemC.text = widget.existingData!['SonstBem'] ?? "";
|
||||
fKontakt1C.text = widget.existingData!['FKontakt1'] ?? "";
|
||||
fKontakt2C.text = widget.existingData!['FKontakt2'] ?? "";
|
||||
fKontakt3C.text = widget.existingData!['FKontakt3'] ?? "";
|
||||
standortC.text = widget.existingData!['Standort'] ?? "";
|
||||
kTage1C.text = widget.existingData!['KTage1'].toString();
|
||||
kTage2C.text = widget.existingData!['KTage2'].toString();
|
||||
protoAm = widget.existingData!['ProtoAm'] == null
|
||||
? null
|
||||
: DateTime.parse(widget.existingData!['ProtoAm']);
|
||||
intKommC.text = widget.existingData!['IntKomm'] ?? "";
|
||||
betreuungC.text = widget.existingData!['Betreuung'] ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
// Function to show the dialog where the user has to choose if he want to safe his values as a template
|
||||
Future<void> showTemplateDialog(List<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: <Widget>[Text(emptyField.join("; "))]),
|
||||
),
|
||||
actions: <Widget>[
|
||||
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<dynamic> _showServerErrorDialog() {
|
||||
bool isLoading = false;
|
||||
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) {
|
||||
return AlertDialog(
|
||||
title: Text(AppLocalizations.of(context)!.servererrortitle),
|
||||
content: isLoading
|
||||
? const SizedBox(
|
||||
height: 100,
|
||||
child: Center(child: CircularProgressIndicator()))
|
||||
: null,
|
||||
actions: [
|
||||
if (!isLoading)
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
setState(() => isLoading = true);
|
||||
int errorCode = await _httpRequest();
|
||||
setState(() => isLoading = false);
|
||||
|
||||
if (errorCode != 201 && context.mounted) {
|
||||
_showServerErrorDialog();
|
||||
} else {
|
||||
if (context.mounted) Navigator.pop(context);
|
||||
saveData(true);
|
||||
_showSuccessDialog();
|
||||
}
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.sendagain)),
|
||||
if (!isLoading)
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.cancel))
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> showSaveOptionsDialog() async {
|
||||
bool isLoading = false;
|
||||
|
||||
return showDialog(
|
||||
context: context,
|
||||
barrierDismissible:
|
||||
false, // Verhindert das Schließen des Dialogs durch den Benutzer
|
||||
builder: (BuildContext context) {
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return AlertDialog(
|
||||
title: isLoading
|
||||
? Text(AppLocalizations.of(context)!.loading)
|
||||
: Text(AppLocalizations.of(context)!.savemethod),
|
||||
content: isLoading
|
||||
? const SizedBox(
|
||||
height: 100,
|
||||
child: Center(child: CircularProgressIndicator()))
|
||||
: null,
|
||||
actions: [
|
||||
if (!isLoading)
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
setState(() => isLoading = true);
|
||||
saveTemplate();
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
context, '/home', (route) => false);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.template)),
|
||||
if (!isLoading)
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
setState(() => isLoading = true);
|
||||
int errorCode = await _httpRequest();
|
||||
setState(() => isLoading = false);
|
||||
|
||||
if (errorCode != 201 || !context.mounted) {
|
||||
saveData();
|
||||
_showServerErrorDialog();
|
||||
} else {
|
||||
saveData(true);
|
||||
_showSuccessDialog();
|
||||
}
|
||||
},
|
||||
child:
|
||||
Text(AppLocalizations.of(context)!.sendtoserver)),
|
||||
if (!isLoading)
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
setState(() => isLoading = true);
|
||||
saveData();
|
||||
saveFile();
|
||||
setState(() => isLoading = false);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.saveasfile)),
|
||||
if (!isLoading)
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
saveData();
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
context, '/home', (route) => false);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.justsave)),
|
||||
if (!isLoading)
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.cancel)),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _showSuccessDialog() async {
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
title: Text(AppLocalizations.of(context)!.successful),
|
||||
actions: [
|
||||
/* TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.back)), */
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
context, '/home', (route) => false);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.continueB))
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<int> _httpRequest() async {
|
||||
Map<String, dynamic> place = getPlace();
|
||||
|
||||
HttpRequest method = HttpRequest();
|
||||
|
||||
await method.httpRequest(jsonEncode(place));
|
||||
|
||||
return method.errorCode;
|
||||
}
|
||||
|
||||
Future<void> saveFile() async {
|
||||
String? selectedDirectory = await FilePicker.platform.getDirectoryPath();
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
Map place = getPlace();
|
||||
String jsonPlace = jsonEncode(place);
|
||||
|
||||
if (selectedDirectory == null) {
|
||||
if (mounted) Navigator.pop(context);
|
||||
return;
|
||||
}
|
||||
await prefs.setString('saveDir', selectedDirectory);
|
||||
|
||||
File file = File(
|
||||
'$selectedDirectory/${mounted ? AppLocalizations.of(context)!.justplace : const Text('')}-${standortC.text}.txt');
|
||||
|
||||
try {
|
||||
await file.writeAsString(jsonPlace);
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
Navigator.pop(context);
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text(AppLocalizations.of(context)!.savefilefailed)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Datei gespeichert in $selectedDirectory')));
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
Navigator.pushNamedAndRemoveUntil(context, '/home', (route) => false);
|
||||
}
|
||||
}
|
||||
|
||||
// checks if required fields are not empty. If one is the name will be returned
|
||||
List<String> validateData() {
|
||||
List<String> emptyFields = <String>[];
|
||||
|
||||
Map<String, TextEditingController> fields = {
|
||||
AppLocalizations.of(context)!.camLink: cid,
|
||||
AppLocalizations.of(context)!.rudel: rudelC,
|
||||
AppLocalizations.of(context)!.adresse1: adresse1C,
|
||||
AppLocalizations.of(context)!.bland: bLandC,
|
||||
AppLocalizations.of(context)!.lkr: lkrC,
|
||||
AppLocalizations.of(context)!.beiort: beiOrtC,
|
||||
AppLocalizations.of(context)!.status: TextEditingController(text: selectedStatus),
|
||||
AppLocalizations.of(context)!.fftyp: ffTypC,
|
||||
"${AppLocalizations.of(context)!.foto} / ${AppLocalizations.of(context)!.filelocation}": TextEditingController(text: selectedFotoFilm),
|
||||
AppLocalizations.of(context)!.zeiteinstellung: TextEditingController(text: selectedMEZ),
|
||||
AppLocalizations.of(context)!.platzung: TextEditingController(text: selectedPlatzung),
|
||||
AppLocalizations.of(context)!.ktage1: kTage1C,
|
||||
AppLocalizations.of(context)!.ktage1: kTage2C,
|
||||
AppLocalizations.of(context)!.location: standortC,
|
||||
};
|
||||
|
||||
for (var entry in fields.entries) {
|
||||
if (entry.value.text.isEmpty) {
|
||||
emptyFields.add(entry.key);
|
||||
}
|
||||
}
|
||||
|
||||
empty = false;
|
||||
if (emptyFields.isNotEmpty) empty = true;
|
||||
|
||||
return emptyFields;
|
||||
}
|
||||
|
||||
// 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<String, dynamic> 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([bool sent = false]) async {
|
||||
var placeDB = DBHelper();
|
||||
Map<String, dynamic> place = getPlace();
|
||||
|
||||
// Get the ID of the newly added or updated place
|
||||
int newPlaceId = await placeDB.addPlace(place);
|
||||
|
||||
if (sent == true) {
|
||||
placeDB.updateSent(newPlaceId); // Update 'Sent' using the correct ID
|
||||
}
|
||||
|
||||
if (widget.isTemplate) {
|
||||
await placeDB.deleteTemplate(cid.text);
|
||||
}
|
||||
}
|
||||
|
||||
// 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<Step> getSteps() => [
|
||||
// First step
|
||||
Step(
|
||||
title: Text(AppLocalizations.of(context)!.firststep),
|
||||
content: Column(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.bottomLeft,
|
||||
child: VarTextField(
|
||||
required: true,
|
||||
dbName: "Standort",
|
||||
textController: standortC,
|
||||
localization: AppLocalizations.of(context)!.altstort,
|
||||
),
|
||||
),
|
||||
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;
|
||||
});
|
||||
},
|
||||
),
|
||||
VarTextField(
|
||||
textController: betreuungC,
|
||||
localization: AppLocalizations.of(context)!.betreuung,
|
||||
dbName: "Betreuung",
|
||||
required: false),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
VarTextField(
|
||||
textController: cid,
|
||||
localization: AppLocalizations.of(context)!.camLink,
|
||||
dbName: "CID",
|
||||
required: true),
|
||||
VarTextField(
|
||||
textController: ffTypC,
|
||||
localization: AppLocalizations.of(context)!.fftyp,
|
||||
dbName: "FFTyp",
|
||||
required: true),
|
||||
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;
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
VarTextField(
|
||||
textController: kSchloNrC,
|
||||
localization: AppLocalizations.of(context)!.kschlonr,
|
||||
dbName: "KSchloNr",
|
||||
required: false),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
VarTextField(
|
||||
textController: rudelC,
|
||||
localization: AppLocalizations.of(context)!.rudel,
|
||||
dbName: "Rudel",
|
||||
required: true),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
],
|
||||
)),
|
||||
|
||||
// Second step
|
||||
Step(
|
||||
title: Text(AppLocalizations.of(context)!.secondstep),
|
||||
content: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Text(currentPosition.latitude.toString()),
|
||||
Text(currentPosition.longitude.toString()),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
width: 15,
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
final result = await Navigator.of(context)
|
||||
.push<LatLng>(
|
||||
MaterialPageRoute(builder: (context) {
|
||||
return Karte(
|
||||
ortInfoC: ortInfoC,
|
||||
beiOrtC: beiOrtC,
|
||||
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)),
|
||||
],
|
||||
),
|
||||
VarTextField(
|
||||
textController: bLandC,
|
||||
localization: AppLocalizations.of(context)!.bland,
|
||||
dbName: "BLand",
|
||||
required: true,
|
||||
defaultValue: "bLand",
|
||||
),
|
||||
VarTextField(
|
||||
textController: lkrC,
|
||||
localization: AppLocalizations.of(context)!.lkr,
|
||||
dbName: "Lkr",
|
||||
required: true),
|
||||
VarTextField(
|
||||
textController: beiOrtC,
|
||||
localization: AppLocalizations.of(context)!.beiort,
|
||||
dbName: "BeiOrt",
|
||||
required: true),
|
||||
VarTextField(
|
||||
textController: ortInfoC,
|
||||
localization: AppLocalizations.of(context)!.ortinfo,
|
||||
dbName: "OrtInfo",
|
||||
required: false),
|
||||
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;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
)),
|
||||
// Third step
|
||||
Step(
|
||||
title: Text(AppLocalizations.of(context)!.thirdstep),
|
||||
content: Column(
|
||||
children: [
|
||||
Datum(
|
||||
initDatum: datum,
|
||||
onDateChanged: (value) {
|
||||
datum = value;
|
||||
},
|
||||
),
|
||||
KontDat(
|
||||
initKontDat: kontDat,
|
||||
onDateChanged: (value) {
|
||||
setState(() {
|
||||
kontDat = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomLeft,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(AppLocalizations.of(context)!.ktage),
|
||||
const Text(
|
||||
'*',
|
||||
style: TextStyle(color: Colors.red),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text( AppLocalizations.of(context)!.ktage1)),
|
||||
|
||||
const SizedBox(width: 15,),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: VarTextField(otherDefault: "24", textController: kTage1C, localization: AppLocalizations.of(context)!.ktage1, dbName: "KTage1", required: true))
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text( AppLocalizations.of(context)!.ktage2)),
|
||||
const SizedBox(width: 15,),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: VarTextField(otherDefault: "48", textController: kTage2C, localization: AppLocalizations.of(context)!.ktage2, dbName: "KTage2", required: true))
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
AbbauDat(
|
||||
initAbbauDat: abbauDat,
|
||||
onDateChanged: (value) {
|
||||
abbauDat = value;
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
VarTextField(
|
||||
textController: auftragC,
|
||||
localization: AppLocalizations.of(context)!.auftrag,
|
||||
dbName: "Auftrag",
|
||||
required: false),
|
||||
VarTextField(
|
||||
textController: kontAbspC,
|
||||
localization: AppLocalizations.of(context)!.kontabsp,
|
||||
dbName: "KontAbsp",
|
||||
required: false),
|
||||
VarTextField(
|
||||
textController: sonstBemC,
|
||||
localization:
|
||||
AppLocalizations.of(context)!.sonstbemerkungen,
|
||||
dbName: "SonstBem",
|
||||
required: false),
|
||||
],
|
||||
)),
|
||||
// Fourth step
|
||||
Step(
|
||||
title: Text(AppLocalizations.of(context)!.fourthstep),
|
||||
content: Column(
|
||||
children: [
|
||||
VarTextField(
|
||||
textController: adresse1C,
|
||||
localization: AppLocalizations.of(context)!.adresse1,
|
||||
dbName: "Adresse1",
|
||||
required: true,
|
||||
defaultValue: "addresse1",
|
||||
),
|
||||
VarTextField(
|
||||
textController: adresse2C,
|
||||
localization: AppLocalizations.of(context)!.adresse2,
|
||||
dbName: "Adresse2",
|
||||
required: false),
|
||||
VarTextField(
|
||||
textController: adresse3C,
|
||||
localization: AppLocalizations.of(context)!.adresse3,
|
||||
dbName: "Adresse2",
|
||||
required: false),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
VarTextField(
|
||||
textController: fKontakt1C,
|
||||
localization: AppLocalizations.of(context)!.fkontakt1,
|
||||
dbName: "FKontakt1",
|
||||
required: false),
|
||||
VarTextField(
|
||||
textController: fKontakt2C,
|
||||
localization: AppLocalizations.of(context)!.fkontakt2,
|
||||
dbName: "FKontakt2",
|
||||
required: false),
|
||||
VarTextField(
|
||||
textController: fKontakt3C,
|
||||
localization: AppLocalizations.of(context)!.fkontakt3,
|
||||
dbName: "FKontakt3",
|
||||
required: false),
|
||||
VarTextField(
|
||||
textController: intKommC,
|
||||
localization: AppLocalizations.of(context)!.intkomm,
|
||||
dbName: "IntKomm",
|
||||
required: false),
|
||||
],
|
||||
))
|
||||
];
|
||||
|
||||
// Here the site is built with 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<double> animation,
|
||||
Animation<double> secondaryAnimation) {
|
||||
return SharedAxisTransition(
|
||||
animation: animation,
|
||||
secondaryAnimation: secondaryAnimation,
|
||||
transitionType: SharedAxisTransitionType.vertical,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: Stepper(
|
||||
key: ValueKey<int>(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 {
|
||||
List<String> emptyFields = validateData();
|
||||
// ! always filled out
|
||||
// empty = false;
|
||||
if (widget.isSent) {
|
||||
Navigator.pushNamedAndRemoveUntil(context, '/home', (route) => false);
|
||||
} else if (empty == true) {
|
||||
showTemplateDialog(emptyFields);
|
||||
return;
|
||||
} else if (empty == false) {
|
||||
await showSaveOptionsDialog();
|
||||
}
|
||||
}
|
||||
},
|
||||
onStepCancel: () {
|
||||
if (currentStep == 0) {
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
setState(() {
|
||||
currentStep -= 1;
|
||||
});
|
||||
}
|
||||
})));
|
||||
}
|
||||
}
|
||||
@@ -1,878 +0,0 @@
|
||||
|
||||
import 'package:fforte/other/db_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:geocoding/geocoding.dart';
|
||||
|
||||
// * Collection of All widgets displayed in the add_cam section
|
||||
|
||||
class VarTextField extends StatefulWidget {
|
||||
final TextEditingController textController;
|
||||
final String localization;
|
||||
final String dbName;
|
||||
final String? defaultValue;
|
||||
final String? otherDefault;
|
||||
final bool required;
|
||||
|
||||
const VarTextField(
|
||||
{super.key,
|
||||
required this.textController,
|
||||
required this.localization,
|
||||
required this.dbName,
|
||||
required this.required,
|
||||
this.defaultValue,
|
||||
this.otherDefault});
|
||||
|
||||
@override
|
||||
State<VarTextField> createState() => _VarTextFieldState();
|
||||
}
|
||||
|
||||
class _VarTextFieldState extends State<VarTextField> {
|
||||
late Future<List<Map<String, dynamic>>> dbVar;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.textController.text == "" && widget.defaultValue != null) {
|
||||
_loadPref();
|
||||
}
|
||||
|
||||
if (widget.otherDefault != null) {
|
||||
widget.textController.text = widget.otherDefault!;
|
||||
}
|
||||
|
||||
dbVar = _loadData();
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>> _loadData() async {
|
||||
var places = await DBHelper().getPlace();
|
||||
var templates = await DBHelper().getTemplates();
|
||||
return [...places, ...templates];
|
||||
}
|
||||
|
||||
void _loadPref() {
|
||||
Future.delayed(Duration.zero, () async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
String bLand = prefs.getString(widget.defaultValue!) ?? "";
|
||||
setState(() {
|
||||
widget.textController.text = bLand;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 5,
|
||||
child: TextField(
|
||||
controller: widget.textController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
maxLines: null,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
widget.textController.text = value;
|
||||
});
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: widget.localization,
|
||||
enabledBorder: widget.required
|
||||
? (widget.textController.text.isEmpty
|
||||
? const UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.red))
|
||||
: const UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.green)))
|
||||
: const UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey)),
|
||||
focusedBorder: widget.required
|
||||
? (widget.textController.text.isEmpty
|
||||
? const UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.red))
|
||||
: const UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.green)))
|
||||
: const UnderlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey))),
|
||||
)),
|
||||
const Expanded(
|
||||
child: SizedBox(
|
||||
width: 15,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomLeft,
|
||||
child: FutureBuilder<List<Map<String, dynamic>>>(
|
||||
future: dbVar,
|
||||
builder: (BuildContext context,
|
||||
AsyncSnapshot<List<Map<String, dynamic>>> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
// Filtern der Daten, um sicherzustellen, dass keine 'null' Werte für den Schlüssel dbName vorhanden sind
|
||||
var filteredData = snapshot.data!
|
||||
.where((item) =>
|
||||
item[widget.dbName] != null &&
|
||||
item[widget.dbName] != "")
|
||||
.toList();
|
||||
|
||||
var uniqueData = { ...filteredData.map((e) => e[widget.dbName].toString())};
|
||||
|
||||
return PopupMenuButton<String>(
|
||||
onSelected: (String value) {
|
||||
setState(() {
|
||||
widget.textController.text = value;
|
||||
});
|
||||
},
|
||||
itemBuilder: (BuildContext context) {
|
||||
return uniqueData
|
||||
.map((value) => PopupMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
))
|
||||
.toList();
|
||||
},
|
||||
child: const Icon(Icons.arrow_drop_down),
|
||||
);
|
||||
} else if (snapshot.hasError) {
|
||||
return Text('Fehler: ${snapshot.error}');
|
||||
} else {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Karte
|
||||
// ! completely new page
|
||||
|
||||
class Karte extends StatefulWidget {
|
||||
final TextEditingController beiOrtC;
|
||||
final TextEditingController ortInfoC;
|
||||
final Position currentPosition;
|
||||
final Function(Position) onPositionChange;
|
||||
|
||||
const Karte(
|
||||
{super.key,
|
||||
required this.currentPosition,
|
||||
required this.onPositionChange,
|
||||
required this.beiOrtC,
|
||||
required this.ortInfoC});
|
||||
|
||||
@override
|
||||
KarteState createState() => KarteState();
|
||||
}
|
||||
|
||||
class KarteState extends State<Karte> {
|
||||
List<Marker> markers = [];
|
||||
LatLng? selectedPosition;
|
||||
Position? updatedPosition;
|
||||
bool saveVisible = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
markers = [
|
||||
Marker(
|
||||
point: LatLng(widget.currentPosition.latitude,
|
||||
widget.currentPosition.longitude),
|
||||
child: const Icon(
|
||||
Icons.location_on,
|
||||
color: Colors.red,
|
||||
))
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(AppLocalizations.of(context)!.map),
|
||||
actions: [
|
||||
Visibility(
|
||||
visible: saveVisible,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(AppLocalizations.of(context)!.saveMap),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
List<Placemark> placemarks = await placemarkFromCoordinates(
|
||||
selectedPosition!.latitude,
|
||||
selectedPosition!.longitude);
|
||||
// print(placemarks);
|
||||
|
||||
if (selectedPosition != null) {
|
||||
setState(() {
|
||||
widget.beiOrtC.text = placemarks.first.locality!;
|
||||
widget.ortInfoC.text = placemarks.first.street!;
|
||||
|
||||
updatedPosition = Position(
|
||||
longitude: selectedPosition!.longitude,
|
||||
latitude: selectedPosition!.latitude,
|
||||
timestamp: DateTime.now(),
|
||||
accuracy: 0.0,
|
||||
altitude: 0.0,
|
||||
altitudeAccuracy: 0.0,
|
||||
heading: 0.0,
|
||||
headingAccuracy: 0.0,
|
||||
speed: 0.0,
|
||||
speedAccuracy: 0.0);
|
||||
widget.onPositionChange(updatedPosition!);
|
||||
});
|
||||
}
|
||||
// ignore: use_build_context_synchronously
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Icon(Icons.save),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: FlutterMap(
|
||||
mapController: MapController(),
|
||||
options: MapOptions(
|
||||
interactionOptions: const InteractionOptions(
|
||||
flags: InteractiveFlag.pinchZoom |
|
||||
InteractiveFlag.drag |
|
||||
InteractiveFlag.pinchMove),
|
||||
initialCenter: LatLng(widget.currentPosition.latitude,
|
||||
widget.currentPosition.longitude),
|
||||
initialZoom: 16.0,
|
||||
onTap: _handleTap,
|
||||
),
|
||||
children: [
|
||||
TileLayer(
|
||||
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
userAgentPackageName: 'com.example.app',
|
||||
),
|
||||
MarkerLayer(markers: markers),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
_handleTap(TapPosition position, LatLng latlng) {
|
||||
setState(() {
|
||||
markers.clear();
|
||||
markers.add(
|
||||
Marker(
|
||||
width: 80.0,
|
||||
height: 80.0,
|
||||
point: latlng,
|
||||
child: const Icon(
|
||||
Icons.location_on,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
);
|
||||
selectedPosition = latlng;
|
||||
saveVisible = true;
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text(
|
||||
"${AppLocalizations.of(context)!.markerSet}\n${selectedPosition!.latitude}\n${selectedPosition!.longitude}")));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
// Datum == Datum der Aufstellung
|
||||
// datum is the variable where the chosen date is stored
|
||||
|
||||
class Datum extends StatefulWidget {
|
||||
final DateTime? initDatum;
|
||||
final Function(DateTime) onDateChanged;
|
||||
|
||||
const Datum({super.key, required this.initDatum, required this.onDateChanged});
|
||||
|
||||
@override
|
||||
State<Datum> createState() => _DatumState();
|
||||
}
|
||||
|
||||
class _DatumState extends State<Datum> {
|
||||
DateTime? datum;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
datum = widget.initDatum;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Row(children: [
|
||||
SizedBox(
|
||||
width: 140,
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
final date = await pickDate();
|
||||
if (date == null) return;
|
||||
setState(() => datum = date);
|
||||
widget.onDateChanged(date);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.pickDate)),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Text(
|
||||
'${datum?.day}. ${datum?.month}. ${datum?.year}',
|
||||
),
|
||||
]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<DateTime?> pickDate() async {
|
||||
final date = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: datum!,
|
||||
firstDate: DateTime(2000),
|
||||
lastDate: DateTime(5000));
|
||||
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
||||
// Status
|
||||
|
||||
class Status extends StatefulWidget {
|
||||
final Function(String) onStatusChanged;
|
||||
final String initialStatus;
|
||||
|
||||
const Status(
|
||||
{super.key, required this.onStatusChanged, this.initialStatus = 'Aktiv'});
|
||||
|
||||
@override
|
||||
State<Status> createState() => _StatusState();
|
||||
}
|
||||
|
||||
class _StatusState extends State<Status> {
|
||||
String? _selectedStatus;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedStatus = widget.initialStatus;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.aktiv),
|
||||
leading: Radio<String>(
|
||||
value: 'aktiv',
|
||||
groupValue: _selectedStatus,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedStatus = value;
|
||||
widget.onStatusChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.inaktiv),
|
||||
leading: Radio<String>(
|
||||
value: 'inaktiv',
|
||||
groupValue: _selectedStatus,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedStatus = value;
|
||||
widget.onStatusChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// STTyp
|
||||
|
||||
class STTyp extends StatefulWidget {
|
||||
final Function(String) onSTTypChanged;
|
||||
final String initialSTTyp;
|
||||
|
||||
const STTyp(
|
||||
{super.key,
|
||||
required this.onSTTypChanged,
|
||||
this.initialSTTyp = 'opportunistisch'});
|
||||
|
||||
@override
|
||||
State<STTyp> createState() => _STTypState();
|
||||
}
|
||||
|
||||
class _STTypState extends State<STTyp> {
|
||||
String? _selectedSTTyp;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedSTTyp = widget.initialSTTyp;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.opportunistisch),
|
||||
leading: Radio<String>(
|
||||
value: 'opportunistisch',
|
||||
groupValue: _selectedSTTyp,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedSTTyp = value;
|
||||
widget.onSTTypChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.systematisch),
|
||||
leading: Radio<String>(
|
||||
value: 'systematisch',
|
||||
groupValue: _selectedSTTyp,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedSTTyp = value;
|
||||
widget.onSTTypChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// platzung
|
||||
|
||||
class Platzung extends StatefulWidget {
|
||||
final Function(String) onPlatzungChanged;
|
||||
final String? initialPlatzung;
|
||||
|
||||
const Platzung({
|
||||
super.key,
|
||||
required this.onPlatzungChanged,
|
||||
this.initialPlatzung,
|
||||
});
|
||||
|
||||
@override
|
||||
State<Platzung> createState() => _PlatzungState();
|
||||
}
|
||||
|
||||
class _PlatzungState extends State<Platzung> {
|
||||
String? _selectedPlatzung;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.initialPlatzung != "") {
|
||||
_selectedPlatzung = widget.initialPlatzung;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.kirrung),
|
||||
leading: Radio<String>(
|
||||
value: 'Kirrung',
|
||||
groupValue: _selectedPlatzung,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedPlatzung = value;
|
||||
widget.onPlatzungChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.wasserstelle),
|
||||
leading: Radio<String>(
|
||||
value: 'Wasserstelle',
|
||||
groupValue: _selectedPlatzung,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedPlatzung = value;
|
||||
widget.onPlatzungChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.wald),
|
||||
leading: Radio<String>(
|
||||
value: 'Wald',
|
||||
groupValue: _selectedPlatzung,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedPlatzung = value;
|
||||
widget.onPlatzungChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.wildwechsel),
|
||||
leading: Radio<String>(
|
||||
value: 'Wildwechsel',
|
||||
groupValue: _selectedPlatzung,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedPlatzung = value;
|
||||
widget.onPlatzungChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.wegstrasse),
|
||||
leading: Radio<String>(
|
||||
value: 'Weg/Straße',
|
||||
groupValue: _selectedPlatzung,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedPlatzung = value;
|
||||
widget.onPlatzungChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.hofgarten),
|
||||
leading: Radio<String>(
|
||||
value: 'Hof/Garten',
|
||||
groupValue: _selectedPlatzung,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedPlatzung = value;
|
||||
widget.onPlatzungChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.wiesefeld),
|
||||
leading: Radio<String>(
|
||||
value: 'Wiese/Feld/Offenfläche',
|
||||
groupValue: _selectedPlatzung,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedPlatzung = value;
|
||||
widget.onPlatzungChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// FotoFilm
|
||||
|
||||
class FotoFilm extends StatefulWidget {
|
||||
final Function(String) onFotoFilmChanged;
|
||||
final String initialFotoFilm;
|
||||
|
||||
const FotoFilm(
|
||||
{super.key,
|
||||
required this.onFotoFilmChanged,
|
||||
this.initialFotoFilm = 'foto'});
|
||||
|
||||
@override
|
||||
State<FotoFilm> createState() => _FotoFilmState();
|
||||
}
|
||||
|
||||
class _FotoFilmState extends State<FotoFilm> {
|
||||
String? _selectedFotoFilm;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedFotoFilm = widget.initialFotoFilm;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.foto),
|
||||
leading: Radio<String>(
|
||||
value: 'Foto',
|
||||
groupValue: _selectedFotoFilm,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedFotoFilm = value;
|
||||
widget.onFotoFilmChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.film),
|
||||
leading: Radio<String>(
|
||||
value: 'Film',
|
||||
groupValue: _selectedFotoFilm,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedFotoFilm = value;
|
||||
widget.onFotoFilmChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// MEZ
|
||||
|
||||
class MEZ extends StatefulWidget {
|
||||
final Function(String) onMEZChanged;
|
||||
final String initialMEZ;
|
||||
|
||||
const MEZ(
|
||||
{super.key, required this.onMEZChanged, this.initialMEZ = 'sommerzeit'});
|
||||
|
||||
@override
|
||||
State<MEZ> createState() => _MEZState();
|
||||
}
|
||||
|
||||
class _MEZState extends State<MEZ> {
|
||||
String? _selectedMEZ;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedMEZ = widget.initialMEZ;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.sommerzeit),
|
||||
leading: Radio<String>(
|
||||
value: 'Sommerzeit',
|
||||
groupValue: _selectedMEZ,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedMEZ = value;
|
||||
widget.onMEZChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
visualDensity: const VisualDensity(vertical: -4),
|
||||
title: Text(AppLocalizations.of(context)!.winterzeit),
|
||||
leading: Radio<String>(
|
||||
value: 'Winterzeit',
|
||||
groupValue: _selectedMEZ,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedMEZ = value;
|
||||
widget.onMEZChanged(value!);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// KontDat
|
||||
|
||||
class KontDat extends StatefulWidget {
|
||||
final DateTime? initKontDat;
|
||||
final Function(DateTime) onDateChanged;
|
||||
|
||||
const KontDat(
|
||||
{super.key, required this.initKontDat, required this.onDateChanged});
|
||||
|
||||
@override
|
||||
State<KontDat> createState() => _KontDatState();
|
||||
}
|
||||
|
||||
class _KontDatState extends State<KontDat> {
|
||||
DateTime? kontDat;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
kontDat = widget.initKontDat;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Row(children: [
|
||||
SizedBox(
|
||||
width: 140,
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
final date = await pickDate();
|
||||
if (date == null) return;
|
||||
setState(() {
|
||||
kontDat = date;
|
||||
});
|
||||
widget.onDateChanged(date);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.pickkontdat)),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Text(
|
||||
'${kontDat?.day}. ${kontDat?.month}. ${kontDat?.year}',
|
||||
),
|
||||
]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<DateTime?> pickDate() async {
|
||||
final date = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: kontDat ?? DateTime.now(),
|
||||
firstDate: DateTime(2000),
|
||||
lastDate: DateTime(5000));
|
||||
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
||||
// AbbauDat
|
||||
|
||||
class AbbauDat extends StatefulWidget {
|
||||
final DateTime? initAbbauDat;
|
||||
final Function(DateTime) onDateChanged;
|
||||
|
||||
const AbbauDat({super.key, required this.initAbbauDat, required this.onDateChanged});
|
||||
|
||||
@override
|
||||
State<AbbauDat> createState() => _AbbauDatState();
|
||||
}
|
||||
|
||||
class _AbbauDatState extends State<AbbauDat> {
|
||||
DateTime? abbauDat;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
abbauDat = widget.initAbbauDat;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Column(children: [
|
||||
SizedBox(
|
||||
width: 140,
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
final date = await pickDate();
|
||||
if (date == null) return;
|
||||
setState(() => abbauDat = date);
|
||||
widget.onDateChanged(date);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.pickabbaudat)),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Builder(builder: (context) {
|
||||
if (abbauDat != null) {
|
||||
return Text(
|
||||
'${abbauDat?.day}. ${abbauDat?.month}. ${abbauDat?.year}');
|
||||
} else {
|
||||
return Text(AppLocalizations.of(context)!.nichts);
|
||||
}
|
||||
}),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
abbauDat = null;
|
||||
});
|
||||
},
|
||||
child: const Text("X"))
|
||||
]),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<DateTime?> pickDate() async {
|
||||
final date = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime(2000),
|
||||
lastDate: DateTime(5000));
|
||||
if (date == null) return null;
|
||||
|
||||
return date;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:fforte/addCam/add_cam_main.dart';
|
||||
import 'package:fforte/other/intro_screen.dart';
|
||||
import 'package:fforte/other/settings.dart';
|
||||
import 'package:fforte/viewCam/view_cams.dart';
|
||||
import 'package:fforte/screens/addCam/add_cam_main.dart';
|
||||
import 'package:fforte/screens/intro_screen.dart';
|
||||
import 'package:fforte/screens/settings.dart';
|
||||
import 'package:fforte/screens/viewCam/view_cams.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flex_color_scheme/flex_color_scheme.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
@@ -15,6 +15,7 @@ import 'l10n/l10n.dart';
|
||||
runApp(const MyApp());
|
||||
} */
|
||||
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
import 'dart:io' as io;
|
||||
import 'package:path/path.dart';
|
||||
|
||||
// * Gives the complete functionality for the databases
|
||||
// ! functions may not be named complete correctly
|
||||
|
||||
class DBHelper {
|
||||
static Database? _placeDB;
|
||||
|
||||
// checks if the databses are existing and creates them with the initPlaceDatabase function if not
|
||||
Future<Database> get placeDB async {
|
||||
if (_placeDB != null) {
|
||||
return _placeDB!;
|
||||
}
|
||||
_placeDB = await initPlaceDatabase();
|
||||
return _placeDB!;
|
||||
}
|
||||
|
||||
// Creates the databases with help from the _onCreatePlace function
|
||||
initPlaceDatabase() async {
|
||||
io.Directory documentsDirectory = await getApplicationCacheDirectory();
|
||||
String path = join(documentsDirectory.path, 'placeDB.db');
|
||||
var placeDB =
|
||||
await openDatabase(path, version: 1, onCreate: _onCreatePlace);
|
||||
return placeDB;
|
||||
}
|
||||
|
||||
// The function that helps
|
||||
_onCreatePlace(Database placeDB, int version) async {
|
||||
await placeDB.execute(
|
||||
'CREATE TABLE place (ID INTEGER PRIMARY KEY AUTOINCREMENT, CID TEXT, Standort TEXT, Rudel TEXT, Datum DATE, Adresse1 TEXT, Adresse2 TEXT, Adresse3 TEXT, BLand TEXT, Lkr TEXT, BeiOrt TEXT, OrtInfo TEXT, Status TEXT, FFTyp TEXT, FotoFilm TEXT, MEZ TEXT, Platzung TEXT, KSchloNr TEXT, KontDat DATE, Betreuung TEXT, AbbauDat DATE, Auftrag TEXT, KontAbsp TEXT, SonstBem TEXT, FKontakt1 TEXT, FKontakt2 TEXT, FKontakt3 TEXT, KTage1 INTEGER, KTage2 INTEGER, ProtoAm DATE, IntKomm TEXT, DECLNG DECIMALS(4,8), DECLAT DECIMALS(4,8), Sent INTEGER DEFAULT 0)');
|
||||
await placeDB.execute(
|
||||
'CREATE TABLE templates (ID INTEGER PRIMARY KEY AUTOINCREMENT, CID TEXT, Standort TEXT, Rudel TEXT, Datum DATE, Adresse1 TEXT, Adresse2 TEXT, Adresse3 TEXT, BLand TEXT, Lkr TEXT, BeiOrt TEXT, OrtInfo TEXT, Status TEXT, FFTyp TEXT, FotoFilm TEXT, MEZ TEXT, Platzung TEXT, KSchloNr TEXT, KontDat DATE, Betreuung TEXT, AbbauDat DATE, Auftrag TEXT, KontAbsp TEXT, SonstBem TEXT, FKontakt1 TEXT, FKontakt2 TEXT, FKontakt3 TEXT, KTage1 INTEGER, KTage2 INTEGER, ProtoAm DATE, IntKomm TEXT, DECLNG DECIMALS(4,8), DECLAT DECIMALS(4,8))');
|
||||
}
|
||||
|
||||
// Function to add a finished entry and return its ID
|
||||
Future<int> addPlace(Map<String, dynamic> place) async {
|
||||
var placeDBClient = await placeDB;
|
||||
final existingID = await placeDBClient.query(
|
||||
'place',
|
||||
where: 'ID = ?',
|
||||
whereArgs: [place['ID']],
|
||||
);
|
||||
|
||||
if (existingID.isNotEmpty) {
|
||||
updatePlace(place);
|
||||
return existingID.first['ID'] as int; // Return existing ID
|
||||
}
|
||||
|
||||
int id = await placeDBClient.insert(
|
||||
'place',
|
||||
place,
|
||||
//conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
|
||||
return id; // Return the ID of the newly inserted entry
|
||||
}
|
||||
|
||||
Future<void> updatePlace(Map<String, dynamic> place) async {
|
||||
var placeDBClient = await placeDB;
|
||||
|
||||
await placeDBClient
|
||||
.update('place', place, where: "ID = ?", whereArgs: [place['ID']]);
|
||||
}
|
||||
|
||||
// function to update the sent value
|
||||
Future<void> updateSent(int id) async {
|
||||
var placeDBClient = await placeDB;
|
||||
|
||||
await placeDBClient.update('place', {'Sent': 1},
|
||||
where: 'ID = ?', whereArgs: [id]);
|
||||
}
|
||||
|
||||
// same thing as before but with templatews
|
||||
Future<void> addTemplate(Map<String, dynamic> templates) async {
|
||||
var placeDBClient = await placeDB;
|
||||
|
||||
final existingCID = await placeDBClient.query(
|
||||
'templates',
|
||||
where: 'ID = ?',
|
||||
whereArgs: [templates['ID']],
|
||||
);
|
||||
if (existingCID.isNotEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
await placeDBClient.insert(
|
||||
'templates',
|
||||
templates,
|
||||
// conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
}
|
||||
|
||||
// Updates a existing template
|
||||
Future<void> updateTemplate(Map<String, dynamic> template) async {
|
||||
var placeDBClient = await placeDB;
|
||||
|
||||
await placeDBClient.update(
|
||||
'templates',
|
||||
template,
|
||||
where: "ID = ?",
|
||||
whereArgs: [template['ID']],
|
||||
);
|
||||
}
|
||||
|
||||
// get the finished entries from db
|
||||
Future<List<Map<String, dynamic>>> getPlace() async {
|
||||
var placeDBClient = await placeDB;
|
||||
return await placeDBClient.query('place');
|
||||
}
|
||||
|
||||
// get the finished templates from db
|
||||
Future<List<Map<String, dynamic>>> getTemplates() async {
|
||||
var placeDBClient = await placeDB;
|
||||
return await placeDBClient.query('templates');
|
||||
}
|
||||
|
||||
// deletes all finished entries from the db LOCALLY
|
||||
Future<void> deleteAllPlaces() async {
|
||||
var placeDBClient = await placeDB;
|
||||
await placeDBClient.delete('place');
|
||||
}
|
||||
|
||||
// deletes all templates from the db LOCALLY
|
||||
Future<void> deleteAllTemplates() async {
|
||||
var placeDBClient = await placeDB;
|
||||
await placeDBClient.delete('templates');
|
||||
}
|
||||
|
||||
// delete specific template
|
||||
Future<void> deleteTemplate(String id) async {
|
||||
var placeDBClient = await placeDB;
|
||||
await placeDBClient.delete(
|
||||
'templates',
|
||||
where: 'ID = ?',
|
||||
whereArgs: [id],
|
||||
);
|
||||
}
|
||||
|
||||
// delete specific place
|
||||
Future<void> deletePlace(String id) async {
|
||||
var placeDBClient = await placeDB;
|
||||
await placeDBClient.delete(
|
||||
'place',
|
||||
where: 'ID = ?',
|
||||
whereArgs: [id],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
class IntroScreen extends StatefulWidget {
|
||||
const IntroScreen({super.key});
|
||||
|
||||
@override
|
||||
State<IntroScreen> createState() => _IntroScreenState();
|
||||
}
|
||||
|
||||
class _IntroScreenState extends State<IntroScreen> {
|
||||
TextEditingController addresse1C = TextEditingController();
|
||||
TextEditingController bLandC = TextEditingController();
|
||||
TextEditingController apiAddress = TextEditingController();
|
||||
|
||||
String selectedApiAddress = "Test";
|
||||
String? selectedBLand;
|
||||
|
||||
Future<void> _saveData() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('addresse1', addresse1C.text);
|
||||
await prefs.setString('bLand', bLandC.text);
|
||||
await prefs.setBool('isFirstLaunch', false);
|
||||
await prefs.setString('apiAddress', apiAddress.text);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadPrefs();
|
||||
}
|
||||
|
||||
void _loadPrefs() {
|
||||
Future.delayed(Duration.zero, () async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
setState(() {
|
||||
apiAddress.text = prefs.getString('apiAddress') ?? "";
|
||||
addresse1C.text = prefs.getString('addresse1') ?? "";
|
||||
bLandC.text = prefs.getString('bLand') ?? "";
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('FFOrte'),
|
||||
),
|
||||
body: Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(31),
|
||||
child: Column(
|
||||
children: [
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
hintText: AppLocalizations.of(context)!.benutzername),
|
||||
controller: addresse1C,
|
||||
onChanged: (value) => setState(() {
|
||||
addresse1C.text = value;
|
||||
}),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: TextField(
|
||||
controller: bLandC,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: PopupMenuButton(
|
||||
icon: const Icon(Icons.arrow_drop_down),
|
||||
initialValue: selectedBLand,
|
||||
onSelected: (value) {
|
||||
setState(() {
|
||||
selectedBLand = value;
|
||||
bLandC.text = value;
|
||||
});
|
||||
},
|
||||
itemBuilder: (context) => const <PopupMenuEntry>[
|
||||
PopupMenuItem(
|
||||
value: 'Baden-Württemberg',
|
||||
child: Text('Baden-Württemberg'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Bayern',
|
||||
child: Text('Bayern'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Berlin',
|
||||
child: Text('Berlin'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Brandenburg',
|
||||
child: Text('Brandenburg'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Bremen',
|
||||
child: Text('Bremen'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Hamburg',
|
||||
child: Text('Hamburg'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Hessen',
|
||||
child: Text('Hessen'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Mecklenburg-Vorpommern',
|
||||
child: Text('Mecklenburg-Vorpommern'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Niedersachsen',
|
||||
child: Text('Niedersachsen'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Nordrhein-Westfalen',
|
||||
child: Text('Nordrhein-Westfalen'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Rheinland-Pfalz',
|
||||
child: Text('Rheinland-Pfalz'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Saarland',
|
||||
child: Text('Saarland'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Sachsen',
|
||||
child: Text('Sachsen'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Sachsen-Anhalt',
|
||||
child: Text('Sachsen-Anhalt'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Schleswig-Holstein',
|
||||
child: Text('Schleswig-Holstein'),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: 'Thüringen',
|
||||
child: Text('Thüringen'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
hintText:
|
||||
AppLocalizations.of(context)!.apiaddress),
|
||||
controller: apiAddress,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: PopupMenuButton(
|
||||
icon: const Icon(Icons.arrow_drop_down),
|
||||
initialValue: selectedApiAddress,
|
||||
onSelected: (value) {
|
||||
setState(() {
|
||||
selectedApiAddress = value;
|
||||
apiAddress.text = value;
|
||||
});
|
||||
},
|
||||
itemBuilder: (context) => <PopupMenuEntry>[
|
||||
PopupMenuItem(
|
||||
value:
|
||||
"http://192.168.1.106/www.dbb-wolf.de/data/app24.php",
|
||||
child:
|
||||
Text(AppLocalizations.of(context)!.test)),
|
||||
PopupMenuItem(
|
||||
value: "https://data.dbb-wolf.de/app24.php",
|
||||
child: Text(
|
||||
AppLocalizations.of(context)!.notest))
|
||||
],
|
||||
))
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
_saveData();
|
||||
Navigator.pushNamedAndRemoveUntil(
|
||||
context, '/home', (route) => false);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.continueB))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'dart:io';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class SendFile extends StatefulWidget {
|
||||
const SendFile({super.key});
|
||||
|
||||
@override
|
||||
State<SendFile> createState() => _SendFileState();
|
||||
}
|
||||
|
||||
class _SendFileState extends State<SendFile> {
|
||||
File? pickedFile;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: Column(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
FilePickerResult? result =
|
||||
await FilePicker.platform.pickFiles();
|
||||
|
||||
if (result != null) {
|
||||
pickedFile = File(result.files.single.path!);
|
||||
} else {
|
||||
pickedFile = File("");
|
||||
}
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.pickfile)),
|
||||
Text(pickedFile.toString()),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
final dio = Dio();
|
||||
final SharedPreferences prefs =
|
||||
await SharedPreferences.getInstance();
|
||||
String? fileContent = await pickedFile?.readAsString();
|
||||
|
||||
dio.options.responseType = ResponseType.plain;
|
||||
Response response = Response(
|
||||
requestOptions: RequestOptions(path: ''), statusCode: 400);
|
||||
|
||||
try {
|
||||
response = await dio.post(prefs.getString('apiAddress') ?? "",
|
||||
data: jsonEncode(fileContent));
|
||||
} on DioException catch (e) {
|
||||
if (e.response?.statusCode == 500) {
|
||||
/* print('-------------------------');
|
||||
print('code 500'); */
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (response.statusCode == 201) {
|
||||
// print(response.statusCode);
|
||||
} else {
|
||||
//print(response.statusCode);
|
||||
}
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.sendtoserver))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class Settings extends StatefulWidget {
|
||||
const Settings({super.key});
|
||||
|
||||
@override
|
||||
State<Settings> createState() => _SettingsState();
|
||||
}
|
||||
|
||||
class _SettingsState extends State<Settings> {
|
||||
|
||||
|
||||
Future<String> _getSaveDir() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
final String saveDir = prefs.getString('saveDir') ?? "";
|
||||
|
||||
return saveDir;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(AppLocalizations.of(context)!.settings),),
|
||||
body: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Text(AppLocalizations.of(context)!.filelocation, style: const TextStyle(fontSize: 20),),
|
||||
FutureBuilder(future: _getSaveDir(), builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
return Text(snapshot.data ?? "");
|
||||
} else {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
}),
|
||||
ElevatedButton(onPressed: () {
|
||||
|
||||
}, child: Text(AppLocalizations.of(context)!.open))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
import 'package:fforte/addCam/add_cam_main.dart';
|
||||
import 'package:fforte/other/db_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
// * Site that shows all entries in the databases
|
||||
|
||||
class ViewCams extends StatefulWidget {
|
||||
const ViewCams({super.key});
|
||||
|
||||
@override
|
||||
State<ViewCams> createState() => _ViewCamsState();
|
||||
}
|
||||
|
||||
class _ViewCamsState extends State<ViewCams> {
|
||||
// var declaration
|
||||
late Future<List<Map<String, dynamic>>> place;
|
||||
late Future<List<Map<String, dynamic>>> templates;
|
||||
late List<Marker> markers;
|
||||
|
||||
// loads the entries
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
place = DBHelper().getPlace();
|
||||
templates = DBHelper().getTemplates();
|
||||
}
|
||||
|
||||
// functions to delete all entries LOCALLY
|
||||
void delAllPlaces() async {
|
||||
return showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(AppLocalizations.of(context)!.deleteEverything),
|
||||
content: SingleChildScrollView(
|
||||
child: ListBody(children: <Widget>[
|
||||
Text(AppLocalizations.of(context)!.deleteEverythingContent)
|
||||
]),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
var placeDB = DBHelper();
|
||||
placeDB.deleteAllPlaces();
|
||||
setState(() {
|
||||
place = DBHelper().getPlace();
|
||||
});
|
||||
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.deleteEverything)),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.cancel))
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void delSinglePlace(int id) async {
|
||||
DBHelper().deletePlace(id.toString());
|
||||
setState(() {
|
||||
place = DBHelper().getPlace();
|
||||
});
|
||||
}
|
||||
|
||||
void delAllTemplates() async {
|
||||
return showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(AppLocalizations.of(context)!.deleteEverything),
|
||||
content: SingleChildScrollView(
|
||||
child: ListBody(children: <Widget>[
|
||||
Text(AppLocalizations.of(context)!.deleteEverythingContent)
|
||||
]),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
var placeDB = DBHelper();
|
||||
placeDB.deleteAllTemplates();
|
||||
setState(() {
|
||||
templates = DBHelper().getTemplates();
|
||||
});
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.deleteEverything)),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.cancel))
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// The widet tree with taps to differentiate between templates and finished entries
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTabController(
|
||||
length: 3,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
bottom: TabBar(tabs: [
|
||||
Tab(text: AppLocalizations.of(context)!.completed),
|
||||
Tab(
|
||||
text: AppLocalizations.of(context)!.uncompleted,
|
||||
),
|
||||
Tab(
|
||||
text: AppLocalizations.of(context)!.map,
|
||||
),
|
||||
]),
|
||||
title: Text(AppLocalizations.of(context)!.viewplacesappbar)),
|
||||
body: TabBarView(
|
||||
children: [
|
||||
FutureBuilder<List<Map<String, dynamic>>>(
|
||||
future: place,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const CircularProgressIndicator();
|
||||
} else if (snapshot.hasError) {
|
||||
return Text('Error: ${snapshot.error}');
|
||||
} else {
|
||||
List<Map<String, dynamic>> placeList =
|
||||
List.of(snapshot.data!);
|
||||
return Scaffold(
|
||||
floatingActionButton: FloatingActionButton(
|
||||
child: const Icon(Icons.delete),
|
||||
onPressed: () {
|
||||
delAllPlaces();
|
||||
},
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Text(
|
||||
style: const TextStyle(
|
||||
decoration: TextDecoration.underline),
|
||||
AppLocalizations.of(context)!.placedata),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
style: const TextStyle(
|
||||
decoration: TextDecoration.underline),
|
||||
AppLocalizations.of(context)!.sent),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: placeList.length,
|
||||
itemBuilder: (context, index) {
|
||||
Map<String, dynamic> place =
|
||||
snapshot.data![index];
|
||||
return Slidable(
|
||||
startActionPane: ActionPane(
|
||||
motion: const ScrollMotion(),
|
||||
children: [
|
||||
SlidableAction(
|
||||
onPressed: (context) {
|
||||
delSinglePlace(
|
||||
place['ID'.toString()]);
|
||||
},
|
||||
backgroundColor: Colors.red,
|
||||
foregroundColor: Colors.white,
|
||||
icon: Icons.delete,
|
||||
label: AppLocalizations.of(context)!
|
||||
.justdelete,
|
||||
)
|
||||
]),
|
||||
child: ListTile(
|
||||
title: Text('Standort ${index + 1}'),
|
||||
subtitle: Text(
|
||||
'ID: ${place['ID']} DATUM: ${place['Datum']}'),
|
||||
trailing: Checkbox(
|
||||
value: place['Sent'] == 0 ? false : true,
|
||||
onChanged: null,
|
||||
),
|
||||
onTap: () async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AddCamMain(
|
||||
isSent: place['Sent'] == 1 ? true : false,
|
||||
isFinished: true,
|
||||
existingData: place,
|
||||
)));
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
FutureBuilder<List<Map<String, dynamic>>>(
|
||||
future: templates,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const CircularProgressIndicator();
|
||||
} else if (snapshot.hasError) {
|
||||
return Text('Error: ${snapshot.error}');
|
||||
} else {
|
||||
return Scaffold(
|
||||
floatingActionButton: FloatingActionButton(
|
||||
child: const Icon(Icons.delete),
|
||||
onPressed: () {
|
||||
delAllTemplates();
|
||||
},
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: snapshot.data!.length,
|
||||
itemBuilder: (context, index) {
|
||||
Map<String, dynamic> templates =
|
||||
snapshot.data![index];
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
AddCamMain(
|
||||
isTemplate: true,
|
||||
isFinished: false,
|
||||
existingData: templates,
|
||||
)));
|
||||
},
|
||||
title: Text('Place ${index + 1}'),
|
||||
subtitle: Text(
|
||||
'ID: ${templates['CID']} DATUM: ${templates['Datum']} RUDEL: ${templates['Rudel']} STATUS: ${templates['Status']}'),
|
||||
);
|
||||
}),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}),
|
||||
FutureBuilder(
|
||||
future: place,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const CircularProgressIndicator();
|
||||
} else if (snapshot.hasError) {
|
||||
return Text("Error ${snapshot.error}");
|
||||
} else {
|
||||
markers = snapshot.data!.map((e) {
|
||||
return Marker(
|
||||
width: 80.0,
|
||||
height: 80.0,
|
||||
point: LatLng(double.parse(e['DECLAT'].toString()),
|
||||
double.parse(e['DECLNG'].toString())),
|
||||
child: Column(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.location_on,
|
||||
color: Colors.red,
|
||||
),
|
||||
Text(
|
||||
"ID: ${e['ID'].toString()}",
|
||||
style: const TextStyle(color: Colors.black),
|
||||
)
|
||||
],
|
||||
));
|
||||
}).toList();
|
||||
return FlutterMap(
|
||||
options: MapOptions(
|
||||
initialCenter: markers.isEmpty
|
||||
? const LatLng(50, 10)
|
||||
: markers.first.point,
|
||||
interactionOptions: const InteractionOptions(
|
||||
flags: InteractiveFlag.pinchZoom |
|
||||
InteractiveFlag.drag |
|
||||
InteractiveFlag.pinchMove)),
|
||||
children: [
|
||||
TileLayer(
|
||||
urlTemplate:
|
||||
'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
userAgentPackageName: 'com.example.app',
|
||||
),
|
||||
MarkerLayer(markers: markers)
|
||||
]);
|
||||
}
|
||||
})
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
24
pubspec.lock
24
pubspec.lock
@@ -337,18 +337,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.4"
|
||||
version: "10.0.5"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
version: "3.0.5"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -393,18 +393,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.0"
|
||||
version: "1.15.0"
|
||||
mgrs_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -638,10 +638,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.0"
|
||||
version: "0.7.2"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -678,10 +678,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.1"
|
||||
version: "14.2.5"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
Reference in New Issue
Block a user