import 'package:flutter/material.dart'; import 'datamodel.dart'; import 'sensorconnector.dart'; import 'databaseconnector.dart'; //import 'package:getwidget/getwidget.dart'; class ViewEvents extends StatefulWidget { const ViewEvents({Key? key}) : super(key: key); @override _ViewEvents createState() => _ViewEvents(); } class _ViewEvents extends State<ViewEvents> { // Get singleton to access locally stored events: final EventStoreInstance _events = EventStoreInstance(); String _status = ''; TextStyle _statusStyle = const TextStyle(color: Colors.black); String _appBarText = ''; int _selectedRow = -1; var database = DatabaseInstance(); List<Event> _localEvents = []; Widget _buildSyncDialog(BuildContext context) { SensorConnector sensorConnector = SensorConnector(); return WillPopScope( onWillPop: () async => false, child: AlertDialog( content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ const Text('Syncing Events...'), //TODO: show progress bar here., Horizontal progress bar? //TODO: show text XX pending events. count down //GFProgressBar( // percentage: 0.7, // backgroundColor : Colors.pink, // progressBarColor: Colors.red, //), ], ), actions: <Widget>[ TextButton( onPressed: () { //Start sync process but only once. }, child: const Text('Start'), //Change text to Syncing... during Sync process. ), TextButton( onPressed: () { // Depending on state. Cancel or close. TODO: implement statemachine. Navigator.of(context).pop(); }, child: const Text('Close'), // Change to Cancel during Sync process. ), ], ) ); } Future<void> fetchEventsFromDb() async{ final EventStoreInstance event = EventStoreInstance(); int pendingEvents = 0; if (event.showAllEvents == false) { _appBarText = 'Pending Events'; //Get only pending events _localEvents = await database.getPendingEvents(); pendingEvents = _localEvents.length; }else { _appBarText = 'All Events'; _localEvents = await database.getEvents(); pendingEvents = await database.getPendingEventCnt(); } _status = pendingEvents.toString() + ' event(s) pending'; _statusStyle = const TextStyle(color: Colors.black); setState(() {}); //Got events from database, so update UI } @override void initState() { super.initState(); fetchEventsFromDb(); } @override void dispose() async { /*Async update current configuration to shared preferences*/ final EventStoreInstance event = EventStoreInstance(); event.storeToSharedPrefs(); super.dispose(); } Future<void> syncEvents() async { final ConfigurationStoreInstance configuration = ConfigurationStoreInstance(); SensorConnector connection = SensorConnector(); List<Event> events = await database.getPendingEvents(); int syncCounter = events.length; debugPrint('Pending Events'); for (var event in events){ debugPrint(event.toString()); } if(syncCounter > 0) { try { String? token = await connection.getAuthToken( configuration.loginInformation.mail, configuration.loginInformation.password); var index = 0; for (var event in events) { debugPrint('Idx: ' + index.toString() + ' ' + event.toSensorJson().toString()); index++; if (await connection.putEvent(event, token) == true) { //Event has been posted to Sensor. syncCounter--; debugPrint( 'put success, remaining events: ' + syncCounter.toString()); event.status = 'EXPORTED'; //Update event to export only once database.updateEvent(event); //Update Event as exported in SQL Database await fetchEventsFromDb(); //update view list //TODO: this is bad for the sync performance! setState(() {}); } else { throw Exception('Sync for ' + event.urn + 'failed'); } } _status = 'export success'; _statusStyle = const TextStyle(color: Colors.green); } catch (e) { String errorText = e.toString(); errorText = errorText.substring(10, errorText.length); //Remove 'Exception' from string. _status = errorText; _statusStyle = const TextStyle(color: Colors.red); setState(() {}); } }else{ debugPrint('No PENDING event(s)'); } } @override Widget build(BuildContext context) { final EventStoreInstance event = EventStoreInstance(); return Scaffold( appBar: AppBar( title: const Text("View & Sync"), actions: <Widget>[ Text(_appBarText, style: const TextStyle(fontStyle: FontStyle.italic),), Switch( //Enable showing all or only pending events. Default is to show only pending events value: event.showAllEvents, onChanged: (value) { event.showAllEvents = value; fetchEventsFromDb(); setState(() {}); }), ], ), body: Container( margin: const EdgeInsets.symmetric(horizontal: 5.0), child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: SingleChildScrollView( scrollDirection: Axis.vertical, child: DataTable( columns: const <DataColumn>[ DataColumn( label: Text( '(ID) URN', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Label', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Type', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Description', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'StartDate', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'EndDate', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Latitude', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Longitude', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Elevation', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Status', style: TextStyle(fontStyle: FontStyle.italic), ), ), ], rows: <DataRow>[ //for (var event in _localEvents) for (var i = 0; i < _localEvents.length; i++) DataRow( selected: i == _selectedRow, onLongPress: () { debugPrint("Selected Row: " + i.toString()); //TODO: open widget to edit the rows event values. Provide "cancel" and "update" functionality //TODO: display "Exported Events are read only, Edit via Sensor.awi.de" when trying to edit. debugPrint(_localEvents[i].toString()); //Navigator.pushNamed(context, '/second', arguments: {'event': _localEvents[i]},); // TODO: create an edit widget. As Add Event, GNSS editing always enabled, No GNSS switch, Now Button? /* //How to get the argument. @override Widget build(BuildContext context) { final arguments = (ModalRoute.of(context)?.settings.arguments ?? <String, dynamic>{}) as Map; print(arguments['exampleArgument']); return Scaffold(...); } */ }, cells: <DataCell>[ //DataCell(Text(_localEvents[i].urnId.toString())), DataCell(Text('('+_localEvents[i].urnId.toString()+') ' + _localEvents[i].urn)), DataCell( Text(_localEvents[i].label), ), DataCell(Text(_localEvents[i].type)), DataCell(Text(_localEvents[i].description)), //Do not show microseconds DataCell(Text(_localEvents[i].startDate.substring(0, 19) + 'Z')), DataCell(Text(_localEvents[i].endDate.substring(0, 19) + 'Z')), DataCell(Text(_localEvents[i].latitude)), DataCell(Text(_localEvents[i].longitude)), DataCell(Text(_localEvents[i].elevation == '' ? '' : double.parse(_localEvents[i].elevation).toStringAsFixed(2))), DataCell(Text(_localEvents[i].status)), ], ), ], ), ), ), ), bottomNavigationBar: Container( margin: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 5.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const SizedBox(width: 1), Text(_status, style: _statusStyle), FloatingActionButton.extended( heroTag: null, tooltip: 'Upload Events', icon: null, label: const Text('Sync'), onPressed: () { syncEvents(); //showDialog( // barrierDismissible: false, // context: context, // builder: (BuildContext context) => _buildSyncDialog(context), //); }, ), ], ), ), ); } } //TODO: allow editing fields here.