Newer
Older
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'dart:async';
import 'package:geolocator/geolocator.dart';
const AddEvent({Key? key}) : super(key: key);
@override
State<AddEvent> createState() => _AddEventPageState();
}
class _AddEventPageState extends State<AddEvent> {
late String long = "";
late String lat = "";
late String alt = "";
late StreamSubscription<Position> streamHandler; //For canceling GNSS stream on dispose
Future startGNSS() async {
debugPrint("Check Location Permission");
bool serviceStatus = false;
bool hasPermission = false;
serviceStatus = await Geolocator.isLocationServiceEnabled();
if(serviceStatus){
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
}else if(permission == LocationPermission.deniedForever){
debugPrint('Location permissions are permanently denied');
if(hasPermission){
debugPrint('Location permissions granted');
if(mounted){
setState(() {
//refresh the UI
});}
debugPrint('Starting location stream');
streamHandler = Geolocator.getPositionStream(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 0,
)).listen((Position position) {
debugPrint('Get Location: Lat:' + position.latitude.toString() +
' Long:' + position.longitude.toString() +
' Alt:' + position.altitude.toString());
long = position.longitude.toString();
lat = position.latitude.toString();
alt = position.altitude.toString();
accuracy = position.accuracy;
if (syncGNSSData == true) {
if(mounted){
setState(() {
//refresh UI on update
});}
}
});
debugPrint("GPS Service is not enabled, turn on GPS location");
setState(() {
//refresh the UI
});}
@override
void dispose() {
try {
streamHandler.cancel();
debugPrint('Cancel location stream');
}catch(e){
debugPrint('Canceling location stream failed');
}
super.dispose();
}
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
bool _validateLatitude(value){
if (value == ""){
return true; //Empty string is valid
}
var number = num.tryParse(value);
if(number != null){
if (number >= -90.0 && number <= 90.0){
return true; // Latitude valid
}
}
return false;
}
bool _validateLongitude(value){
if (value == ""){
return true; //Empty string is valid
}
var number = num.tryParse(value);
if(number != null){
if (number >= -180.0 && number <= 180.0){
return true; // Longitude valid
}
}
return false;
}
bool _validateElevation(value){
if (value == ""){
return true; //Empty string is valid
}
var number = num.tryParse(value);
if(number != null){
return true; // Any numerical value is valid for elevation
}
return false;
}
bool _validateInput(){
final EventStoreInstance eventsStore = EventStoreInstance();
if (RegExp(r'^[a-z A-Z . \- 0-9 , ( ) + - _ :]+$').hasMatch(
eventsStore.currentEvent.label)) {
if (RegExp(r'^[a-z A-Z . \- 0-9 , ( ) + - _ :]+$').hasMatch(
eventsStore.currentEvent.description)) {
if(_validateLatitude(eventsStore.currentEvent.latitude)){
if(_validateLongitude(eventsStore.currentEvent.longitude)){
if(_validateElevation(eventsStore.currentEvent.elevation)){
debugPrint('All inputs valid');
return true;
}
}
}
}
}
return false;
}
final EventStoreInstance eventsStore = EventStoreInstance();
eventsStore.currentEvent.status = "PENDING";
eventsStore.events.add(
eventsStore.currentEvent.id,
eventsStore.currentEvent.urn,
eventsStore.currentEvent.label,
eventsStore.currentEvent.type,
eventsStore.currentEvent.description,
eventsStore.currentEvent.status,
eventsStore.currentEvent.startDate,
eventsStore.currentEvent.endDate,
eventsStore.currentEvent.latitude,
eventsStore.currentEvent.longitude,
eventsStore.currentEvent.elevation
//TODO: add field validators for freetext fields. Check allowed characters in sensor.awi.de
@override
Widget build(BuildContext context) {
/* Get singletons to access relevant data here.*/
final EventStoreInstance eventsStore = EventStoreInstance();
final ConfigurationStoreInstance configuration = ConfigurationStoreInstance();
// Update current event coordinates from GNSS stream
eventsStore.currentEvent.latitude = lat;
eventsStore.currentEvent.longitude = long;
eventsStore.currentEvent.elevation = alt;
var date = DateTime.now().toUtc();
eventsStore.currentEvent.startDate = '$date';
eventsStore.currentEvent.endDate = '$date';
if(accuracy == 0.0){
gnssStatusText = "No-Fix";
else{
gnssStatusText = "Precision: "+ accuracy.toStringAsFixed(2) +"m";
}
gnssStatusText = "GNSS Disabled"; // Just display existing event coordinates
if (configuration.initialized == true) {
return Scaffold(
appBar: AppBar(title: const Text("Add Event")),
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
body: SingleChildScrollView(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 5.0),
child:
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
const SizedBox(height: 15.0),
TextFormField(
initialValue: eventsStore.currentEvent.label,
autovalidateMode: AutovalidateMode.always,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Label',
),
onChanged: (value){
eventsStore.currentEvent.label = value;
setState(() {});
},
validator: (value) {
if (!RegExp(r'^[a-z A-Z . \- 0-9 , ( ) + - _ :]+$').hasMatch(
value!)) {
return "Only: a-z , A-Z , _ , 0-9 , ,(Comma) , ( , ) , + , - , . , :";
} else {
return null; // Entered Text is valid
}
},
),
const SizedBox(height: 15.0),
DropdownButtonFormField(
value: eventsStore.currentEvent.type,
isExpanded: true,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Event Type',
),
items:
configuration.eventTypes.map((EventType event) {
return DropdownMenuItem(
value: event.name,
child: Text(event.name),
);
}).toList(),
onChanged: (value) {
eventsStore.currentEvent.type = value.toString();
}
),
const SizedBox(height: 15.0),
DropdownButtonFormField(
value: eventsStore.currentEvent.urn,
isExpanded: true,
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
items:
configuration.devices.map((Device device) {
return DropdownMenuItem(
value: device.urn,
child: Text(device.urn),
);
}).toList(),
onChanged: (value) {
eventsStore.currentEvent.urn = value.toString();
eventsStore.currentEvent.id =
configuration.getDeviceIdFromUrn(value.toString());
}
),
const SizedBox(height: 15.0),
TextFormField(
initialValue: eventsStore.currentEvent.description,
autovalidateMode: AutovalidateMode.always,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Description'
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
onChanged: (value){
eventsStore.currentEvent.description = value;
setState(() {});
},
validator: (value) {
if (!RegExp(r'^[a-z A-Z . \- 0-9 , ( ) + - _ :]+$').hasMatch(
value!)) {
return "Only: a-z , A-Z , _ , 0-9 , ,(Comma) , ( , ) , + , - , . , :";
} else {
//eventsStore.currentEvent.label = value;
return null; // Entered Text is valid
}
},
),
const SizedBox(height: 15.0),
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(child:
TextFormField(
controller: TextEditingController(
text: eventsStore.currentEvent.startDate),
readOnly: true,
decoration: const InputDecoration(
labelText: 'Timestamp',
//helperText: '', //Adds some space below field
border: OutlineInputBorder(),
),
onTap: () {
DatePicker.showDateTimePicker(context,
showTitleActions: true,
onConfirm: (date) {
//Only one field for start and end date.
eventsStore.currentEvent.startDate = '$date';
eventsStore.currentEvent.endDate = '$date';
debugPrint('Date set to : $date');
setState(() {});
},
currentTime: DateTime.now().toUtc(),
locale: LocaleType.en);
},
),
),
ElevatedButton(
onPressed: () {
var date = DateTime.now().toUtc();
eventsStore.currentEvent.startDate = '$date';
eventsStore.currentEvent.endDate = '$date';
debugPrint('Date set to : ' +
eventsStore.currentEvent.endDate.toString());
setState(() {});
},
child: const Text('Now'),
),
]
const SizedBox(height: 15.0),
TextFormField(
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
readOnly: false,
enabled: !syncGNSSData,
keyboardType: TextInputType.number,
autovalidateMode: AutovalidateMode.onUserInteraction,
controller: TextEditingController(
text: eventsStore.currentEvent.latitude.toString()),
decoration: const InputDecoration(
labelText: 'Latitude',
border: OutlineInputBorder(),
),
onChanged: (value) {
eventsStore.currentEvent.latitude = value;
},
onFieldSubmitted: (value){
if (!syncGNSSData) {
setState(() {});
}
},
validator: (value) {
if (value == "") {
return null; // Empty value is allowed
}
final number = num.tryParse(value!);
if (number != null){
if (number >= -90.0 && number <= 90.0){
return null; // Latitude valid
}
),
const SizedBox(height: 15.0),
TextFormField(
readOnly: false,
enabled: !syncGNSSData,
keyboardType: TextInputType.number,
autovalidateMode: AutovalidateMode.onUserInteraction,
controller: TextEditingController(
text: eventsStore.currentEvent.longitude.toString()),
decoration: const InputDecoration(
labelText: 'Longitude',
border: OutlineInputBorder(),
),
onChanged: (value) {
eventsStore.currentEvent.longitude = value;
},
onFieldSubmitted: (value){
if (!syncGNSSData) {
setState(() {});
}
},
validator: (value) {
if (value == "") {
return null; // Empty value is allowed
final number = num.tryParse(value!);
if (number != null){
if (number >= -180.0 && number <= 180.0){
return null; // Longitude valid
}
}
return "-180 => Longitude <= +180";
},
const SizedBox(height: 15.0),
TextFormField(
readOnly: false,
enabled: !syncGNSSData,
keyboardType: TextInputType.number,
autovalidateMode: AutovalidateMode.onUserInteraction,
controller: TextEditingController(
text: eventsStore.currentEvent.elevation.toString()),
decoration: const InputDecoration(
labelText: 'Elevation',
border: OutlineInputBorder(),
),
onChanged: (value) {
eventsStore.currentEvent.elevation = value;
},
onFieldSubmitted: (value){
if (!syncGNSSData) {
setState(() {});
}
},
validator: (value) {
if (value == "") {
return null; // Empty value is allowed
final number = num.tryParse(value!);
if (number != null){
return null; // Elevation valid
}
return "Only numerical values for elevation in [m]";
},
bottomNavigationBar: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(gnssStatusText),
const SizedBox(width: 10),
Switch(
value: syncGNSSData,
onChanged: (value) {
syncGNSSData = value;
debugPrint('Switched to:' + syncGNSSData.toString());
setState(() {
//refresh the UI
});
},
const SizedBox(width: 50),
Container(
margin: const EdgeInsets.symmetric(vertical: 10.0),
child: _validateInput() ?
FloatingActionButton(
onPressed: () {
if (_validateInput()) {
_storeCurrentEvent();
HapticFeedback.vibrate();
//Update time for next event
var date = DateTime.now().toUtc();
eventsStore.currentEvent.startDate = '$date';
eventsStore.currentEvent.endDate = '$date';
},
tooltip: 'Add Event',
child: const Icon(Icons.check),
) :
FloatingActionButton(
enableFeedback: false,
backgroundColor: Colors.grey,
onPressed: () {
},
tooltip: 'Correct Inputs before adding the Event',
//child: const Icon(Icons.check),
)
],
),
);
}else {
return Scaffold(
appBar: AppBar(title: const Text("Add Event")),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.end,
children: const <Widget>[
Text(
' Visit Configuration first.',
style: TextStyle(fontSize: 20)
),
],
),
);
}