Update AndroidManifest to include foreground service permissions and implement tracking service in excursion screens. Refactor tracking logic to utilize TrackingService for better state management and streamline position updates.

This commit is contained in:
Nico
2025-06-04 20:59:50 +02:00
parent 0a737f5153
commit 9261abfc57
6 changed files with 404 additions and 377 deletions

View File

@@ -19,6 +19,7 @@ import 'package:fforte/screens/sharedMethods/save_template.dart';
import 'package:fforte/screens/sharedWidgets/datum.dart';
import 'package:fforte/screens/sharedWidgets/var_text_field.dart';
import 'package:fforte/l10n/app_localizations.dart';
import 'package:fforte/services/tracking_service.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -147,7 +148,6 @@ class _ExcursionMainState extends State<ExcursionMain> {
return currentPosition;
});
if (widget.existingData?.isNotEmpty ?? false) {
for (var key in widget.existingData!.keys) {
rmap[key]!["controller"]!.text =
@@ -188,268 +188,288 @@ class _ExcursionMainState extends State<ExcursionMain> {
@override
Widget build(BuildContext context) {
List<Step> getSteps() => [
Step(
title: Text(AppLocalizations.of(context)!.dateandtime),
content: Column(
children: [
// ---------- Date
Datum(
initDatum: DateTime.now(),
onDateChanged: (date) {
rmap["Datum"]!["controller"]!.text = date.toString();
},
name: AppLocalizations.of(context)!.date,
),
const SizedBox(height: 10),
// ---------- Pack
VarTextField(
textController: rmap["Rudel"]!["controller"]!,
localization: AppLocalizations.of(context)!.rudel,
dbName: "Rudel",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 10),
// ---------- Participants
VarTextField(
textController: rmap["Teilnehmer"]!["controller"]!,
localization: AppLocalizations.of(context)!.teilnehmer,
dbName: "Teilnehmer",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 10),
// ---------- Duration
VarTextField(
textController: rmap["Dauer"]!["controller"]!,
localization: AppLocalizations.of(context)!.dauer,
dbName: "Dauer",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 20),
// ---------- Dog(leash)
HundULeine(
mHund: rmap["MHund"]!["controller"]!,
mLeine: rmap["MLeine"]!["controller"]!,
),
const SizedBox(height: 10),
// ---------- State
VarTextField(
textController: rmap["BLand"]!["controller"]!,
localization: AppLocalizations.of(context)!.bland,
dbName: "BLand",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 10),
// ---------- Country
VarTextField(
textController: rmap["Lkr"]!["controller"]!,
localization: AppLocalizations.of(context)!.lkr,
dbName: "Lkr",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 10),
// ---------- By State
VarTextField(
textController: rmap["BeiOrt"]!["controller"]!,
localization: AppLocalizations.of(context)!.beiort,
dbName: "BeiOrt",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 10),
// ---------- Bima number
const Divider(),
const SizedBox(height: 10),
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(10)),
child: ExpansionPanelList(
expansionCallback:
((int index, bool isExpanded) =>
Step(
title: Text(AppLocalizations.of(context)!.dateandtime),
content: Column(
children: [
// ---------- Date
Datum(
initDatum: DateTime.now(),
onDateChanged: (date) {
rmap["Datum"]!["controller"]!.text = date.toString();
},
name: AppLocalizations.of(context)!.date,
),
const SizedBox(height: 10),
// ---------- Pack
VarTextField(
textController: rmap["Rudel"]!["controller"]!,
localization: AppLocalizations.of(context)!.rudel,
dbName: "Rudel",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 10),
// ---------- Participants
VarTextField(
textController: rmap["Teilnehmer"]!["controller"]!,
localization: AppLocalizations.of(context)!.teilnehmer,
dbName: "Teilnehmer",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 10),
// ---------- Duration
VarTextField(
textController: rmap["Dauer"]!["controller"]!,
localization: AppLocalizations.of(context)!.dauer,
dbName: "Dauer",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 20),
// ---------- Dog(leash)
HundULeine(
mHund: rmap["MHund"]!["controller"]!,
mLeine: rmap["MLeine"]!["controller"]!,
),
const SizedBox(height: 10),
// ---------- State
VarTextField(
textController: rmap["BLand"]!["controller"]!,
localization: AppLocalizations.of(context)!.bland,
dbName: "BLand",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 10),
// ---------- Country
VarTextField(
textController: rmap["Lkr"]!["controller"]!,
localization: AppLocalizations.of(context)!.lkr,
dbName: "Lkr",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 10),
// ---------- By State
VarTextField(
textController: rmap["BeiOrt"]!["controller"]!,
localization: AppLocalizations.of(context)!.beiort,
dbName: "BeiOrt",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 10),
// ---------- Bima number
const Divider(),
const SizedBox(height: 10),
ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(10)),
child: ExpansionPanelList(
expansionCallback: ((int index, bool isExpanded) =>
setState(() => bimaExtended = isExpanded)),
expandedHeaderPadding: EdgeInsets.all(0),
children: [
ExpansionPanel(
isExpanded: bimaExtended,
canTapOnHeader: true,
headerBuilder: (context, bool isOpen) => Padding(
padding: const EdgeInsets.only(left: 15),
child: Align(alignment: Alignment.centerLeft, child: Text("BImA", style: Theme.of(context).textTheme.bodyLarge,),),
),
body: Padding(
padding: const EdgeInsets.all(15),
child: Column(
children: [
const SizedBox(height: 10),
VarTextField(
textController: rmap["BimaNr"]!["controller"]!,
localization: AppLocalizations.of(context)!.bimaNr,
dbName: "BimaNr",
required: false,
dbDesignation: DatabasesEnum.excursion,
expandedHeaderPadding: EdgeInsets.all(0),
children: [
ExpansionPanel(
isExpanded: bimaExtended,
canTapOnHeader: true,
headerBuilder: (context, bool isOpen) => Padding(
padding: const EdgeInsets.only(left: 15),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
"BImA",
style: Theme.of(context).textTheme.bodyLarge,
),
),
const SizedBox(height: 10),
// ---------- Bima name
VarTextField(
textController: rmap["BimaName"]!["controller"]!,
localization: AppLocalizations.of(context)!.bimaName,
dbName: "BimaName",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
body: Padding(
padding: const EdgeInsets.all(15),
child: Column(
children: [
const SizedBox(height: 10),
VarTextField(
textController: rmap["BimaNr"]!["controller"]!,
localization:
AppLocalizations.of(context)!.bimaNr,
dbName: "BimaNr",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 10),
// ---------- Bima name
VarTextField(
textController:
rmap["BimaName"]!["controller"]!,
localization:
AppLocalizations.of(context)!.bimaName,
dbName: "BimaName",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 10),
// ---------- Bima user
BimaNutzer(
onBimaNutzerChanged: (value) {
setState(() {
rmap["BimaNutzer"]!["controller"]!.text =
value;
});
},
),
const SizedBox(height: 10),
// ---------- Bima AGV
VarTextField(
textController: rmap["BimaAGV"]!["controller"]!,
localization:
AppLocalizations.of(context)!.bimaAGV,
dbName: "BimaAGV",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
],
),
const SizedBox(height: 10),
// ---------- Bima user
BimaNutzer(
onBimaNutzerChanged: (value) {
setState(() {
rmap["BimaNutzer"]!["controller"]!.text = value;
});
},
),
const SizedBox(height: 10),
// ---------- Bima AGV
VarTextField(
textController: rmap["BimaAGV"]!["controller"]!,
localization: AppLocalizations.of(context)!.bimaAGV,
dbName: "BimaAGV",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
],
),
),
),
],
),
],
),
),
],
),
],
),
),
Step(
title: Text(AppLocalizations.of(context)!.umstaendeUndAktionen),
content: Column(
children: [
// ---------- Tracking
ElevatedButton(
onPressed:
() => Navigator.push(
context,
MaterialPageRoute(
),
Step(
title: Text(AppLocalizations.of(context)!.umstaendeUndAktionen),
content: Column(
children: [
// ---------- Tracking
ElevatedButton(
onPressed: () async {
await Navigator.push(context, MaterialPageRoute(
builder: (context) {
return Tracking(
weg: rmap["Weg"]!["controller"]!,
startPosition: currentPosition,
);
},
),
),
child: Text(AppLocalizations.of(context)!.trackingAnAusschalten),
),
const SizedBox(height: 10),
));
setState(() {});
},
child:
Text(AppLocalizations.of(context)!.trackingAnAusschalten),
),
const SizedBox(height: 10),
// ---------- Weather
VarTextField(
textController: rmap["Wetter"]!["controller"]!,
localization: AppLocalizations.of(context)!.wetter,
dbName: "Wetter",
required: false,
dbDesignation: DatabasesEnum.excursion,
// ---------- Weather
VarTextField(
textController: rmap["Wetter"]!["controller"]!,
localization: AppLocalizations.of(context)!.wetter,
dbName: "Wetter",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 10),
// ---------- Temperature
VarTextField(
textController: rmap["Temperat"]!["controller"]!,
localization: AppLocalizations.of(context)!.temperatur,
dbName: "Temperat",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 10),
// ---------- Last precipitation
LetzterNiederschlag(
controller: rmap["RegenVor"]!["controller"]!),
const SizedBox(height: 20),
// ---------- Track conditions
StreckeUSpurbedingungen(
kmAutoController: rmap["KmAuto"]!["controller"]!,
kmFussController: rmap["KmFuss"]!["controller"]!,
kmRadController: rmap["KmRad"]!["controller"]!,
spGutController: rmap["SpGut"]!["controller"]!,
spMittelController: rmap["SpMittel"]!["controller"]!,
spSchlechtController: rmap["SpSchlecht"]!["controller"]!,
),
const SizedBox(height: 20),
const Divider(),
// ---------- Track found
SpurGefunden(
spurFund: rmap["SpurFund"]!["controller"]!,
spurLang: rmap["SpurLang"]!["controller"]!,
spurTiere: rmap["SpurTiere"]!["controller"]!,
spSicher: rmap["SpSicher"]!["controller"]!,
welpenSp: rmap["WelpenSp"]!["controller"]!,
welpenAnz: rmap["WelpenAnz"]!["controller"]!,
wpSicher: rmap["WpSicher"]!["controller"]!,
),
const Divider(),
const SizedBox(height: 20),
// ---------- Counts
Anzahlen(
losungAnz: rmap["LosungAnz"]!["controller"]!,
losungGes: rmap["LosungGes"]!["controller"]!,
losungGen: rmap["LosungGen"]!["controller"]!,
urinAnz: rmap["UrinAnz"]!["controller"]!,
urinGen: rmap["UrinGen"]!["controller"]!,
oestrAnz: rmap["OestrAnz"]!["controller"]!,
oestrGen: rmap["OestrGen"]!["controller"]!,
haarAnz: rmap["HaarAnz"]!["controller"]!,
haarGen: rmap["HaarGen"]!["controller"]!,
),
const SizedBox(height: 20),
const Divider(),
const SizedBox(height: 20),
// ---------- Clues
Align(
alignment: Alignment.bottomLeft,
child: Text(
AppLocalizations.of(context)!.hinweise,
style: Theme.of(context).textTheme.titleMedium,
),
),
Hinweise(hinweise: rmap["Hinweise"]!["controller"]!),
],
),
const SizedBox(height: 10),
// ---------- Temperature
VarTextField(
textController: rmap["Temperat"]!["controller"]!,
localization: AppLocalizations.of(context)!.temperatur,
dbName: "Temperat",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
Step(
title: Text(AppLocalizations.of(context)!.intkomm),
content: Column(
children: [
// ---------- Remarks
VarTextField(
textController: rmap["Bemerk"]!["controller"]!,
localization: AppLocalizations.of(context)!.sonstbemerkungen,
dbName: "Bemerk",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 20),
// ---------- Internal communication
VarTextField(
textController: rmap["IntKomm"]!["controller"]!,
localization: AppLocalizations.of(context)!.intkomm,
dbName: "IntKomm",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
],
),
const SizedBox(height: 10),
// ---------- Last precipitation
LetzterNiederschlag(controller: rmap["RegenVor"]!["controller"]!),
const SizedBox(height: 20),
// ---------- Track conditions
StreckeUSpurbedingungen(
kmAutoController: rmap["KmAuto"]!["controller"]!,
kmFussController: rmap["KmFuss"]!["controller"]!,
kmRadController: rmap["KmRad"]!["controller"]!,
spGutController: rmap["SpGut"]!["controller"]!,
spMittelController: rmap["SpMittel"]!["controller"]!,
spSchlechtController: rmap["SpSchlecht"]!["controller"]!,
),
const SizedBox(height: 20),
const Divider(),
// ---------- Track found
SpurGefunden(
spurFund: rmap["SpurFund"]!["controller"]!,
spurLang: rmap["SpurLang"]!["controller"]!,
spurTiere: rmap["SpurTiere"]!["controller"]!,
spSicher: rmap["SpSicher"]!["controller"]!,
welpenSp: rmap["WelpenSp"]!["controller"]!,
welpenAnz: rmap["WelpenAnz"]!["controller"]!,
wpSicher: rmap["WpSicher"]!["controller"]!,
),
const Divider(),
const SizedBox(height: 20),
// ---------- Counts
Anzahlen(
losungAnz: rmap["LosungAnz"]!["controller"]!,
losungGes: rmap["LosungGes"]!["controller"]!,
losungGen: rmap["LosungGen"]!["controller"]!,
urinAnz: rmap["UrinAnz"]!["controller"]!,
urinGen: rmap["UrinGen"]!["controller"]!,
oestrAnz: rmap["OestrAnz"]!["controller"]!,
oestrGen: rmap["OestrGen"]!["controller"]!,
haarAnz: rmap["HaarAnz"]!["controller"]!,
haarGen: rmap["HaarGen"]!["controller"]!,
),
const SizedBox(height: 20),
const Divider(),
const SizedBox(height: 20),
// ---------- Clues
Align(
alignment: Alignment.bottomLeft,
child: Text(
AppLocalizations.of(context)!.hinweise,
style: Theme.of(context).textTheme.titleMedium,
),
),
Hinweise(hinweise: rmap["Hinweise"]!["controller"]!),
],
),
),
Step(
title: Text(AppLocalizations.of(context)!.intkomm),
content: Column(
children: [
// ---------- Remarks
VarTextField(
textController: rmap["Bemerk"]!["controller"]!,
localization: AppLocalizations.of(context)!.sonstbemerkungen,
dbName: "Bemerk",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
const SizedBox(height: 20),
// ---------- Internal communication
VarTextField(
textController: rmap["IntKomm"]!["controller"]!,
localization: AppLocalizations.of(context)!.intkomm,
dbName: "IntKomm",
required: false,
dbDesignation: DatabasesEnum.excursion,
),
],
),
),
];
),
];
// Begin of widget tree
return Scaffold(
appBar: AppBar(title: Text(AppLocalizations.of(context)!.excursion)),
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.excursion),
actions: [
// Text(TrackingService().isTracking ? "Tracking" : "Not tracking")
Image.asset(
TrackingService().isTracking ? "assets/icons/tracking_on.png" : "assets/icons/tracking_off.png",
width: 40,
),
],
),
body: PageTransitionSwitcher(
duration: const Duration(microseconds: 800),
transitionBuilder: (