besprechung

This commit is contained in:
Nico
2025-06-06 19:46:29 +02:00
parent 7d85090d4e
commit ccf1643711
13 changed files with 256 additions and 210 deletions

View File

@@ -1,3 +1,4 @@
import 'package:fforte/screens/sharedMethods/send_file.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'l10n/app_localizations.dart'; import 'l10n/app_localizations.dart';
import 'screens/addCam/add_cam_main.dart'; import 'screens/addCam/add_cam_main.dart';
@@ -101,7 +102,7 @@ class HomePage extends StatelessWidget {
minimumSize: const Size(250, 40), minimumSize: const Size(250, 40),
), ),
onPressed: () { onPressed: () {
// _sendFile(); SendFile.sendFile();
}, },
child: Text(AppLocalizations.of(context)!.sendfile), child: Text(AppLocalizations.of(context)!.sendfile),
), ),

View File

@@ -16,16 +16,19 @@ import 'l10n/l10n.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
// Set default values (Propably there is a better way to do this)
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
// isFirstLaunch decides whether the intro screen is shown or not
bool isFirstLaunch = prefs.getBool('isFirstLaunch') ?? true; bool isFirstLaunch = prefs.getBool('isFirstLaunch') ?? true;
if (prefs.getString("kTage1")?.isEmpty ?? true) await prefs.setString('kTage1', "28"); if (prefs.getString("kTage1")?.isEmpty ?? true) await prefs.setString('kTage1', "28");
if (prefs.getString("kTage2")?.isEmpty ?? true) await prefs.setString('kTage2', "48"); if (prefs.getString("kTage2")?.isEmpty ?? true) await prefs.setString('kTage2', "48");
if (prefs.getString("fotofallenApiAddress")?.isEmpty ?? true) await prefs.setString('fotofallenApiAddress', 'http://192.168.1.170/www.dbb-wolf.de/data/app24.php'); // if (prefs.getString("fotofallenApiAddress")?.isEmpty ?? true) await prefs.setString('fotofallenApiAddress', 'http://192.168.1.170/www.dbb-wolf.de/data/app24.php');
if (prefs.getString("exkursionenApiAddress")?.isEmpty ?? true) await prefs.setString('exkursionenApiAddress', 'http://192.168.1.170/www.dbb-wolf.de/data/api_exkursion.php'); // if (prefs.getString("exkursionenApiAddress")?.isEmpty ?? true) await prefs.setString('exkursionenApiAddress', 'http://192.168.1.170/www.dbb-wolf.de/data/api_exkursion.php');
runApp(MyApp(isFirstLaunch: isFirstLaunch)); runApp(MyApp(isFirstLaunch: isFirstLaunch));
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
final bool isFirstLaunch; final bool isFirstLaunch;
const MyApp({super.key, required this.isFirstLaunch}); const MyApp({super.key, required this.isFirstLaunch});
@@ -37,7 +40,9 @@ class MyApp extends StatelessWidget {
darkTheme: darkTheme:
FlexThemeData.dark(scheme: FlexScheme.greenM3, useMaterial3: true), FlexThemeData.dark(scheme: FlexScheme.greenM3, useMaterial3: true),
themeMode: ThemeMode.system, themeMode: ThemeMode.system,
// here the isFirstLaunch comes into play
initialRoute: isFirstLaunch ? '/introScreen' : '/home', initialRoute: isFirstLaunch ? '/introScreen' : '/home',
// Localization settings
supportedLocales: L10n.all, supportedLocales: L10n.all,
localizationsDelegates: const [ localizationsDelegates: const [
AppLocalizations.delegate, AppLocalizations.delegate,

View File

@@ -13,6 +13,7 @@ import 'package:fforte/l10n/app_localizations.dart';
import 'package:geolocator/geolocator.dart'; import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:animations/animations.dart'; import 'package:animations/animations.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'widgets/abbau_dat.dart'; import 'widgets/abbau_dat.dart';
import 'widgets/karte.dart'; import 'widgets/karte.dart';
@@ -164,11 +165,15 @@ class _AddCamMainState extends State<AddCamMain> {
} }
} else { } else {
// If it is not a template set default values // If it is not a template set default values
rmap["Datum"]!["controller"]!.text = DateTime.now().toString(); rmap["Datum"]!["controller"]!.text = DateTime.now().toString().split(" ").first;
rmap["Status"]!["controller"]!.text = "aktiv"; rmap["Status"]!["controller"]!.text = "aktiv";
rmap["FotoFilm"]!["controller"]!.text = "Foto"; rmap["FotoFilm"]!["controller"]!.text = "Foto";
rmap["MEZ"]!["controller"]!.text = "Sommerzeit"; rmap["MEZ"]!["controller"]!.text = "Sommerzeit";
rmap["Platzung"]!["controller"]!.text = ""; rmap["Platzung"]!["controller"]!.text = "";
SharedPreferences.getInstance().then((SharedPreferences prefs) {
rmap["KTage1"]!["controller"]!.text = prefs.getString("kTage1");
rmap["KTage2"]!["controller"]!.text = prefs.getString("kTage2");
});
} }
// Set initial default position // Set initial default position
@@ -201,7 +206,6 @@ class _AddCamMainState extends State<AddCamMain> {
); );
} }
} }
return currentPosition;
}); });
} }
@@ -353,7 +357,7 @@ class _AddCamMainState extends State<AddCamMain> {
} }
} }
if (!mounted) return; if (!context.mounted) return;
final result = await Navigator.of(context).push<LatLng>( final result = await Navigator.of(context).push<LatLng>(
MaterialPageRoute( MaterialPageRoute(
@@ -480,7 +484,7 @@ class _AddCamMainState extends State<AddCamMain> {
initDatum: DateTime.parse(rmap["Datum"]!["controller"]!.text), initDatum: DateTime.parse(rmap["Datum"]!["controller"]!.text),
onDateChanged: (value) { onDateChanged: (value) {
setState(() { setState(() {
rmap["Datum"]!["controller"]!.text = value.toString(); rmap["Datum"]!["controller"]!.text = value.toString().split(" ").first;
}); });
}, },
name: AppLocalizations.of(context)!.pickDate, name: AppLocalizations.of(context)!.pickDate,
@@ -705,8 +709,8 @@ class _AddCamMainState extends State<AddCamMain> {
} }
bool empty = CheckRequired.checkRequired(rmap); bool empty = CheckRequired.checkRequired(rmap);
// TODO for debugging always false // for debugging always false
empty = false; // empty = false;
if (empty == true) { if (empty == true) {
AddEntriesDialogHelper.showTemplateDialog( AddEntriesDialogHelper.showTemplateDialog(

View File

@@ -167,7 +167,7 @@ class _ExcursionMainState extends State<ExcursionMain> {
rmap["BLand"]!["controller"]!.text = prefs.getString('bLand') ?? ""; rmap["BLand"]!["controller"]!.text = prefs.getString('bLand') ?? "";
}); });
rmap["Datum"]!["controller"]!.text = DateTime.now().toString(); rmap["Datum"]!["controller"]!.text = DateTime.now().toString().split(" ").first;
rmap["Sent"]!["controller"]!.text = "0"; rmap["Sent"]!["controller"]!.text = "0";
} }
@@ -205,7 +205,7 @@ class _ExcursionMainState extends State<ExcursionMain> {
Datum( Datum(
initDatum: DateTime.now(), initDatum: DateTime.now(),
onDateChanged: (date) { onDateChanged: (date) {
rmap["Datum"]!["controller"]!.text = date.toString(); rmap["Datum"]!["controller"]!.text = date.toString().split(" ").first;
}, },
name: AppLocalizations.of(context)!.date, name: AppLocalizations.of(context)!.date,
), ),
@@ -357,7 +357,7 @@ class _ExcursionMainState extends State<ExcursionMain> {
// Check for always permission before starting tracking // Check for always permission before starting tracking
LocationPermission permission = await Geolocator.checkPermission(); LocationPermission permission = await Geolocator.checkPermission();
if (permission != LocationPermission.always) { if (permission != LocationPermission.always) {
if (mounted) { if (context.mounted) {
bool? shouldContinue = await showDialog<bool>( bool? shouldContinue = await showDialog<bool>(
context: context, context: context,
builder: (context) => AlertDialog( builder: (context) => AlertDialog(
@@ -371,7 +371,7 @@ class _ExcursionMainState extends State<ExcursionMain> {
TextButton( TextButton(
onPressed: () async { onPressed: () async {
await Geolocator.openAppSettings(); await Geolocator.openAppSettings();
Navigator.of(context).pop(true); if (context.mounted) Navigator.of(context).pop(true);
}, },
child: Text(AppLocalizations.of(context)!.openSettings), child: Text(AppLocalizations.of(context)!.openSettings),
), ),
@@ -387,7 +387,7 @@ class _ExcursionMainState extends State<ExcursionMain> {
// Try checking the permission multiple times // Try checking the permission multiple times
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
await Future.delayed(const Duration(seconds: 1)); await Future.delayed(const Duration(seconds: 1));
if (!mounted) return; if (!context.mounted) return;
permission = await Geolocator.checkPermission(); permission = await Geolocator.checkPermission();
if (permission == LocationPermission.always) { if (permission == LocationPermission.always) {
@@ -396,7 +396,7 @@ class _ExcursionMainState extends State<ExcursionMain> {
// If this is the last attempt and we still don't have permission // If this is the last attempt and we still don't have permission
if (i == 4 && permission != LocationPermission.always) { if (i == 4 && permission != LocationPermission.always) {
if (mounted) { if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text(AppLocalizations.of(context)!.permissionNotGranted), content: Text(AppLocalizations.of(context)!.permissionNotGranted),
@@ -410,6 +410,7 @@ class _ExcursionMainState extends State<ExcursionMain> {
} }
} }
if (context.mounted) {
await Navigator.push(context, MaterialPageRoute( await Navigator.push(context, MaterialPageRoute(
builder: (context) { builder: (context) {
return Tracking( return Tracking(
@@ -418,6 +419,7 @@ class _ExcursionMainState extends State<ExcursionMain> {
); );
}, },
)); ));
}
setState(() {}); setState(() {});
}, },
child: Text(AppLocalizations.of(context)!.trackingAnAusschalten), child: Text(AppLocalizations.of(context)!.trackingAnAusschalten),

View File

@@ -26,15 +26,15 @@ class _HinweiseState extends State<Hinweise> {
void initState() { void initState() {
sonstigesController.addListener(updateController); sonstigesController.addListener(updateController);
liegestelleChecked = widget.hinweise.text.contains("liegestelle") ? true : false; liegestelleChecked = widget.hinweise.text.contains("Liegestelle") ? true : false;
kadaverChecked = widget.hinweise.text.contains("kadaver") ? true : false; kadaverChecked = widget.hinweise.text.contains("Wildtierkadaver") ? true : false;
sichtungChecked = widget.hinweise.text.contains("sichtung") ? true : false; sichtungChecked = widget.hinweise.text.contains("Sichtung") ? true : false;
heulenChecked = widget.hinweise.text.contains("heulen") ? true : false; heulenChecked = widget.hinweise.text.contains("Heulen") ? true : false;
bool firstRun = true; bool firstRun = true;
for (String val in widget.hinweise.text.split(",")) { for (String val in widget.hinweise.text.split(",")) {
if (val != "liegestelle" && val != "kadaver" && val != "sichtung" && val != "heulen" && val != "") { if (val != "Liegestelle" && val != "Wildtierkadaver" && val != "Sichtung" && val != "Heulen" && val != "") {
sonstigesChecked = true; sonstigesChecked = true;
if (!firstRun) sonstigesController.text += ","; if (!firstRun) sonstigesController.text += ",";
sonstigesController.text += val; sonstigesController.text += val;
@@ -53,10 +53,10 @@ class _HinweiseState extends State<Hinweise> {
void updateController() { void updateController() {
Map<String, bool> props = { Map<String, bool> props = {
"liegestelle": liegestelleChecked, "Liegestelle": liegestelleChecked,
"kadaver": kadaverChecked, "Wildtierkadaver": kadaverChecked,
"sichtung": sichtungChecked, "Sichtung": sichtungChecked,
"heulen": heulenChecked, "Heulen": heulenChecked,
"sonstiges": sonstigesChecked "sonstiges": sonstigesChecked
}; };
bool firstRun = true; bool firstRun = true;

View File

@@ -215,7 +215,7 @@ class _TrackingState extends State<Tracking> {
currentPosition!.longitude, currentPosition!.longitude,
), ),
radius: currentPosition!.accuracy, radius: currentPosition!.accuracy,
color: Colors.blue.withOpacity(0.2), color: Colors.blue.withAlpha(2),
borderColor: Colors.blue, borderColor: Colors.blue,
borderStrokeWidth: 2, borderStrokeWidth: 2,
), ),

View File

@@ -72,12 +72,12 @@ class AddEntriesDialogHelper {
TextButton( TextButton(
onPressed: () async { onPressed: () async {
setState(() => isLoading = true); setState(() => isLoading = true);
int errorCode = await HttpRequest.httpRequest( int errorCode = await HttpRequestService.httpRequest(
saveDataMap: saveData, saveDataMap: saveData,
); );
setState(() => isLoading = false); setState(() => isLoading = false);
if (errorCode == 201 && context.mounted) { if (errorCode == 200 && context.mounted) {
Navigator.pop(context); Navigator.pop(context);
// saveData(true); // saveData(true);
SaveMainEntryMethod.saveEntry( SaveMainEntryMethod.saveEntry(
@@ -160,12 +160,12 @@ class AddEntriesDialogHelper {
TextButton( TextButton(
onPressed: () async { onPressed: () async {
setState(() => isLoading = true); setState(() => isLoading = true);
int errorCode = await HttpRequest.httpRequest( int errorCode = await HttpRequestService.httpRequest(
saveDataMap: saveData, saveDataMap: saveData,
); );
setState(() => isLoading = false); setState(() => isLoading = false);
if (errorCode != 201 || !context.mounted) { if (errorCode != 200 || !context.mounted) {
SaveMainEntryMethod.saveEntry( SaveMainEntryMethod.saveEntry(
entryData: saveData, entryData: saveData,
isTemplate: isTemplate, isTemplate: isTemplate,

View File

@@ -15,8 +15,8 @@ class _IntroScreenState extends State<IntroScreen> {
TextEditingController ffApiAddress = TextEditingController(); TextEditingController ffApiAddress = TextEditingController();
TextEditingController exApiAddress = TextEditingController(); TextEditingController exApiAddress = TextEditingController();
String selectedFFApiAddress = "Test"; String selectedFFApiAddress = "https://data.dbb-wolf.de/app24.php";
String selectedEXApiAddress = "Test"; String selectedEXApiAddress = "https://data.dbb-wolf.de/api_exkursion.php";
String? selectedBLand = "Sachsen"; String? selectedBLand = "Sachsen";
Future<void> _saveData() async { Future<void> _saveData() async {
@@ -38,8 +38,8 @@ class _IntroScreenState extends State<IntroScreen> {
Future.delayed(Duration.zero, () async { Future.delayed(Duration.zero, () async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() { setState(() {
ffApiAddress.text = prefs.getString('fotofallenApiAddress') ?? ""; ffApiAddress.text = prefs.getString('fotofallenApiAddress') ?? "https://data.dbb-wolf.de/app24.php";
exApiAddress.text = prefs.getString('exkursionenApiAddress') ?? ""; exApiAddress.text = prefs.getString('exkursionenApiAddress') ?? "https://data.dbb-wolf.de/api_exkursion.php";
addresse1C.text = prefs.getString('addresse1') ?? ""; addresse1C.text = prefs.getString('addresse1') ?? "";
bLandC.text = prefs.getString('bLand') ?? "Sachsen"; bLandC.text = prefs.getString('bLand') ?? "Sachsen";
}); });
@@ -161,86 +161,97 @@ class _IntroScreenState extends State<IntroScreen> {
], ],
), ),
const SizedBox( const SizedBox(
height: 25, height: 35,
), ),
Align( Align(
alignment: Alignment.bottomLeft, alignment: Alignment.bottomLeft,
child: Text(AppLocalizations.of(context)!.ffApiAddress)), child: Text(AppLocalizations.of(context)!.ffApiAddress)),
Row( Row(
children: [ children: [
Expanded( // Expanded(
flex: 4, // flex: 4,
child: TextField( // child: TextField(
decoration: InputDecoration( // decoration: InputDecoration(
hintText: // hintText:
AppLocalizations.of(context)!.ffApiAddress), // AppLocalizations.of(context)!.ffApiAddress),
controller: ffApiAddress, // controller: ffApiAddress,
), // ),
), // ),
Expanded( Expanded(
flex: 1, flex: 1,
child: PopupMenuButton( child: TextField(
icon: const Icon(Icons.arrow_drop_down), controller: ffApiAddress,
initialValue: selectedFFApiAddress, ),
onSelected: (value) { // child: PopupMenuButton(
setState(() { // icon: const Icon(Icons.arrow_drop_down),
selectedFFApiAddress = value; // initialValue: selectedFFApiAddress,
ffApiAddress.text = value; // onSelected: (value) {
}); // setState(() {
}, // selectedFFApiAddress = value;
itemBuilder: (context) => <PopupMenuEntry>[ // ffApiAddress.text = value;
PopupMenuItem( // });
value: // },
"http://192.168.1.106/www.dbb-wolf.de/data/app24.php", // itemBuilder: (context) => <PopupMenuEntry>[
child: // // PopupMenuItem(
Text(AppLocalizations.of(context)!.test)), // // value:
PopupMenuItem( // // "http://192.168.1.106/www.dbb-wolf.de/data/app24.php",
value: "...", // // child:
child: Text( // // Text(AppLocalizations.of(context)!.test)),
AppLocalizations.of(context)!.notest)) // PopupMenuItem(
], // value: "https://data.dbb-wolf.de/app24.php",
)) // child: Text(
// AppLocalizations.of(context)!.notest))
// ],
// ),
//)
)
], ],
), ),
const SizedBox(height: 10,), const SizedBox(
height: 10,
),
Align( Align(
alignment: Alignment.bottomLeft, alignment: Alignment.bottomLeft,
child: Text(AppLocalizations.of(context)!.exApiAddress)), child: Text(AppLocalizations.of(context)!.exApiAddress)),
Row( Row(
children: [ children: [
Expanded( // Expanded(
flex: 4, // flex: 4,
child: TextField( // child: TextField(
decoration: InputDecoration( // decoration: InputDecoration(
hintText: // hintText:
AppLocalizations.of(context)!.exApiAddress), // AppLocalizations.of(context)!.exApiAddress),
controller: exApiAddress, // controller: exApiAddress,
), // ),
), // ),
Expanded( Expanded(
flex: 1, flex: 1,
child: PopupMenuButton( child: TextField(
icon: const Icon(Icons.arrow_drop_down), controller: exApiAddress,
initialValue: selectedEXApiAddress, ),
onSelected: (value) { // child: PopupMenuButton(
setState(() { // icon: const Icon(Icons.arrow_drop_down),
selectedEXApiAddress = value; // initialValue: selectedEXApiAddress,
exApiAddress.text = value; // onSelected: (value) {
}); // setState(() {
}, // selectedEXApiAddress = value;
itemBuilder: (context) => <PopupMenuEntry>[ // exApiAddress.text = value;
PopupMenuItem( // });
value: // },
"http://192.168.1.106/www.dbb-wolf.de/data/app24.php", // itemBuilder: (context) => <PopupMenuEntry>[
child: // // PopupMenuItem(
Text(AppLocalizations.of(context)!.test)), // // value:
PopupMenuItem( // // "http://192.168.1.106/www.dbb-wolf.de/data/app24.php",
value: "...", // // child:
child: Text( // // Text(AppLocalizations.of(context)!.test)),
AppLocalizations.of(context)!.notest)) // PopupMenuItem(
], // value:
)) // "https://data.dbb-wolf.de/api_exkursion.php",
// child: Text(
// AppLocalizations.of(context)!.notest))
// ],
// )
)
], ],
) )
], ],

View File

@@ -3,7 +3,7 @@ import 'package:dio/dio.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
class HttpRequest { class HttpRequestService {
static Future<int> httpRequest({Map<String, String>? saveDataMap, String? saveDataString}) async { static Future<int> httpRequest({Map<String, String>? saveDataMap, String? saveDataString}) async {
// print(jsonEncode(place)); // print(jsonEncode(place));
@@ -25,7 +25,6 @@ class HttpRequest {
response = await dio.post(prefs.getString('exkursionenApiAddress') ?? "", response = await dio.post(prefs.getString('exkursionenApiAddress') ?? "",
data: saveDataMap == null ? saveDataString : jsonEncode(saveDataMap)); data: saveDataMap == null ? saveDataString : jsonEncode(saveDataMap));
} }
return response.statusCode!; return response.statusCode!;
} on DioException { } on DioException {
return response.statusCode ?? 400; return response.statusCode ?? 400;

View File

@@ -1,69 +1,80 @@
import 'dart:convert';
import 'package:dio/dio.dart'; import 'package:fforte/screens/sharedMethods/http_request.dart';
import 'package:fforte/l10n/app_localizations.dart'; import 'package:file_picker/file_picker.dart';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SendFile extends StatefulWidget { class SendFile {
const SendFile({super.key}); static Future<void> sendFile() async {
@override
State<SendFile> createState() => _SendFileState();
}
class _SendFileState extends State<SendFile> {
File? pickedFile; File? pickedFile;
@override FilePickerResult? result = await FilePicker.platform.pickFiles();
Widget build(BuildContext context) {
return Scaffold( if (result != null) {
appBar: AppBar(), pickedFile = File(result.files.single.path!);
body: Column( String fileContent = await pickedFile.readAsString();
children: [ await HttpRequestService.httpRequest(saveDataString: fileContent);
ElevatedButton( }
onPressed: () async { }
}
// 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 = // FilePickerResult? result =
// await FilePicker.platform.pickFiles(); // await FilePicker.platform.pickFiles();
//
// if (result != null) { // if (result != null) {
// pickedFile = File(result.files.single.path!); // pickedFile = File(result.files.single.path!);
// } else { // } else {
// pickedFile = File(""); // pickedFile = File("");
// } // }
}, // },
child: Text(AppLocalizations.of(context)!.pickfile)), // child: Text(AppLocalizations.of(context)!.pickfile)),
Text(pickedFile.toString()), // Text(pickedFile.toString()),
ElevatedButton( // ElevatedButton(
onPressed: () async { // onPressed: () async {
final dio = Dio(); // final dio = Dio();
final SharedPreferences prefs = await SharedPreferences.getInstance(); // final SharedPreferences prefs = await SharedPreferences.getInstance();
String? fileContent = await pickedFile?.readAsString(); // String? fileContent = await pickedFile?.readAsString();
//
dio.options.responseType = ResponseType.plain; // dio.options.responseType = ResponseType.plain;
Response response = Response( // Response response = Response(
requestOptions: RequestOptions(path: ''), statusCode: 400); // requestOptions: RequestOptions(path: ''), statusCode: 400);
//
try { // try {
response = await dio.post(prefs.getString('apiAddress') ?? "", // response = await dio.post(prefs.getString('apiAddress') ?? "",
data: jsonEncode(fileContent)); // data: jsonEncode(fileContent));
} on DioException catch (e) { // } on DioException catch (e) {
if (e.response?.statusCode == 500) { // if (e.response?.statusCode == 500) {
/* print('-------------------------'); // /* print('-------------------------');
print('code 500'); */ // print('code 500'); */
return; // return;
} // }
} // }
if (response.statusCode == 201) { // if (response.statusCode == 201) {
// print(response.statusCode); // // print(response.statusCode);
} else { // } else {
//print(response.statusCode); // //print(response.statusCode);
} // }
}, // },
child: Text(AppLocalizations.of(context)!.sendtoserver)) // child: Text(AppLocalizations.of(context)!.sendtoserver))
], // ],
), // ),
); // );
} // }
} // }

View File

@@ -185,7 +185,7 @@ class _ViewEntriesState extends State<ViewEntries> {
mainEntries[index]["Datum"])), mainEntries[index]["Datum"])),
), ),
trailing: Checkbox( trailing: Checkbox(
value: mainEntries[index]['Sent'] == 0 value: mainEntries[index]['Sent'] == "0" || mainEntries[index]["Sent"] == ""
? false ? false
: true, : true,
onChanged: null, onChanged: null,

View File

@@ -8,7 +8,13 @@ import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
/// Service, with the Singleton design pattern, that runs the geolocator service that tracks the position of the device.
/// This is needed for excursions
///
/// Start the tracking service via [startTracking]
/// Manage the position stream via [pauseTracking], [stopTracking] and [resumeTracking]
class TrackingService { class TrackingService {
// Singleton stuff
static TrackingService? _instance; static TrackingService? _instance;
factory TrackingService() { factory TrackingService() {
@@ -18,6 +24,7 @@ class TrackingService {
TrackingService._internal(); TrackingService._internal();
/// Resets all values, making it possible to start tracking again.
static void resetInstance() { static void resetInstance() {
if (_instance != null) { if (_instance != null) {
_instance!.dispose(); _instance!.dispose();
@@ -25,41 +32,48 @@ class TrackingService {
} }
} }
// Variables
// - Stores the tracked coordinates
List<LatLng> pathList = []; List<LatLng> pathList = [];
// - Stores all gotten accuracies
List<double> accuracyList = []; List<double> accuracyList = [];
// - Stores timer so that is responsible vor the periodically tracking
Timer? _positionTimer; Timer? _positionTimer;
bool isTracking = false; bool isTracking = false;
// - Some more Singleton stuff (i guess. Vibecoded it because of lack of time)
BuildContext? _lastContext; BuildContext? _lastContext;
final _positionController = StreamController<Position>.broadcast(); final _positionController = StreamController<Position>.broadcast();
final _statsController = StreamController<TrackingStats>.broadcast(); final _statsController = StreamController<TrackingStats>.broadcast();
// - Getter
Stream<Position> get positionStream$ => _positionController.stream; Stream<Position> get positionStream$ => _positionController.stream;
Stream<TrackingStats> get statsStream$ => _statsController.stream; Stream<TrackingStats> get statsStream$ => _statsController.stream;
double? currentAccuracy; // - Stores the last measured accuracy so that it can be displayed in the excursions view double? currentAccuracy;
// Name says it all
double _calculateMedianAccuracy(List<double> accuracies) { double _calculateMedianAccuracy(List<double> accuracies) {
// if one or less values for accuracy are available return that accuracy or 0
if (accuracies.isEmpty) return 0; if (accuracies.isEmpty) return 0;
if (accuracies.length == 1) return accuracies.first; if (accuracies.length == 1) return accuracies.first;
// Kopiere die Liste, um die Originaldaten nicht zu verändern // Copy the list so that the original data doesnt get modified
var sorted = List<double>.from(accuracies)..sort(); var sorted = List<double>.from(accuracies)..sort();
// Calculates median (not arithmetic mean!!). That is because often the firsed tracked accuracy is about 9000m
if (sorted.length % 2 == 0) { if (sorted.length % 2 == 0) {
// Bei gerader Anzahl: Durchschnitt der beiden mittleren Werte
int midIndex = sorted.length ~/ 2; int midIndex = sorted.length ~/ 2;
return (sorted[midIndex - 1] + sorted[midIndex]) / 2; return (sorted[midIndex - 1] + sorted[midIndex]) / 2;
} else { } else {
// Bei ungerader Anzahl: Der mittlere Wert
return sorted[sorted.length ~/ 2]; return sorted[sorted.length ~/ 2];
} }
} }
/// Starts tracking
Future<void> startTracking(BuildContext context) async { Future<void> startTracking(BuildContext context) async {
if (isTracking) return; if (isTracking) return;
final LocationSettings locationSettings = LocationSettings( final LocationSettings locationSettings =
accuracy: LocationAccuracy.high LocationSettings(accuracy: LocationAccuracy.high);
);
_lastContext = context; _lastContext = context;
await NotificationService().initNotification(); await NotificationService().initNotification();
@@ -74,12 +88,11 @@ class TrackingService {
final intervalSeconds = prefs.getInt('trackingInterval') ?? 60; final intervalSeconds = prefs.getInt('trackingInterval') ?? 60;
// Create a timer that triggers position updates // Create a timer that triggers position updates
_positionTimer = Timer.periodic(Duration(seconds: intervalSeconds), (_) async { _positionTimer =
Timer.periodic(Duration(seconds: intervalSeconds), (_) async {
try { try {
final Position position = await Geolocator.getCurrentPosition( final Position position = await Geolocator.getCurrentPosition(
locationSettings: locationSettings locationSettings: locationSettings);
);
pathList.add(LatLng(position.latitude, position.longitude)); pathList.add(LatLng(position.latitude, position.longitude));
accuracyList.add(position.accuracy); accuracyList.add(position.accuracy);
@@ -95,8 +108,7 @@ class TrackingService {
// Get initial position immediately // Get initial position immediately
try { try {
final Position position = await Geolocator.getCurrentPosition( final Position position = await Geolocator.getCurrentPosition(
locationSettings: locationSettings locationSettings: locationSettings);
);
pathList.add(LatLng(position.latitude, position.longitude)); pathList.add(LatLng(position.latitude, position.longitude));
accuracyList.add(position.accuracy); accuracyList.add(position.accuracy);
@@ -119,8 +131,7 @@ class TrackingService {
_lastStats = TrackingStats( _lastStats = TrackingStats(
currentAccuracy: currentAccuracy ?? 0, currentAccuracy: currentAccuracy ?? 0,
averageAccuracy: 0, averageAccuracy: 0,
totalDistanceMeters: 0 totalDistanceMeters: 0);
);
_statsController.add(_lastStats!); _statsController.add(_lastStats!);
return; return;
} }
@@ -131,8 +142,7 @@ class TrackingService {
pathList[i - 1].latitude, pathList[i - 1].latitude,
pathList[i - 1].longitude, pathList[i - 1].longitude,
pathList[i].latitude, pathList[i].latitude,
pathList[i].longitude pathList[i].longitude);
);
} }
double medianAccuracy = _calculateMedianAccuracy(accuracyList); double medianAccuracy = _calculateMedianAccuracy(accuracyList);
@@ -140,8 +150,7 @@ class TrackingService {
_lastStats = TrackingStats( _lastStats = TrackingStats(
currentAccuracy: currentAccuracy ?? 0, currentAccuracy: currentAccuracy ?? 0,
averageAccuracy: medianAccuracy, averageAccuracy: medianAccuracy,
totalDistanceMeters: totalDistance totalDistanceMeters: totalDistance);
);
_statsController.add(_lastStats!); _statsController.add(_lastStats!);
} }
@@ -149,7 +158,8 @@ class TrackingService {
_updateStats(); _updateStats();
} }
double _calculateDistance(double lat1, double lon1, double lat2, double lon2) { double _calculateDistance(
double lat1, double lon1, double lat2, double lon2) {
const double earthRadius = 6371000; // Erdradius in Metern const double earthRadius = 6371000; // Erdradius in Metern
double lat1Rad = lat1 * math.pi / 180; double lat1Rad = lat1 * math.pi / 180;
@@ -158,8 +168,10 @@ class TrackingService {
double deltaLon = (lon2 - lon1) * math.pi / 180; double deltaLon = (lon2 - lon1) * math.pi / 180;
double a = math.sin(deltaLat / 2) * math.sin(deltaLat / 2) + double a = math.sin(deltaLat / 2) * math.sin(deltaLat / 2) +
math.cos(lat1Rad) * math.cos(lat2Rad) * math.cos(lat1Rad) *
math.sin(deltaLon/2) * math.sin(deltaLon/2); math.cos(lat2Rad) *
math.sin(deltaLon / 2) *
math.sin(deltaLon / 2);
double c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)); double c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a));
return earthRadius * c; return earthRadius * c;

View File

@@ -97,3 +97,4 @@
4 jun 3h 45min 4 jun 3h 45min
5 jun 2h 10min 5 jun 2h 10min
6 jun 3h 30min