Newer
Older
import 'package:flutter/material.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'databaseconnector.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
final prefs = SharedPreferences.getInstance(); // Is async
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(mounted){
setState(() {
//refresh UI on update
});}
}
});
debugPrint("GPS Service is not enabled, turn on GPS location");
setState(() {
//refresh the UI
});}
@override
try {
streamHandler.cancel();
debugPrint('Cancel location stream');
}catch(e){
debugPrint('Canceling location stream failed');
}
/*Async update current event configuration to shared preferences*/
final EventStoreInstance event = EventStoreInstance();
event.storeToSharedPrefs();
super.dispose();
}
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
138
139
140
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){
if (RegExp(r'^[a-z A-Z . \- 0-9 , ( ) + - _ :]+$').hasMatch(
if (RegExp(r'^[a-z A-Z . \- 0-9 , ( ) + - _ :]+$').hasMatch(
event.currentEvent.description) || event.currentEvent.description == '') {
if(_validateLatitude(event.currentEvent.latitude)){
if(_validateLongitude(event.currentEvent.longitude)){
if(_validateElevation(event.currentEvent.elevation)){
}
}
return false;
}
bool _addButtonStatus(){
if((_validateInput() == true) && (_addButtonEnabled == true)){
return true;
}
return false;
}
Future<void> _storeCurrentEvent() async {
final EventStoreInstance event = EventStoreInstance();
final ConfigurationStoreInstance configuration = ConfigurationStoreInstance();
event.currentEvent.typeId = configuration.getEventIdFromName(event.currentEvent.type);
event.currentEvent.status = "PENDING";
await database.addEvent(event.currentEvent); //TODO: display feedback after this async function
HapticFeedback.vibrate(); //Feedback that adding event succeeded
_addButtonEnabled = true; //Activate button for add more events
setState(() {});
//Update timestamp in UI
var date = DateTime.now().toUtc();
var isoDate = date.toIso8601String();
event.currentEvent.startDate = isoDate;
event.currentEvent.endDate = isoDate;
@override
Widget build(BuildContext context) {
/* Get singletons to access relevant data here.*/
final ConfigurationStoreInstance configuration = ConfigurationStoreInstance();
// Update current event coordinates from GNSS stream
eventStore.currentEvent.latitude = lat;
eventStore.currentEvent.longitude = long;
eventStore.currentEvent.elevation = alt;
var isoDate = date.toIso8601String();
eventStore.currentEvent.startDate = isoDate;
eventStore.currentEvent.endDate = isoDate;
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")),
body: SingleChildScrollView(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 5.0),
child:
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
const SizedBox(height: 15.0),
TextFormField(
autovalidateMode: AutovalidateMode.always,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Label',
),
onChanged: (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(
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) {
),
const SizedBox(height: 15.0),
DropdownButtonFormField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
items:
configuration.devices.map((Device device) {
return DropdownMenuItem(
value: device.urn,
child: Text(device.urn),
);
}).toList(),
onChanged: (value) {
eventStore.currentEvent.urn = value.toString();
eventStore.currentEvent.urnId =
configuration.getDeviceIdFromUrn(value.toString());
}
),
const SizedBox(height: 15.0),
TextFormField(
autovalidateMode: AutovalidateMode.always,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Description'
setState(() {});
},
validator: (value) {
if (!RegExp(r'^[a-z A-Z . \- 0-9 , ( ) + - _ :]+$').hasMatch(
value!)) {
if(value == ''){
return null; //An empty description is also allowed.
}
return "Only: a-z , A-Z , _ , 0-9 , ,(Comma) , ( , ) , + , - , . , :";
} else {
return null; // Entered Text is valid
}
},
),
const SizedBox(height: 15.0),
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(child:
TextFormField(
controller: TextEditingController(
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.
var isoDate = date.toIso8601String();
eventStore.currentEvent.startDate = isoDate;
eventStore.currentEvent.endDate = isoDate;
debugPrint('Date set to : $isoDate');
setState(() {});
},
currentTime: DateTime.now().toUtc(),
locale: LocaleType.en);
},
),
),
ElevatedButton(
onPressed: () {
var date = DateTime.now().toUtc();
var isoDate = date.toIso8601String();
eventStore.currentEvent.startDate = isoDate;
eventStore.currentEvent.endDate = isoDate;
setState(() {});
},
child: const Text('Now'),
),
]
const SizedBox(height: 15.0),
TextFormField(
keyboardType: TextInputType.number,
autovalidateMode: AutovalidateMode.onUserInteraction,
controller: TextEditingController(
decoration: const InputDecoration(
labelText: 'Latitude',
border: OutlineInputBorder(),
),
onChanged: (value) {
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(
keyboardType: TextInputType.number,
autovalidateMode: AutovalidateMode.onUserInteraction,
controller: TextEditingController(
decoration: const InputDecoration(
labelText: 'Longitude',
border: OutlineInputBorder(),
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(
keyboardType: TextInputType.number,
autovalidateMode: AutovalidateMode.onUserInteraction,
controller: TextEditingController(
decoration: const InputDecoration(
labelText: 'Elevation',
border: OutlineInputBorder(),
),
onChanged: (value) {
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(
onChanged: (value) {
eventStore.gnssSync = value;
debugPrint('Switched to:' + eventStore.gnssSync.toString());
setState(() {
//refresh the UI
});
},
const SizedBox(width: 50),
Container(
margin: const EdgeInsets.symmetric(vertical: 10.0),
FloatingActionButton(
onPressed: () {
_addButtonEnabled = false; //Disable button until event is stored
},
tooltip: 'Add Event',
child: const Icon(Icons.check),
) :
FloatingActionButton(
enableFeedback: false,
backgroundColor: Colors.grey,
onPressed: () {
//child: const Icon(Icons.check),
)
const SizedBox(width: 5.0),
],
),
);
}else {
return Scaffold(
appBar: AppBar(title: const Text("Add Event")),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Container(
margin: const EdgeInsets.all(10.0),
child:const Text(
'Check Configuration Page for initial setup!',
style: TextStyle(fontSize: 20)
),
),
],
),
);
}