Implement map
This commit is contained in:
parent
69e82fcf4c
commit
a5fa4e0309
7 changed files with 184 additions and 57 deletions
17
README.md
17
README.md
|
@ -24,16 +24,17 @@ The app does not store data locally, due to the way android permissions function
|
||||||
|
|
||||||
# Implementation
|
# Implementation
|
||||||
|
|
||||||
- [ ] Basic UI
|
- [x] Basic UI
|
||||||
- [x] Permissions
|
- [x] Permissions
|
||||||
- [x] Automatic updates
|
- [x] Automatic updates
|
||||||
- [ ] GPS Tracking
|
- [x] GPS Tracking
|
||||||
- [ ] Formatting GPS on a viewable map
|
- [x] Formatting GPS on a viewable map
|
||||||
- [ ] Track driving hours
|
- [x] Track driving hours
|
||||||
- [ ] Track trips
|
- [x] Track trips
|
||||||
- [ ] Track stops/deliveries
|
- [x] Track stops/deliveries
|
||||||
- [ ] Track trip base pay
|
- [x] Track trip base pay
|
||||||
- [ ] Track each delivery's tips
|
- [x] Track each delivery's tips
|
||||||
|
- [ ] Map marker for each stop/delivery with text saying "Trip #X/DropOff #X\nBase Pay: $$$; Tip: $$$"
|
||||||
- [ ] Basic version of the app in readonly mode when deployed on a web server.
|
- [ ] Basic version of the app in readonly mode when deployed on a web server.
|
||||||
- [ ] Backend server
|
- [ ] Backend server
|
||||||
- [ ] PHP?
|
- [ ] PHP?
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
"alpha": "1.0.0-dev.7"
|
"alpha": "1.0.0-dev.8"
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,12 @@ import 'package:geolocator/geolocator.dart';
|
||||||
class TTConsts {
|
class TTConsts {
|
||||||
static get UPDATE_URL =>
|
static get UPDATE_URL =>
|
||||||
"https://git.zontreck.com/AriasCreations/TimeTracker/raw/branch/main/latest-releases.json";
|
"https://git.zontreck.com/AriasCreations/TimeTracker/raw/branch/main/latest-releases.json";
|
||||||
static const VERSION = "1.0.0-dev.7";
|
static const VERSION = "1.0.0-dev.8";
|
||||||
static bool UPDATE_AVAILABLE = false;
|
static bool UPDATE_AVAILABLE = false;
|
||||||
static UpdateChannel UPDATE_CHANNEL = UpdateChannel.alpha;
|
static UpdateChannel UPDATE_CHANNEL = UpdateChannel.alpha;
|
||||||
static final LocationSettings LOCATION_SETTINGS = LocationSettings(
|
static final LocationSettings LOCATION_SETTINGS = LocationSettings(
|
||||||
accuracy: LocationAccuracy.bestForNavigation,
|
accuracy: LocationAccuracy.bestForNavigation,
|
||||||
|
distanceFilter: 50,
|
||||||
);
|
);
|
||||||
|
|
||||||
static Future<void> checkUpdate() async {
|
static Future<void> checkUpdate() async {
|
||||||
|
|
|
@ -53,6 +53,16 @@ class _HomePageState extends State<HomePage> {
|
||||||
},
|
},
|
||||||
leading: Icon(Icons.update),
|
leading: Icon(Icons.update),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text("Trip Map"),
|
||||||
|
leading: Icon(Icons.map),
|
||||||
|
subtitle: Text(
|
||||||
|
"View a map of the route\n(NOTE: This is not live, and reflects the current state as of the time the map is opened.)",
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
await Navigator.pushNamed(context, "/map");
|
||||||
|
},
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text("RESET APP SESSION"),
|
title: Text("RESET APP SESSION"),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
|
@ -72,6 +82,10 @@ class _HomePageState extends State<HomePage> {
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
Text(
|
||||||
|
"Hit engage when you are ready to go online and start tracking location data, and trips.",
|
||||||
|
style: TextStyle(fontSize: 18),
|
||||||
|
),
|
||||||
if (!SessionData.IsOnTheClock)
|
if (!SessionData.IsOnTheClock)
|
||||||
Center(
|
Center(
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
|
@ -129,27 +143,6 @@ class _HomePageState extends State<HomePage> {
|
||||||
},
|
},
|
||||||
child: Text("END TRIP"),
|
child: Text("END TRIP"),
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () async {
|
|
||||||
if (SessionData.currentTrip != null ||
|
|
||||||
SessionData.currentDelivery != null) {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (build) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text("Cannot end work day"),
|
|
||||||
content: Text(
|
|
||||||
"You must end the trip and any delivery before you can fully end the work day.",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
SessionData.Logout();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text("End work day"),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -185,6 +178,57 @@ class _HomePageState extends State<HomePage> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget GetNonTripWidgets() {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
var reply = await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (builder) {
|
||||||
|
return InputPrompt(
|
||||||
|
title: "What is the base pay?",
|
||||||
|
prompt: "Enter the base pay amount below.",
|
||||||
|
type: InputPromptType.Number,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (reply == null || reply == "") reply = "0";
|
||||||
|
|
||||||
|
double basePay = double.parse(reply as String);
|
||||||
|
SessionData.GetNewTrip(basePay: basePay);
|
||||||
|
SessionData.GetNewDelivery();
|
||||||
|
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
child: Text("Start New Trip"),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (SessionData.currentTrip != null ||
|
||||||
|
SessionData.currentDelivery != null) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (build) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text("Cannot end work day"),
|
||||||
|
content: Text(
|
||||||
|
"You must end the trip and any delivery before you can fully end the work day.",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
SessionData.Logout();
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text("End work day"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget GetLoggedInWidgets() {
|
Widget GetLoggedInWidgets() {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
|
@ -194,29 +238,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
),
|
),
|
||||||
if (SessionData.currentTrip != null) GetTripWidgets(),
|
if (SessionData.currentTrip != null) GetTripWidgets(),
|
||||||
if (SessionData.currentDelivery != null) GetDeliveryWidgets(),
|
if (SessionData.currentDelivery != null) GetDeliveryWidgets(),
|
||||||
if (SessionData.currentTrip == null)
|
if (SessionData.currentTrip == null) GetNonTripWidgets(),
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () async {
|
|
||||||
var reply = await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (builder) {
|
|
||||||
return InputPrompt(
|
|
||||||
title: "What is the base pay?",
|
|
||||||
prompt: "Enter the base pay amount below.",
|
|
||||||
type: InputPromptType.Number,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (reply == null || reply == "") reply = "0";
|
|
||||||
|
|
||||||
double basePay = double.parse(reply as String);
|
|
||||||
SessionData.GetNewTrip(basePay: basePay);
|
|
||||||
SessionData.GetNewDelivery();
|
|
||||||
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
child: Text("Start New Trip"),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:timetrack/pages/HomePage.dart';
|
import 'package:timetrack/pages/HomePage.dart';
|
||||||
|
import 'package:timetrack/pages/MapPage.dart';
|
||||||
import 'package:timetrack/pages/UpdateSettings.dart';
|
import 'package:timetrack/pages/UpdateSettings.dart';
|
||||||
|
|
||||||
class MainApp extends StatefulWidget {
|
class MainApp extends StatefulWidget {
|
||||||
|
@ -21,7 +22,11 @@ class MainAppState extends State<MainApp> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: "Time Tracker",
|
title: "Time Tracker",
|
||||||
routes: {"/": (ctx) => HomePage(), "/upd": (ctx) => UpdateSettingsPage()},
|
routes: {
|
||||||
|
"/": (ctx) => HomePage(),
|
||||||
|
"/upd": (ctx) => UpdateSettingsPage(),
|
||||||
|
"/map": (ctx) => MapPage(),
|
||||||
|
},
|
||||||
theme: ThemeData.dark(),
|
theme: ThemeData.dark(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
97
lib/pages/MapPage.dart
Normal file
97
lib/pages/MapPage.dart
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
|
import 'package:latlong2/latlong.dart';
|
||||||
|
import 'package:libacflutter/Constants.dart';
|
||||||
|
import 'package:timetrack/data.dart';
|
||||||
|
|
||||||
|
class MapPage extends StatefulWidget {
|
||||||
|
MapPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() {
|
||||||
|
return _MapPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MapPage extends State<MapPage> {
|
||||||
|
MapController controller = MapController();
|
||||||
|
LatLng initialPosition = LatLng(0, 0);
|
||||||
|
List<LatLng> PointMap = [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
PointMap = [];
|
||||||
|
|
||||||
|
var firstPos = SessionData.positions[0];
|
||||||
|
initialPosition = LatLng(firstPos.latitude, firstPos.longitude);
|
||||||
|
|
||||||
|
for (var position in SessionData.positions) {
|
||||||
|
PointMap.add(LatLng(position.latitude, position.longitude));
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {});
|
||||||
|
super.didChangeDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
print("Map page disposed");
|
||||||
|
controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text("Time Tracker - Map View"),
|
||||||
|
backgroundColor: LibACFlutterConstants.TITLEBAR_COLOR,
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
didChangeDependencies();
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.refresh),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: GestureDetector(
|
||||||
|
onTap: FocusScope.of(context).unfocus,
|
||||||
|
child: SafeArea(
|
||||||
|
child: FlutterMap(
|
||||||
|
mapController: controller,
|
||||||
|
options: MapOptions(
|
||||||
|
minZoom: 1,
|
||||||
|
maxZoom: 30,
|
||||||
|
initialZoom: 15,
|
||||||
|
initialCenter: initialPosition,
|
||||||
|
keepAlive: false,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
TileLayer(
|
||||||
|
urlTemplate: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||||
|
userAgentPackageName: "dev.zontreck.timetrack",
|
||||||
|
),
|
||||||
|
PolylineLayer(
|
||||||
|
polylines: [
|
||||||
|
Polyline(
|
||||||
|
points: PointMap,
|
||||||
|
color: Colors.blue,
|
||||||
|
borderStrokeWidth: 3,
|
||||||
|
borderColor: Colors.blue,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
RichAttributionWidget(
|
||||||
|
attributions: [
|
||||||
|
TextSourceAttribution('OpenStreetMap contributors'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.0.0-dev.7
|
version: 1.0.0-dev.8
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.7.2
|
sdk: ^3.7.2
|
||||||
|
@ -43,7 +43,8 @@ dependencies:
|
||||||
dio: ^5.8.0+1
|
dio: ^5.8.0+1
|
||||||
ota_update: ^7.0.1
|
ota_update: ^7.0.1
|
||||||
geolocator: ^14.0.0
|
geolocator: ^14.0.0
|
||||||
free_map: ^2.0.3
|
flutter_map: ^8.1.1
|
||||||
|
latlong2: ^0.9.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue