Erweiterungen
This commit is contained in:
1 parent
ba1f7d5440
commit
bb3dc9e856
28 files changed
+1672
-1291
No files matched your search
Vendored
+10
-1
@@ -18,10 +18,19 @@
|
|||||||
"program": "lib/main.dart"
|
"program": "lib/main.dart"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Stadtbote - Aktuelles Gerät",
|
"name": "Stadtbote - Pixel 7",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"flutterMode": "debug",
|
"flutterMode": "debug",
|
||||||
|
"deviceId": "2B101FDH2003HB",
|
||||||
|
"program": "lib/main.dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Stadtbote - Pixel 7 (Release)",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release",
|
||||||
|
"deviceId": "2B101FDH2003HB",
|
||||||
"program": "lib/main.dart"
|
"program": "lib/main.dart"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ if (keystorePropertiesFile.exists()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 34
|
compileSdk 36
|
||||||
ndkVersion flutter.ndkVersion
|
ndkVersion flutter.ndkVersion
|
||||||
namespace "de.assecutor.stadtbote"
|
namespace "de.assecutor.stadtbote"
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ android {
|
|||||||
applicationId "de.assecutor.stadtbote"
|
applicationId "de.assecutor.stadtbote"
|
||||||
minSdkVersion flutter.minSdkVersion
|
minSdkVersion flutter.minSdkVersion
|
||||||
targetSdkVersion flutter.targetSdkVersion
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
compileSdk = 35
|
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
minSdkVersion 28
|
minSdkVersion 28
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
org.gradle.jvmargs=-Xmx1536M
|
org.gradle.jvmargs=-Xmx4096M -XX:MaxMetaspaceSize=512m
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
@@ -18,8 +18,8 @@ pluginManagement {
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||||
id "com.android.application" version "8.1.0" apply false
|
id "com.android.application" version "8.9.1" apply false
|
||||||
id "org.jetbrains.kotlin.android" version "1.8.10" apply false
|
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
include ":app"
|
include ":app"
|
||||||
@@ -8,6 +8,7 @@ import 'package:stadtbote/messages/pop_content_view_message.dart';
|
|||||||
import '../main.dart';
|
import '../main.dart';
|
||||||
import '../model/job.dart';
|
import '../model/job.dart';
|
||||||
import '../objectbox.g.dart';
|
import '../objectbox.g.dart';
|
||||||
|
import '../popups/files_popup.dart';
|
||||||
import '../tools/constants.dart';
|
import '../tools/constants.dart';
|
||||||
import '../tools/util.dart';
|
import '../tools/util.dart';
|
||||||
|
|
||||||
@@ -92,38 +93,48 @@ class _AcceptedJobsPageState extends State<AcceptedJobsPage>
|
|||||||
border: Border.all(color: Colors.grey, width: 0.5),
|
border: Border.all(color: Colors.grey, width: 0.5),
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(13))),
|
borderRadius: const BorderRadius.all(Radius.circular(13))),
|
||||||
padding: const EdgeInsets.all(15.0),
|
padding: const EdgeInsets.all(15.0),
|
||||||
child: Column(
|
child: Stack(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
Column(
|
||||||
children: <Widget>[
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Text(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
DateFormat('dd.MM.yyyy HH:mm')
|
children: <Widget>[
|
||||||
.format(DateTime.fromMillisecondsSinceEpoch(job.ordertime * 1000, isUtc: true).toLocal())
|
Text(
|
||||||
.toString(),
|
DateFormat('dd.MM.yyyy HH:mm')
|
||||||
style: TextStyle(
|
.format(DateTime.fromMillisecondsSinceEpoch(job.ordertime * 1000, isUtc: true).toLocal())
|
||||||
backgroundColor: isJobTooFarInFuture(job)
|
.toString(),
|
||||||
? Colors.red
|
style: TextStyle(
|
||||||
: Colors.transparent),
|
backgroundColor: isJobTooFarInFuture(job)
|
||||||
textAlign: TextAlign.left,
|
? Colors.red
|
||||||
),
|
: Colors.transparent),
|
||||||
Text(
|
textAlign: TextAlign.left,
|
||||||
job.crFilter != null ? job.crFilter.toString() : "",
|
),
|
||||||
textAlign: TextAlign.left,
|
Text(
|
||||||
),
|
job.crFilter != null ? job.crFilter.toString() : "",
|
||||||
Row(
|
textAlign: TextAlign.left,
|
||||||
children: [
|
),
|
||||||
Image.asset(
|
Row(
|
||||||
"assets/${mapVehicleTypeToImageName(job.vhtId)}",
|
children: [
|
||||||
width: 60,
|
Image.asset(
|
||||||
height: 60),
|
"assets/${mapVehicleTypeToImageName(job.vhtId)}",
|
||||||
const SizedBox(width: 10),
|
width: 60,
|
||||||
Text(mapVehicleTypeToText(job.vhtId)),
|
height: 60),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Text(mapVehicleTypeToText(job.vhtId)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
bottomText,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(
|
if (job.hasFiles)
|
||||||
bottomText,
|
Positioned(
|
||||||
textAlign: TextAlign.left,
|
top: 0,
|
||||||
),
|
right: 0,
|
||||||
|
child: FilesBadge(files: job.files),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import 'package:stadtbote/tools/messages/remove_state_0_job_from_map.dart';
|
|||||||
|
|
||||||
import '../main.dart';
|
import '../main.dart';
|
||||||
import '../model/job.dart';
|
import '../model/job.dart';
|
||||||
|
import '../popups/files_popup.dart';
|
||||||
import '../tools/constants.dart';
|
import '../tools/constants.dart';
|
||||||
import '../tools/util.dart';
|
import '../tools/util.dart';
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ class _AssignedJobsPageState extends State<AssignedJobsPage> with ProducerMixin,
|
|||||||
late bool _initCompleted = false;
|
late bool _initCompleted = false;
|
||||||
|
|
||||||
List<Job> _jobs = [];
|
List<Job> _jobs = [];
|
||||||
|
final List<ExpandableController> _expandableControllers = [];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -59,6 +61,11 @@ class _AssignedJobsPageState extends State<AssignedJobsPage> with ProducerMixin,
|
|||||||
// Unsubscribe from all message topics
|
// Unsubscribe from all message topics
|
||||||
unsubscribe(queueId: Constants.messageTopicJobReceived);
|
unsubscribe(queueId: Constants.messageTopicJobReceived);
|
||||||
|
|
||||||
|
// Dispose all expandable controllers
|
||||||
|
for (var controller in _expandableControllers) {
|
||||||
|
controller.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,9 +99,16 @@ class _AssignedJobsPageState extends State<AssignedJobsPage> with ProducerMixin,
|
|||||||
List<Widget> _createWidgets() {
|
List<Widget> _createWidgets() {
|
||||||
List<Widget> widgets = [];
|
List<Widget> widgets = [];
|
||||||
|
|
||||||
|
// Clear previous controllers before creating new ones
|
||||||
|
for (var controller in _expandableControllers) {
|
||||||
|
controller.dispose();
|
||||||
|
}
|
||||||
|
_expandableControllers.clear();
|
||||||
|
|
||||||
for (var job in _jobs) {
|
for (var job in _jobs) {
|
||||||
var expandableController = ExpandableController();
|
var expandableController = ExpandableController();
|
||||||
expandableController.expanded = true;
|
expandableController.expanded = true;
|
||||||
|
_expandableControllers.add(expandableController);
|
||||||
|
|
||||||
widgets.add(ExpandableNotifier(
|
widgets.add(ExpandableNotifier(
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
@@ -126,27 +140,29 @@ class _AssignedJobsPageState extends State<AssignedJobsPage> with ProducerMixin,
|
|||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(12.0),
|
padding: const EdgeInsets.all(12.0),
|
||||||
child: Column(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Column(
|
||||||
DateFormat('dd.MM.yyyy HH:mm').format(DateTime.fromMillisecondsSinceEpoch(job.ordertime * 1000, isUtc: true).toLocal()).toString(),
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
style: TextStyle(backgroundColor: isJobTooFarInFuture(job) ? Colors.red : Colors.white),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
children: [
|
||||||
Image.asset("assets/${mapVehicleTypeToImageName(job.vhtId)}", width: 60, height: 60),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Text(
|
Text(
|
||||||
mapVehicleTypeToText(job.vhtId)
|
DateFormat('dd.MM.yyyy HH:mm').format(DateTime.fromMillisecondsSinceEpoch(job.ordertime * 1000, isUtc: true).toLocal()).toString(),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
style: TextStyle(backgroundColor: isJobTooFarInFuture(job) ? Colors.red : Colors.white),
|
||||||
),
|
),
|
||||||
],
|
Row(
|
||||||
),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
Text('Auftragsnummer: ${job.externalId}'),
|
children: [
|
||||||
Text(job.tours[0].comp),
|
Image.asset("assets/${mapVehicleTypeToImageName(job.vhtId)}", width: 60, height: 60),
|
||||||
Text(job.tours[0].street),
|
const SizedBox(width: 10),
|
||||||
Text(job.tours[0].city),
|
Text(
|
||||||
|
mapVehicleTypeToText(job.vhtId)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text('Auftragsnummer: ${job.externalId}'),
|
||||||
|
Text(job.tours[0].comp),
|
||||||
|
Text(job.tours[0].street),
|
||||||
|
Text(job.tours[0].city),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
ScrollOnExpand(
|
ScrollOnExpand(
|
||||||
scrollOnExpand: true,
|
scrollOnExpand: true,
|
||||||
@@ -217,11 +233,19 @@ class _AssignedJobsPageState extends State<AssignedJobsPage> with ProducerMixin,
|
|||||||
])
|
])
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
if (job.hasFiles)
|
||||||
|
Positioned(
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
child: FilesBadge(files: job.files),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
));
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return widgets;
|
return widgets;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:stadtbote/messages/pop_content_view_message.dart';
|
|||||||
|
|
||||||
import '../main.dart';
|
import '../main.dart';
|
||||||
import '../model/job.dart';
|
import '../model/job.dart';
|
||||||
|
import '../popups/files_popup.dart';
|
||||||
import '../tools/constants.dart';
|
import '../tools/constants.dart';
|
||||||
import '../tools/util.dart';
|
import '../tools/util.dart';
|
||||||
|
|
||||||
@@ -85,34 +86,44 @@ class _FinishedJobsPageState extends State<FinishedJobsPage>
|
|||||||
border: Border.all(color: Colors.grey, width: 0.5),
|
border: Border.all(color: Colors.grey, width: 0.5),
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(13))),
|
borderRadius: const BorderRadius.all(Radius.circular(13))),
|
||||||
padding: const EdgeInsets.all(15.0),
|
padding: const EdgeInsets.all(15.0),
|
||||||
child: Column(
|
child: Stack(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
Column(
|
||||||
children: <Widget>[
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Text(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
DateFormat('dd.MM.yyyy HH:mm')
|
children: <Widget>[
|
||||||
.format(DateTime.fromMillisecondsSinceEpoch(job.ordertime * 1000, isUtc: true).toLocal())
|
Text(
|
||||||
.toString(),
|
DateFormat('dd.MM.yyyy HH:mm')
|
||||||
textAlign: TextAlign.left,
|
.format(DateTime.fromMillisecondsSinceEpoch(job.ordertime * 1000, isUtc: true).toLocal())
|
||||||
),
|
.toString(),
|
||||||
Text(
|
textAlign: TextAlign.left,
|
||||||
job.crFilter != null ? job.crFilter.toString() : "",
|
),
|
||||||
textAlign: TextAlign.left,
|
Text(
|
||||||
),
|
job.crFilter != null ? job.crFilter.toString() : "",
|
||||||
Row(
|
textAlign: TextAlign.left,
|
||||||
children: [
|
),
|
||||||
Image.asset(
|
Row(
|
||||||
"assets/${mapVehicleTypeToImageName(job.vhtId)}",
|
children: [
|
||||||
width: 60,
|
Image.asset(
|
||||||
height: 60),
|
"assets/${mapVehicleTypeToImageName(job.vhtId)}",
|
||||||
const SizedBox(width: 10),
|
width: 60,
|
||||||
Text(mapVehicleTypeToText(job.vhtId)),
|
height: 60),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Text(mapVehicleTypeToText(job.vhtId)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
bottomText,
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(
|
if (job.hasFiles)
|
||||||
bottomText,
|
Positioned(
|
||||||
textAlign: TextAlign.left,
|
top: 0,
|
||||||
),
|
right: 0,
|
||||||
|
child: FilesBadge(files: job.files),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
|
|||||||
@@ -102,6 +102,12 @@ class _SetDesiredZipPageState extends State<SetDesiredZipPage>
|
|||||||
acknowledgeRequired: true),
|
acknowledgeRequired: true),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
textEditController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _HeaderWidget extends StatefulWidget {
|
class _HeaderWidget extends StatefulWidget {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:stadtbote/messages/pop_content_view_message.dart';
|
|||||||
import 'package:stadtbote/model/job_tour.dart';
|
import 'package:stadtbote/model/job_tour.dart';
|
||||||
|
|
||||||
import '../model/job.dart';
|
import '../model/job.dart';
|
||||||
|
import '../popups/files_popup.dart';
|
||||||
import '../popups/map_popup.dart';
|
import '../popups/map_popup.dart';
|
||||||
import '../tools/constants.dart';
|
import '../tools/constants.dart';
|
||||||
import '../tools/util.dart';
|
import '../tools/util.dart';
|
||||||
@@ -103,6 +104,22 @@ class _TourDetailsPageState extends State<TourDetailsPage> with ProducerMixin {
|
|||||||
"Komm-#: ${widget.tour.com}",
|
"Komm-#: ${widget.tour.com}",
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
|
if (widget.tour.files != null && widget.tour.files!.isNotEmpty) ...[
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
FilesBadge(files: widget.tour.files),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
const Text(
|
||||||
|
'Dateien verfügbar',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
getDefaultButton(
|
getDefaultButton(
|
||||||
'Karte',
|
'Karte',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:stadtbote/model/job_tour.dart';
|
|||||||
import 'package:stadtbote/popups/map_popup.dart';
|
import 'package:stadtbote/popups/map_popup.dart';
|
||||||
|
|
||||||
import '../model/job.dart';
|
import '../model/job.dart';
|
||||||
|
import '../popups/files_popup.dart';
|
||||||
import '../tools/constants.dart';
|
import '../tools/constants.dart';
|
||||||
import '../tools/util.dart';
|
import '../tools/util.dart';
|
||||||
|
|
||||||
@@ -152,9 +153,16 @@ class _TourListPageState extends State<TourListPage> with ProducerMixin {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Row(
|
||||||
"${"Station ${tour.sort}"} ${mapTourMode(tour.mode)}",
|
children: [
|
||||||
textAlign: TextAlign.left,
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
"${"Station ${tour.sort}"} ${mapTourMode(tour.mode)}",
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
FilesBadge(files: tour.files),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
tour.comp,
|
tour.comp,
|
||||||
|
|||||||
+33
-1
@@ -188,7 +188,7 @@ class Main extends StatefulWidget {
|
|||||||
State<Main> createState() => _MainState();
|
State<Main> createState() => _MainState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MainState extends State<Main> with SingleTickerProviderStateMixin, ProducerMixin, ConsumerMixin {
|
class _MainState extends State<Main> with SingleTickerProviderStateMixin, ProducerMixin, ConsumerMixin, WidgetsBindingObserver {
|
||||||
DateTime? _lastBackPressed;
|
DateTime? _lastBackPressed;
|
||||||
late ContentViewWidget _currentContent;
|
late ContentViewWidget _currentContent;
|
||||||
late WidgetStack<ContentViewWidget> contentStack = WidgetStack();
|
late WidgetStack<ContentViewWidget> contentStack = WidgetStack();
|
||||||
@@ -210,7 +210,11 @@ class _MainState extends State<Main> with SingleTickerProviderStateMixin, Produc
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
// Unregister from app lifecycle events
|
||||||
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
|
|
||||||
_animationController?.dispose();
|
_animationController?.dispose();
|
||||||
|
_checkForNewState0JobsJobsTimer?.cancel();
|
||||||
|
|
||||||
// Unsubscribe from all message topics
|
// Unsubscribe from all message topics
|
||||||
unsubscribe(queueId: Constants.messageTopicPushContentView);
|
unsubscribe(queueId: Constants.messageTopicPushContentView);
|
||||||
@@ -228,6 +232,9 @@ class _MainState extends State<Main> with SingleTickerProviderStateMixin, Produc
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
|
// Register for app lifecycle events
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
|
||||||
_animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 1000));
|
_animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 1000));
|
||||||
_animationController!.repeat(reverse: true);
|
_animationController!.repeat(reverse: true);
|
||||||
@@ -253,6 +260,31 @@ class _MainState extends State<Main> with SingleTickerProviderStateMixin, Produc
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
super.didChangeAppLifecycleState(state);
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case AppLifecycleState.paused:
|
||||||
|
case AppLifecycleState.inactive:
|
||||||
|
case AppLifecycleState.hidden:
|
||||||
|
// App geht in den Hintergrund - Animation und Timer pausieren
|
||||||
|
_animationController?.stop();
|
||||||
|
_checkForNewState0JobsJobsTimer?.cancel();
|
||||||
|
Util.logInfo('App in Hintergrund - Rendering pausiert');
|
||||||
|
break;
|
||||||
|
case AppLifecycleState.resumed:
|
||||||
|
// App kommt zurück in den Vordergrund - Animation und Timer fortsetzen
|
||||||
|
_animationController?.repeat(reverse: true);
|
||||||
|
_startCheckForNewState0JobsJobsTimer();
|
||||||
|
Util.logInfo('App im Vordergrund - Rendering fortgesetzt');
|
||||||
|
break;
|
||||||
|
case AppLifecycleState.detached:
|
||||||
|
// App wird beendet
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (!_initCompleted) {
|
if (!_initCompleted) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:objectbox/objectbox.dart';
|
import 'package:objectbox/objectbox.dart';
|
||||||
import 'package:stadtbote/model/job_tour.dart';
|
import 'package:stadtbote/model/job_tour.dart';
|
||||||
|
import 'package:stadtbote/model/tour_file.dart';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
class Job {
|
class Job {
|
||||||
@@ -34,6 +35,25 @@ class Job {
|
|||||||
toursJson = jsonEncode(value.map((e) => e.toJson()).toList());
|
toursJson = jsonEncode(value.map((e) => e.toJson()).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store files as JSON string for ObjectBox
|
||||||
|
String filesJson = '';
|
||||||
|
|
||||||
|
@Transient()
|
||||||
|
List<TourFile> get files {
|
||||||
|
if (filesJson.isEmpty) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
final List<dynamic> decoded = jsonDecode(filesJson);
|
||||||
|
return decoded.map((e) => TourFile.fromJson(e as Map<String, dynamic>)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
set files(List<TourFile> value) {
|
||||||
|
filesJson = jsonEncode(value.map((e) => e.toJson()).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to check if job has files
|
||||||
|
bool get hasFiles => files.isNotEmpty;
|
||||||
|
|
||||||
Job();
|
Job();
|
||||||
|
|
||||||
factory Job.fromJson(Map<String, dynamic> json) {
|
factory Job.fromJson(Map<String, dynamic> json) {
|
||||||
@@ -52,6 +72,11 @@ class Job {
|
|||||||
job.tours = (json['tours'] as List<dynamic>)
|
job.tours = (json['tours'] as List<dynamic>)
|
||||||
.map((e) => JobTour.fromJson(e as Map<String, dynamic>))
|
.map((e) => JobTour.fromJson(e as Map<String, dynamic>))
|
||||||
.toList();
|
.toList();
|
||||||
|
if (json['files'] != null) {
|
||||||
|
job.files = (json['files'] as List<dynamic>)
|
||||||
|
.map((e) => TourFile.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +93,7 @@ class Job {
|
|||||||
'rem': rem,
|
'rem': rem,
|
||||||
'delayOrderTime': delayOrderTime,
|
'delayOrderTime': delayOrderTime,
|
||||||
'tours': tours.map((e) => e.toJson()).toList(),
|
'tours': tours.map((e) => e.toJson()).toList(),
|
||||||
|
'files': files.map((e) => e.toJson()).toList(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:stadtbote/model/tour_file.dart';
|
||||||
import 'package:stadtbote/model/tour_position.dart';
|
import 'package:stadtbote/model/tour_position.dart';
|
||||||
import 'package:stadtbote/model/tour_task.dart';
|
import 'package:stadtbote/model/tour_task.dart';
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ class JobTour {
|
|||||||
TourPosition? pos;
|
TourPosition? pos;
|
||||||
late String rem;
|
late String rem;
|
||||||
List<TourTask>? tasks;
|
List<TourTask>? tasks;
|
||||||
|
List<TourFile>? files;
|
||||||
|
|
||||||
JobTour();
|
JobTour();
|
||||||
|
|
||||||
@@ -39,6 +41,11 @@ class JobTour {
|
|||||||
.map((e) => TourTask.fromJson(e as Map<String, dynamic>))
|
.map((e) => TourTask.fromJson(e as Map<String, dynamic>))
|
||||||
.toList()
|
.toList()
|
||||||
: null;
|
: null;
|
||||||
|
tour.files = json['files'] != null
|
||||||
|
? (json['files'] as List<dynamic>)
|
||||||
|
.map((e) => TourFile.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList()
|
||||||
|
: null;
|
||||||
return tour;
|
return tour;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +64,7 @@ class JobTour {
|
|||||||
'pos': pos?.toJson(),
|
'pos': pos?.toJson(),
|
||||||
'rem': rem,
|
'rem': rem,
|
||||||
'tasks': tasks?.map((e) => e.toJson()).toList(),
|
'tasks': tasks?.map((e) => e.toJson()).toList(),
|
||||||
|
'files': files?.map((e) => e.toJson()).toList(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
class TourFile {
|
||||||
|
late int index;
|
||||||
|
late String name;
|
||||||
|
String? remark;
|
||||||
|
late String url;
|
||||||
|
|
||||||
|
TourFile();
|
||||||
|
|
||||||
|
factory TourFile.fromJson(Map<String, dynamic> json) {
|
||||||
|
final file = TourFile();
|
||||||
|
file.index = json['index'] as int;
|
||||||
|
file.name = json['name'] as String;
|
||||||
|
file.remark = json['remark'] as String?;
|
||||||
|
file.url = json['url'] as String;
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'index': index,
|
||||||
|
'name': name,
|
||||||
|
'remark': remark,
|
||||||
|
'url': url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isPdf {
|
||||||
|
return name.toLowerCase().endsWith('.pdf');
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isImage {
|
||||||
|
final ext = name.toLowerCase();
|
||||||
|
return ext.endsWith('.jpg') ||
|
||||||
|
ext.endsWith('.jpeg') ||
|
||||||
|
ext.endsWith('.png') ||
|
||||||
|
ext.endsWith('.gif') ||
|
||||||
|
ext.endsWith('.webp');
|
||||||
|
}
|
||||||
|
}
|
||||||
+72
-51
@@ -34,7 +34,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3:808686831791135454",
|
"id": "3:808686831791135454",
|
||||||
"lastPropertyId": "12:5320049580625017283",
|
"lastPropertyId": "13:1803006975816940878",
|
||||||
"name": "Job",
|
"name": "Job",
|
||||||
"properties": [
|
"properties": [
|
||||||
{
|
{
|
||||||
@@ -99,6 +99,11 @@
|
|||||||
"type": 6,
|
"type": 6,
|
||||||
"flags": 40,
|
"flags": 40,
|
||||||
"indexId": "1:2108836818156887439"
|
"indexId": "1:2108836818156887439"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "13:1803006975816940878",
|
||||||
|
"name": "filesJson",
|
||||||
|
"type": 9
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"relations": []
|
"relations": []
|
||||||
@@ -302,52 +307,6 @@
|
|||||||
],
|
],
|
||||||
"relations": []
|
"relations": []
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"id": "11:8606238749478059367",
|
|
||||||
"lastPropertyId": "7:4056540326670791484",
|
|
||||||
"name": "ChatMessage",
|
|
||||||
"properties": [
|
|
||||||
{
|
|
||||||
"id": "1:3236243497308902958",
|
|
||||||
"name": "id",
|
|
||||||
"type": 6,
|
|
||||||
"flags": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2:1689142031630531044",
|
|
||||||
"name": "time",
|
|
||||||
"type": 6
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3:9168654785572108691",
|
|
||||||
"name": "from",
|
|
||||||
"type": 9
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "4:3167039386322657979",
|
|
||||||
"name": "subj",
|
|
||||||
"type": 9
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "5:371212500165499194",
|
|
||||||
"name": "text",
|
|
||||||
"type": 9
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "6:7798301556204918939",
|
|
||||||
"name": "read",
|
|
||||||
"type": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "7:4056540326670791484",
|
|
||||||
"name": "externalId",
|
|
||||||
"type": 6,
|
|
||||||
"flags": 40,
|
|
||||||
"indexId": "2:428171949166894655"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"relations": []
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "12:4865606913057136676",
|
"id": "12:4865606913057136676",
|
||||||
"lastPropertyId": "5:4858121848992922038",
|
"lastPropertyId": "5:4858121848992922038",
|
||||||
@@ -381,17 +340,65 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"relations": []
|
"relations": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "14:3621615714595976689",
|
||||||
|
"lastPropertyId": "7:5084802055406173119",
|
||||||
|
"name": "ChatMessage",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "1:8160599835026826926",
|
||||||
|
"name": "id",
|
||||||
|
"type": 6,
|
||||||
|
"flags": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2:5736027494386837185",
|
||||||
|
"name": "externalId",
|
||||||
|
"type": 6,
|
||||||
|
"flags": 40,
|
||||||
|
"indexId": "4:8487631368009819350"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3:4672344908186587803",
|
||||||
|
"name": "time",
|
||||||
|
"type": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4:2026513613729432961",
|
||||||
|
"name": "from",
|
||||||
|
"type": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5:6544369704442218939",
|
||||||
|
"name": "subj",
|
||||||
|
"type": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6:8234519373253706290",
|
||||||
|
"name": "text",
|
||||||
|
"type": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7:5084802055406173119",
|
||||||
|
"name": "read",
|
||||||
|
"type": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"relations": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"lastEntityId": "12:4865606913057136676",
|
"lastEntityId": "14:3621615714595976689",
|
||||||
"lastIndexId": "2:428171949166894655",
|
"lastIndexId": "4:8487631368009819350",
|
||||||
"lastRelationId": "0:0",
|
"lastRelationId": "0:0",
|
||||||
"lastSequenceId": "0:0",
|
"lastSequenceId": "0:0",
|
||||||
"modelVersion": 5,
|
"modelVersion": 5,
|
||||||
"modelVersionParserMinimum": 5,
|
"modelVersionParserMinimum": 5,
|
||||||
"retiredEntityUids": [
|
"retiredEntityUids": [
|
||||||
3121960281516643928,
|
3121960281516643928,
|
||||||
3330049220205502332
|
3330049220205502332,
|
||||||
|
8606238749478059367,
|
||||||
|
9126058286032860192
|
||||||
],
|
],
|
||||||
"retiredIndexUids": [],
|
"retiredIndexUids": [],
|
||||||
"retiredPropertyUids": [
|
"retiredPropertyUids": [
|
||||||
@@ -405,7 +412,21 @@
|
|||||||
3530187464938423960,
|
3530187464938423960,
|
||||||
2939471599149775609,
|
2939471599149775609,
|
||||||
4870701960967093484,
|
4870701960967093484,
|
||||||
9072329172508444422
|
9072329172508444422,
|
||||||
|
3236243497308902958,
|
||||||
|
1689142031630531044,
|
||||||
|
9168654785572108691,
|
||||||
|
3167039386322657979,
|
||||||
|
371212500165499194,
|
||||||
|
7798301556204918939,
|
||||||
|
4056540326670791484,
|
||||||
|
2028590175083469347,
|
||||||
|
7733678916130816919,
|
||||||
|
5111729991837061065,
|
||||||
|
2981635918450905844,
|
||||||
|
4551221086536914442,
|
||||||
|
1176594062059570557,
|
||||||
|
4272081635478028034
|
||||||
],
|
],
|
||||||
"retiredRelationUids": [],
|
"retiredRelationUids": [],
|
||||||
"version": 1
|
"version": 1
|
||||||
|
|||||||
+933
-1097
File diff suppressed because it is too large.
Load diff
@@ -133,6 +133,7 @@ Future<void> showAcceptJobsPopup(SharedMapReference mapReference, Function() onF
|
|||||||
// Ensure we always clean up subscriptions even if the dialog is dismissed via barrier tap or back button
|
// Ensure we always clean up subscriptions even if the dialog is dismissed via barrier tap or back button
|
||||||
dialogFuture.whenComplete(() {
|
dialogFuture.whenComplete(() {
|
||||||
isActive = false;
|
isActive = false;
|
||||||
|
_scrollController.dispose();
|
||||||
if (builderCreated) {
|
if (builderCreated) {
|
||||||
try {
|
try {
|
||||||
extendedStatefulBuilder.unsubscribe(queueId: Constants.messageTopicJobReceived);
|
extendedStatefulBuilder.unsubscribe(queueId: Constants.messageTopicJobReceived);
|
||||||
|
|||||||
@@ -0,0 +1,262 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:stadtbote/content_views/pdf_viewer_page.dart';
|
||||||
|
import 'package:stadtbote/model/tour_file.dart';
|
||||||
|
import 'package:stadtbote/tools/constants.dart';
|
||||||
|
import 'package:stadtbote/tools/util.dart' show showAlertDialog;
|
||||||
|
import 'package:stadtbote/tools/util.dart' as util;
|
||||||
|
|
||||||
|
void showFilesPopup(BuildContext context, List<TourFile> files) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('Dateien'),
|
||||||
|
content: SizedBox(
|
||||||
|
width: double.maxFinite,
|
||||||
|
height: 400,
|
||||||
|
child: ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: files.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final file = files[index];
|
||||||
|
return _FileListItem(file: file);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: const Text('Schließen'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FileListItem extends StatefulWidget {
|
||||||
|
final TourFile file;
|
||||||
|
|
||||||
|
const _FileListItem({required this.file});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_FileListItem> createState() => _FileListItemState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FileListItemState extends State<_FileListItem> {
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
IconData iconData;
|
||||||
|
if (widget.file.isPdf) {
|
||||||
|
iconData = Icons.picture_as_pdf;
|
||||||
|
} else if (widget.file.isImage) {
|
||||||
|
iconData = Icons.image;
|
||||||
|
} else {
|
||||||
|
iconData = Icons.insert_drive_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(iconData, color: Constants.colorPrimary),
|
||||||
|
title: Text(widget.file.name),
|
||||||
|
subtitle: widget.file.remark != null && widget.file.remark!.isNotEmpty
|
||||||
|
? Text(widget.file.remark!)
|
||||||
|
: null,
|
||||||
|
trailing: _isLoading
|
||||||
|
? const SizedBox(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
|
)
|
||||||
|
: const Icon(Icons.open_in_new),
|
||||||
|
onTap: () => _openFile(context),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _openFile(BuildContext context) async {
|
||||||
|
if (_isLoading) return;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (widget.file.isPdf) {
|
||||||
|
Navigator.of(context).pop(); // Close the files dialog
|
||||||
|
|
||||||
|
// Navigate to PDF viewer
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => PdfViewerPage(
|
||||||
|
pdfUrl: widget.file.url,
|
||||||
|
title: widget.file.name,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (widget.file.isImage) {
|
||||||
|
Navigator.of(context).pop(); // Close the files dialog
|
||||||
|
|
||||||
|
// Show image in a dialog
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return Dialog(
|
||||||
|
insetPadding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
AppBar(
|
||||||
|
title: Text(widget.file.name),
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(Icons.close),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
automaticallyImplyLeading: false,
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: Image.network(
|
||||||
|
widget.file.url,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
loadingBuilder: (context, child, loadingProgress) {
|
||||||
|
if (loadingProgress == null) return child;
|
||||||
|
return Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
value: loadingProgress.expectedTotalBytes != null
|
||||||
|
? loadingProgress.cumulativeBytesLoaded /
|
||||||
|
loadingProgress.expectedTotalBytes!
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.error_outline,
|
||||||
|
size: 64,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Fehler beim Laden des Bildes:\n$error',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(color: Colors.red),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// For other file types, try to open with external app
|
||||||
|
showAlertDialog(
|
||||||
|
'Information',
|
||||||
|
'Dateityp wird nicht direkt unterstützt. Die Datei wird im Browser geöffnet.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
util.Util.logException('Error opening file: $e');
|
||||||
|
if (context.mounted) {
|
||||||
|
showAlertDialog(
|
||||||
|
'Fehler',
|
||||||
|
'Fehler beim Öffnen der Datei: $e',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Widget to display a file icon button if files are available
|
||||||
|
class FilesIconButton extends StatelessWidget {
|
||||||
|
final List<TourFile>? files;
|
||||||
|
final double iconSize;
|
||||||
|
|
||||||
|
const FilesIconButton({
|
||||||
|
super.key,
|
||||||
|
required this.files,
|
||||||
|
this.iconSize = 24,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (files == null || files!.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
return IconButton(
|
||||||
|
icon: const Icon(Icons.attach_file),
|
||||||
|
iconSize: iconSize,
|
||||||
|
color: Constants.colorPrimary,
|
||||||
|
tooltip: '${files!.length} Datei(en)',
|
||||||
|
onPressed: () => showFilesPopup(context, files!),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Badge widget showing file count with icon
|
||||||
|
class FilesBadge extends StatelessWidget {
|
||||||
|
final List<TourFile>? files;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
|
const FilesBadge({
|
||||||
|
super.key,
|
||||||
|
required this.files,
|
||||||
|
this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (files == null || files!.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap ?? () => showFilesPopup(context, files!),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Constants.colorPrimary.withAlpha(30),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: Constants.colorPrimary, width: 1),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.attach_file,
|
||||||
|
size: 16,
|
||||||
|
color: Constants.colorPrimary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
'${files!.length}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Constants.colorPrimary,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,9 @@ import '../model/job_tour.dart';
|
|||||||
List<Color> _colors = [Colors.red, Colors.green];
|
List<Color> _colors = [Colors.red, Colors.green];
|
||||||
|
|
||||||
showMapPopup(BuildContext context, JobTour currentTour, List<JobTour> tourList) async {
|
showMapPopup(BuildContext context, JobTour currentTour, List<JobTour> tourList) async {
|
||||||
|
MapController? mapController;
|
||||||
|
Map<int, StreamController<bool>> streamControllers = {};
|
||||||
|
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
@@ -19,24 +22,24 @@ showMapPopup(BuildContext context, JobTour currentTour, List<JobTour> tourList)
|
|||||||
shadowColor: Colors.transparent,
|
shadowColor: Colors.transparent,
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
content: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
|
content: StatefulBuilder(builder: (BuildContext context, StateSetter setState) {
|
||||||
var mapController = MapController();
|
mapController = MapController();
|
||||||
Map<int, StreamController<bool>> streamController = {};
|
streamControllers = {};
|
||||||
|
|
||||||
List<Widget> tourWidgets = [];
|
List<Widget> tourWidgets = [];
|
||||||
for (JobTour tour in tourList) {
|
for (JobTour tour in tourList) {
|
||||||
var currentStreamController = StreamController<bool>();
|
var currentStreamController = StreamController<bool>();
|
||||||
streamController[tour.sort] = currentStreamController;
|
streamControllers[tour.sort] = currentStreamController;
|
||||||
|
|
||||||
tourWidgets.add(
|
tourWidgets.add(
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
if (tour.pos != null) {
|
if (tour.pos != null) {
|
||||||
mapController.move(LatLng(tour.pos!.lat, tour.pos!.long), mapController.camera.zoom);
|
mapController!.move(LatLng(tour.pos!.lat, tour.pos!.long), mapController!.camera.zoom);
|
||||||
|
|
||||||
streamController[currentTour.sort]!.sink.add(false);
|
streamControllers[currentTour.sort]!.sink.add(false);
|
||||||
|
|
||||||
currentTour = tour;
|
currentTour = tour;
|
||||||
streamController[tour.sort]!.sink.add(true);
|
streamControllers[tour.sort]!.sink.add(true);
|
||||||
} else {
|
} else {
|
||||||
showAlertDialog('Information', 'Es liegt keine Position für diese Station vor');
|
showAlertDialog('Information', 'Es liegt keine Position für diese Station vor');
|
||||||
}
|
}
|
||||||
@@ -158,7 +161,7 @@ showMapPopup(BuildContext context, JobTour currentTour, List<JobTour> tourList)
|
|||||||
height: 25,
|
height: 25,
|
||||||
onChanged: (b) {
|
onChanged: (b) {
|
||||||
valueNotifier.value = b;
|
valueNotifier.value = b;
|
||||||
mapController.move(mapController.camera.center, b ? 14.0 : 16.0);
|
mapController!.move(mapController!.camera.center, b ? 14.0 : 16.0);
|
||||||
},
|
},
|
||||||
styleBuilder: (i) => ToggleStyle(indicatorColor: i == true ? _colors[0] : _colors[1]),
|
styleBuilder: (i) => ToggleStyle(indicatorColor: i == true ? _colors[0] : _colors[1]),
|
||||||
textBuilder: (value) => value ? const Center(child: Text('Fern')) : const Center(child: Text('Nah')),
|
textBuilder: (value) => value ? const Center(child: Text('Fern')) : const Center(child: Text('Nah')),
|
||||||
@@ -173,7 +176,7 @@ showMapPopup(BuildContext context, JobTour currentTour, List<JobTour> tourList)
|
|||||||
child: AbsorbPointer(
|
child: AbsorbPointer(
|
||||||
absorbing: true,
|
absorbing: true,
|
||||||
child: FlutterMap(
|
child: FlutterMap(
|
||||||
mapController: mapController,
|
mapController: mapController!,
|
||||||
options: MapOptions(
|
options: MapOptions(
|
||||||
initialCenter: LatLng(currentTour.pos!.lat, currentTour.pos!.long),
|
initialCenter: LatLng(currentTour.pos!.lat, currentTour.pos!.long),
|
||||||
initialZoom: 14,
|
initialZoom: 14,
|
||||||
@@ -196,4 +199,10 @@ showMapPopup(BuildContext context, JobTour currentTour, List<JobTour> tourList)
|
|||||||
));
|
));
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Dispose all controllers after dialog is closed
|
||||||
|
mapController?.dispose();
|
||||||
|
for (var controller in streamControllers.values) {
|
||||||
|
controller.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,12 @@ showComissionNumberPopup(BuildContext context, TaskInfo taskInfo, Function(Strin
|
|||||||
controller: textEditingController,
|
controller: textEditingController,
|
||||||
autofocus: true),
|
autofocus: true),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
getDefaultButton('Übernehmen', () => {Navigator.of(context).pop(), onFinished(textEditingController.text)})
|
getDefaultButton('Übernehmen', () {
|
||||||
|
final result = textEditingController.text;
|
||||||
|
textEditingController.dispose();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
onFinished(result);
|
||||||
|
})
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ showPickupDeliveryStatusPopup(BuildContext context, TaskInfo taskInfo, Function(
|
|||||||
if (!didPop) {
|
if (!didPop) {
|
||||||
final shouldPop = await taskDialogSecurityException(context);
|
final shouldPop = await taskDialogSecurityException(context);
|
||||||
if (shouldPop && context.mounted) {
|
if (shouldPop && context.mounted) {
|
||||||
|
controller.dispose();
|
||||||
|
remController.dispose();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,6 +75,9 @@ showPickupDeliveryStatusPopup(BuildContext context, TaskInfo taskInfo, Function(
|
|||||||
if (remController.text.isNotEmpty) {
|
if (remController.text.isNotEmpty) {
|
||||||
Navigator.of(context).pop(),
|
Navigator.of(context).pop(),
|
||||||
|
|
||||||
|
controller.dispose(),
|
||||||
|
remController.dispose(),
|
||||||
|
|
||||||
onFinished(selectedTourEntry, remController.text)
|
onFinished(selectedTourEntry, remController.text)
|
||||||
} else {
|
} else {
|
||||||
showAlertDialog("Fehler", "Bitte geben Sie eine Bemerkung ein")
|
showAlertDialog("Fehler", "Bitte geben Sie eine Bemerkung ein")
|
||||||
@@ -80,6 +85,9 @@ showPickupDeliveryStatusPopup(BuildContext context, TaskInfo taskInfo, Function(
|
|||||||
} else {
|
} else {
|
||||||
Navigator.of(context).pop(),
|
Navigator.of(context).pop(),
|
||||||
|
|
||||||
|
controller.dispose(),
|
||||||
|
remController.dispose(),
|
||||||
|
|
||||||
onFinished(selectedTourEntry, null)
|
onFinished(selectedTourEntry, null)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -77,7 +77,11 @@ showReceiptGiverPopup(BuildContext context, TaskInfo taskInfo, Function(String,
|
|||||||
if (textEditingController.text.isNotEmpty && selectedTourEntry != null) {
|
if (textEditingController.text.isNotEmpty && selectedTourEntry != null) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
onFinished(textEditingController.text, selectedTourEntry!);
|
final result = textEditingController.text;
|
||||||
|
textEditingController.dispose();
|
||||||
|
controller.dispose();
|
||||||
|
|
||||||
|
onFinished(result, selectedTourEntry!);
|
||||||
} else {
|
} else {
|
||||||
showAlertDialog("Fehler", "Sie müssen einen Text eingeben und ein Geschlecht auswählen");
|
showAlertDialog("Fehler", "Sie müssen einen Text eingeben und ein Geschlecht auswählen");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,10 +54,12 @@ showStationNoticePopup(BuildContext context, TaskInfo taskInfo, Function(String)
|
|||||||
autofocus: true),
|
autofocus: true),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
getDefaultButton('Übernehmen', buttonHeight: 50, () => {
|
getDefaultButton('Übernehmen', buttonHeight: 50, () {
|
||||||
Navigator.of(context).pop(),
|
final result = textEditingController.text;
|
||||||
|
textEditingController.dispose();
|
||||||
onFinished(textEditingController.text)})
|
Navigator.of(context).pop();
|
||||||
|
onFinished(result);
|
||||||
|
})
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -67,15 +67,16 @@ showZipPopup(BuildContext context, String text, String buttonText,
|
|||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
getDefaultButton(
|
getDefaultButton(
|
||||||
buttonText,
|
buttonText,
|
||||||
() => {
|
() {
|
||||||
if (_checkInput(context,
|
if (_checkInput(context,
|
||||||
textEditingController.text))
|
textEditingController.text)) {
|
||||||
{
|
final result = textEditingController.text;
|
||||||
Navigator.of(context).pop(),
|
textEditingController.dispose();
|
||||||
onFinished(
|
focusNode.dispose();
|
||||||
textEditingController.text)
|
Navigator.of(context).pop();
|
||||||
},
|
onFinished(result);
|
||||||
}),
|
}
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import 'package:stadtbote/model/chat_message.dart';
|
|||||||
import 'package:stadtbote/model/location_status.dart';
|
import 'package:stadtbote/model/location_status.dart';
|
||||||
import 'package:stadtbote/model/rank.dart';
|
import 'package:stadtbote/model/rank.dart';
|
||||||
import 'package:stadtbote/model/status.dart';
|
import 'package:stadtbote/model/status.dart';
|
||||||
|
import 'package:stadtbote/model/tour_file.dart';
|
||||||
import 'package:stadtbote/objectbox.g.dart';
|
import 'package:stadtbote/objectbox.g.dart';
|
||||||
import 'package:stadtbote/network_messages/acknowledge_job.dart';
|
import 'package:stadtbote/network_messages/acknowledge_job.dart';
|
||||||
import 'package:stadtbote/network_messages/acknowledge_message.dart';
|
import 'package:stadtbote/network_messages/acknowledge_message.dart';
|
||||||
@@ -401,8 +402,16 @@ class MqttCore with ProducerMixin, ConsumerMixin {
|
|||||||
query.close();
|
query.close();
|
||||||
|
|
||||||
if (existingJob != null) {
|
if (existingJob != null) {
|
||||||
// Update existing job
|
// Update existing job - preserve the ID
|
||||||
newJob.id = existingJob.id;
|
newJob.id = existingJob.id;
|
||||||
|
|
||||||
|
// Ensure files are updated even if the job already exists
|
||||||
|
// This handles migration from old app versions without files support
|
||||||
|
if (payloadData['files'] != null) {
|
||||||
|
newJob.files = (payloadData['files'] as List<dynamic>)
|
||||||
|
.map((e) => TourFile.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
box.put(newJob);
|
box.put(newJob);
|
||||||
@@ -754,7 +763,7 @@ class MqttCore with ProducerMixin, ConsumerMixin {
|
|||||||
|
|
||||||
void _commandLogoutReceived(Map<String, dynamic> payload) {
|
void _commandLogoutReceived(Map<String, dynamic> payload) {
|
||||||
try {
|
try {
|
||||||
String timestamp = payload['timestamp'] ?? 'Unknown timestamp';
|
String timestamp = payload['timestamp']?.toString() ?? 'Unknown timestamp';
|
||||||
Util.logInfo('Command logout received with timestamp: $timestamp');
|
Util.logInfo('Command logout received with timestamp: $timestamp');
|
||||||
|
|
||||||
// Disconnect MQTT client gracefully
|
// Disconnect MQTT client gracefully
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
import 'package:stadtbote/main.dart';
|
import 'package:stadtbote/main.dart';
|
||||||
import 'package:stadtbote/model/job.dart';
|
import 'package:stadtbote/model/job.dart';
|
||||||
import 'package:stadtbote/model/log_entry.dart';
|
import 'package:stadtbote/model/log_entry.dart';
|
||||||
|
import 'package:stadtbote/objectbox.g.dart';
|
||||||
import 'package:stadtbote/tools/constants.dart';
|
import 'package:stadtbote/tools/constants.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
@@ -97,6 +98,28 @@ class Util {
|
|||||||
|
|
||||||
final box = MyApp.objectBoxStore.box<LogEntry>();
|
final box = MyApp.objectBoxStore.box<LogEntry>();
|
||||||
box.put(logEntry);
|
box.put(logEntry);
|
||||||
|
|
||||||
|
// Cleanup old logs (delete entries older than 3 days)
|
||||||
|
_cleanupOldLogs();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _cleanupOldLogs() async {
|
||||||
|
try {
|
||||||
|
final box = MyApp.objectBoxStore.box<LogEntry>();
|
||||||
|
|
||||||
|
// Delete logs older than 3 days
|
||||||
|
final threeDaysAgo = DateTime.now().subtract(const Duration(days: 3));
|
||||||
|
final oldLogsQuery = box.query(LogEntry_.time.lessThan(threeDaysAgo.millisecondsSinceEpoch)).build();
|
||||||
|
final oldLogs = oldLogsQuery.find();
|
||||||
|
oldLogsQuery.close();
|
||||||
|
|
||||||
|
if (oldLogs.isNotEmpty) {
|
||||||
|
box.removeMany(oldLogs.map((e) => e.id).toList());
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Silent fail for cleanup - don't log to avoid infinite loop
|
||||||
|
debugPrint('Log cleanup failed: $e');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static logInfo(String message) {
|
static logInfo(String message) {
|
||||||
|
|||||||
+41
-33
@@ -5,18 +5,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d
|
sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "91.0.0"
|
version: "67.0.0"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08
|
sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.4.1"
|
version: "6.4.1"
|
||||||
animated_toggle_switch:
|
animated_toggle_switch:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -125,18 +125,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build
|
name: build
|
||||||
sha256: ce76b1d48875e3233fde17717c23d1f60a91cc631597e49a400c89b475395b1d
|
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "2.4.1"
|
||||||
build_config:
|
build_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_config
|
name: build_config
|
||||||
sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187"
|
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.1.2"
|
||||||
build_daemon:
|
build_daemon:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -149,26 +149,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
sha256: d1d57f7807debd7349b4726a19fd32ec8bc177c71ad0febf91a20f84cd2d4b46
|
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "2.4.2"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: b24597fceb695969d47025c958f3837f9f0122e237c6a22cb082a5ac66c3ca30
|
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.7.1"
|
version: "2.4.13"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
sha256: "066dda7f73d8eb48ba630a55acb50c4a84a2e6b453b1cb4567f581729e794f7b"
|
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.3.1"
|
version: "7.3.2"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -317,10 +317,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b
|
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "2.3.6"
|
||||||
device_info_plus:
|
device_info_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -776,6 +776,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.2"
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -788,10 +796,10 @@ packages:
|
|||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: json_serializable
|
name: json_serializable
|
||||||
sha256: c5b2ee75210a0f263c6c7b9eeea80553dbae96ea1bf57f02484e806a3ffdffa3
|
sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.11.2"
|
version: "6.8.0"
|
||||||
latlong2:
|
latlong2:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -916,26 +924,26 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: objectbox
|
name: objectbox
|
||||||
sha256: "3cc186749178a3556e1020c9082d0897d0f9ecbdefcc27320e65c5bc650f0e57"
|
sha256: "3d1cb5f9aa564f95c76ba251299f6cb1591c3dd8ff05fd76fa0549d899d9fe31"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.3.1"
|
version: "4.1.0"
|
||||||
objectbox_flutter_libs:
|
objectbox_flutter_libs:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: objectbox_flutter_libs
|
name: objectbox_flutter_libs
|
||||||
sha256: cd754766e04229a4f51250f121813d9a3c1a74fc21cd68e48b3c6085cbcd6c85
|
sha256: "4f54ebbd7a3b72f1a5ef4fea76cb01cc36440a4cac1f63bfb6719afba400eedb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.3.1"
|
version: "4.1.0"
|
||||||
objectbox_generator:
|
objectbox_generator:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: objectbox_generator
|
name: objectbox_generator
|
||||||
sha256: "71a3f6948e631be5c7160d512ad2a8cb7471cdbcf1731ec6baf2a794b82386d7"
|
sha256: "777924e14daf18603a023b346333acac1c4fce9fe4a715b85d98cfcc723d9a1a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.3.1"
|
version: "4.1.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1140,10 +1148,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pointycastle
|
name: pointycastle
|
||||||
sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5"
|
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0"
|
version: "3.9.1"
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -1284,10 +1292,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf_web_socket
|
name: shelf_web_socket
|
||||||
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
|
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "2.0.1"
|
||||||
signature:
|
signature:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1305,18 +1313,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_gen
|
name: source_gen
|
||||||
sha256: "7b19d6ba131c6eb98bfcbf8d56c1a7002eba438af2e7ae6f8398b2b0f4f381e3"
|
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "1.5.0"
|
||||||
source_helper:
|
source_helper:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_helper
|
name: source_helper
|
||||||
sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723"
|
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.8"
|
version: "1.3.5"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ dev_dependencies:
|
|||||||
build_runner: ^2.4.13
|
build_runner: ^2.4.13
|
||||||
objectbox_generator: ^4.0.3
|
objectbox_generator: ^4.0.3
|
||||||
json_serializable: ^6.6.2
|
json_serializable: ^6.6.2
|
||||||
|
analyzer: ^6.0.0
|
||||||
|
|
||||||
# The "flutter_lints" package below contains a set of recommended lints to
|
# The "flutter_lints" package below contains a set of recommended lints to
|
||||||
# encourage good coding practices. The lint set provided by the package is
|
# encourage good coding practices. The lint set provided by the package is
|
||||||
|
|||||||
Reference in new issue
Block a user