diff --git a/trackingservice/sample-env.txt b/trackingservice/sample-env.txt
index d4a61d34..04fe617d 100644
--- a/trackingservice/sample-env.txt
+++ b/trackingservice/sample-env.txt
@@ -1,5 +1,5 @@
DB_JDBC_URL=jdbc:postgresql://db-postgresql.com:25060/defaultdb
DB_USER=doadmin
DB_PASSWORD=AVNS_VOzqloso05
-DIGIT_FSM_URL=https://unified-dev.digit.org/fsm/v1/
-DIGIT_VEHICLE_TRIP_URL=https://unified-dev.digit.org/vehicle/trip/v1/
\ No newline at end of file
+DIGIT_FSM_URL=https://fsm-demo.digit.org/fsm/v1
+DIGIT_VEHICLE_TRIP_URL=https://fsm-demo.digit.org/vehicle/trip/v1/
diff --git a/trackingservice/src/main/java/org/digit/tracking/util/Constants.java b/trackingservice/src/main/java/org/digit/tracking/util/Constants.java
index 36dc22f1..a9e08d6a 100644
--- a/trackingservice/src/main/java/org/digit/tracking/util/Constants.java
+++ b/trackingservice/src/main/java/org/digit/tracking/util/Constants.java
@@ -14,9 +14,9 @@ public class Constants {
final public static int POI_MATCH_THRESHOLD_METERS = 100;
final public static String MONITORING_USER_ID = "MonitoringService";
@Value("${DIGIT_FSM_URL}")
- final public static String DIGIT_FSM_URL = "https://unified-dev.digit.org/fsm/v1/";
+ final public static String DIGIT_FSM_URL = "https://fsm-demo.digit.org/fsm/v1";
@Value("${DIGIT_VEHICLE_TRIP_URL}")
- final public static String DIGIT_VEHICLE_TRIP_URL = "https://unified-dev.digit.org/vehicle/trip/v1/";
+ final public static String DIGIT_VEHICLE_TRIP_URL = "https://fsm-demo.digit.org/vehicle/trip/v1";
final public static String ILLEGAL_DUMP_YARD_STOPPAGE_CODE = "Stoppage";
final public static int ILLEGAL_DUMP_YARD_STOPPAGE_THRESHOLD = 5;
final public static int TRIP_PROGRESS_FETCH_LIMIT = 1000;
diff --git a/vehicle-tracker/map_web_app/pubspec.lock b/vehicle-tracker/map_web_app/pubspec.lock
index bfb960e7..bbe90d0f 100644
--- a/vehicle-tracker/map_web_app/pubspec.lock
+++ b/vehicle-tracker/map_web_app/pubspec.lock
@@ -149,10 +149,10 @@ packages:
dependency: transitive
description:
name: collection
- sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
+ sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
url: "https://pub.dev"
source: hosted
- version: "1.18.0"
+ version: "1.17.1"
convert:
dependency: transitive
description:
@@ -188,10 +188,9 @@ packages:
digit_components:
dependency: "direct main"
description:
- name: digit_components
- sha256: "261f4407e5fe2ab3be3800dfbdc69112b632baa90f87c0e66b5c93f12e10df39"
- url: "https://pub.dev"
- source: hosted
+ path: "../packages/digit_components"
+ relative: true
+ source: path
version: "0.0.1+8"
fake_async:
dependency: transitive
@@ -417,10 +416,10 @@ packages:
dependency: transitive
description:
name: intl
- sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
+ sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6
url: "https://pub.dev"
source: hosted
- version: "0.18.1"
+ version: "0.18.0"
io:
dependency: transitive
description:
@@ -513,26 +512,26 @@ packages:
dependency: transitive
description:
name: matcher
- sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
+ sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
url: "https://pub.dev"
source: hosted
- version: "0.12.16"
+ version: "0.12.15"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
- sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
+ sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev"
source: hosted
- version: "0.5.0"
+ version: "0.2.0"
meta:
dependency: transitive
description:
name: meta
- sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
+ sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
url: "https://pub.dev"
source: hosted
- version: "1.10.0"
+ version: "1.9.1"
mgrs_dart:
dependency: transitive
description:
@@ -702,26 +701,26 @@ packages:
dependency: transitive
description:
name: source_span
- sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
+ sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
url: "https://pub.dev"
source: hosted
- version: "1.10.0"
+ version: "1.9.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
- sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
+ sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
url: "https://pub.dev"
source: hosted
- version: "1.11.1"
+ version: "1.11.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
- sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
+ sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
url: "https://pub.dev"
source: hosted
- version: "2.1.2"
+ version: "2.1.1"
stream_transform:
dependency: transitive
description:
@@ -750,10 +749,10 @@ packages:
dependency: transitive
description:
name: test_api
- sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
+ sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
url: "https://pub.dev"
source: hosted
- version: "0.6.1"
+ version: "0.5.1"
timing:
dependency: transitive
description:
@@ -794,14 +793,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
- web:
- dependency: transitive
- description:
- name: web
- sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
- url: "https://pub.dev"
- source: hosted
- version: "0.3.0"
web_socket_channel:
dependency: transitive
description:
@@ -827,5 +818,5 @@ packages:
source: hosted
version: "3.1.2"
sdks:
- dart: ">=3.2.0-194.0.dev <4.0.0"
+ dart: ">=3.0.0 <4.0.0"
flutter: ">=3.10.0"
diff --git a/vehicle-tracker/map_web_app/pubspec.yaml b/vehicle-tracker/map_web_app/pubspec.yaml
index a6616802..f0df14fa 100644
--- a/vehicle-tracker/map_web_app/pubspec.yaml
+++ b/vehicle-tracker/map_web_app/pubspec.yaml
@@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
- sdk: '>=3.0.2 <4.0.0'
+ sdk: '>=3.0.0 <4.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
diff --git a/vehicle-tracker/map_web_app/pubspec_overrides.yaml b/vehicle-tracker/map_web_app/pubspec_overrides.yaml
new file mode 100644
index 00000000..a4ed6c5f
--- /dev/null
+++ b/vehicle-tracker/map_web_app/pubspec_overrides.yaml
@@ -0,0 +1,3 @@
+dependency_overrides:
+ digit_components:
+ path: ../packages/digit_components
\ No newline at end of file
diff --git a/vehicle-tracker/map_web_app/route_map/lib/blocs/polygon_map/controllers/map_controllers.dart b/vehicle-tracker/map_web_app/route_map/lib/blocs/polygon_map/controllers/map_controllers.dart
index 16157d85..a7951f04 100644
--- a/vehicle-tracker/map_web_app/route_map/lib/blocs/polygon_map/controllers/map_controllers.dart
+++ b/vehicle-tracker/map_web_app/route_map/lib/blocs/polygon_map/controllers/map_controllers.dart
@@ -45,6 +45,9 @@ class MapControllers extends GetxController {
final alertPolygons = await Map2HttpRepository.getAllPolygonsWithAlerts(tenantId);
+ if(alertMarkers.isNotEmpty) alertMarkers.clear();
+ if(this.alertPolygons.isNotEmpty) this.alertPolygons.clear();
+
for (var alartPolygon in alertPolygons) {
if (alartPolygon.type == "point") {
alertMarkers.add(alartPolygon);
diff --git a/vehicle-tracker/map_web_app/route_map/pubspec.lock b/vehicle-tracker/map_web_app/route_map/pubspec.lock
index ed658da9..83257bc7 100644
--- a/vehicle-tracker/map_web_app/route_map/pubspec.lock
+++ b/vehicle-tracker/map_web_app/route_map/pubspec.lock
@@ -149,10 +149,10 @@ packages:
dependency: transitive
description:
name: collection
- sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
+ sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
url: "https://pub.dev"
source: hosted
- version: "1.18.0"
+ version: "1.17.1"
convert:
dependency: transitive
description:
@@ -377,10 +377,10 @@ packages:
dependency: transitive
description:
name: geolocator_android
- sha256: "136f1c97e1903366393bda514c5d9e98843418baea52899aa45edae9af8a5cd6"
+ sha256: "06e37fa32392f69f133e166ef6b358a8b6afddbf4c418fc236988184cc115a49"
url: "https://pub.dev"
source: hosted
- version: "4.5.2"
+ version: "4.4.1"
geolocator_apple:
dependency: transitive
description:
@@ -473,10 +473,10 @@ packages:
dependency: transitive
description:
name: intl
- sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
+ sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6
url: "https://pub.dev"
source: hosted
- version: "0.18.1"
+ version: "0.18.0"
io:
dependency: transitive
description:
@@ -517,30 +517,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.9.0"
- leak_tracker:
- dependency: transitive
- description:
- name: leak_tracker
- sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
- url: "https://pub.dev"
- source: hosted
- version: "10.0.0"
- leak_tracker_flutter_testing:
- dependency: transitive
- description:
- name: leak_tracker_flutter_testing
- sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
- url: "https://pub.dev"
- source: hosted
- version: "2.0.1"
- leak_tracker_testing:
- dependency: transitive
- description:
- name: leak_tracker_testing
- sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
- url: "https://pub.dev"
- source: hosted
- version: "2.0.1"
lints:
dependency: transitive
description:
@@ -593,26 +569,26 @@ packages:
dependency: transitive
description:
name: matcher
- sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
+ sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
url: "https://pub.dev"
source: hosted
- version: "0.12.16+1"
+ version: "0.12.15"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
- sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
+ sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev"
source: hosted
- version: "0.8.0"
+ version: "0.2.0"
meta:
dependency: transitive
description:
name: meta
- sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
+ sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
url: "https://pub.dev"
source: hosted
- version: "1.11.0"
+ version: "1.9.1"
mgrs_dart:
dependency: transitive
description:
@@ -657,10 +633,10 @@ packages:
dependency: transitive
description:
name: path
- sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
+ sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
url: "https://pub.dev"
source: hosted
- version: "1.9.0"
+ version: "1.8.3"
plugin_platform_interface:
dependency: transitive
description:
@@ -782,10 +758,10 @@ packages:
dependency: transitive
description:
name: source_span
- sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
+ sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
url: "https://pub.dev"
source: hosted
- version: "1.10.0"
+ version: "1.9.1"
sprintf:
dependency: transitive
description:
@@ -798,18 +774,18 @@ packages:
dependency: transitive
description:
name: stack_trace
- sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
+ sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
url: "https://pub.dev"
source: hosted
- version: "1.11.1"
+ version: "1.11.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
- sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
+ sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
url: "https://pub.dev"
source: hosted
- version: "2.1.2"
+ version: "2.1.1"
stream_transform:
dependency: transitive
description:
@@ -838,10 +814,10 @@ packages:
dependency: transitive
description:
name: test_api
- sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
+ sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
url: "https://pub.dev"
source: hosted
- version: "0.6.1"
+ version: "0.5.1"
timing:
dependency: transitive
description:
@@ -878,10 +854,10 @@ packages:
dependency: transitive
description:
name: uuid
- sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8
+ sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f"
url: "https://pub.dev"
source: hosted
- version: "4.3.3"
+ version: "4.2.2"
vector_math:
dependency: transitive
description:
@@ -890,14 +866,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
- vm_service:
- dependency: transitive
- description:
- name: vm_service
- sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
- url: "https://pub.dev"
- source: hosted
- version: "13.0.0"
watcher:
dependency: transitive
description:
@@ -931,5 +899,5 @@ packages:
source: hosted
version: "3.1.2"
sdks:
- dart: ">=3.2.0-0 <4.0.0"
+ dart: ">=3.0.0 <4.0.0"
flutter: ">=3.10.0"
diff --git a/vehicle-tracker/packages/digit_components/.dart_tool/package_config.json b/vehicle-tracker/packages/digit_components/.dart_tool/package_config.json
new file mode 100644
index 00000000..5a969ec4
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/.dart_tool/package_config.json
@@ -0,0 +1,644 @@
+{
+ "configVersion": 2,
+ "packages": [
+ {
+ "name": "_fe_analyzer_shared",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/_fe_analyzer_shared-61.0.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.17"
+ },
+ {
+ "name": "analyzer",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/analyzer-5.13.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "args",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/args-2.4.2",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "async",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/async-2.11.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "bloc",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/bloc-8.1.3",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "bloc_test",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/bloc_test-9.1.6",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "boolean_selector",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/boolean_selector-2.1.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.17"
+ },
+ {
+ "name": "build",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build-2.4.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "build_config",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_config-1.1.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.14"
+ },
+ {
+ "name": "build_daemon",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_daemon-4.0.1",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "build_resolvers",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_resolvers-2.4.2",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "build_runner",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_runner-2.4.8",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "build_runner_core",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_runner_core-7.3.0",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "built_collection",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/built_collection-5.1.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "built_value",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/built_value-8.9.1",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "characters",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/characters-1.3.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "checked_yaml",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/checked_yaml-2.0.3",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "clock",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/clock-1.1.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "code_builder",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/code_builder-4.10.0",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "collection",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/collection-1.17.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "convert",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/convert-3.1.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "coverage",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/coverage-1.6.4",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "crypto",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/crypto-3.0.3",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "dart_style",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/dart_style-2.3.2",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "diff_match_patch",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/diff_match_patch-0.4.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "fake_async",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/fake_async-1.3.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "file",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/file-7.0.0",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "fixnum",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/fixnum-1.1.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "flutter",
+ "rootUri": "file:///home/naveen-gloify/flutter/packages/flutter",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "flutter_bloc",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_bloc-8.1.4",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "flutter_keyboard_visibility",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility-5.4.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "flutter_keyboard_visibility_linux",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_linux-1.0.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "flutter_keyboard_visibility_macos",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_macos-1.0.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "flutter_keyboard_visibility_platform_interface",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_platform_interface-2.0.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "flutter_keyboard_visibility_web",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_web-2.0.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "flutter_keyboard_visibility_windows",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_windows-1.0.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "flutter_lints",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_lints-2.0.3",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "flutter_localizations",
+ "rootUri": "file:///home/naveen-gloify/flutter/packages/flutter_localizations",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "flutter_spinkit",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_spinkit-5.2.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "flutter_test",
+ "rootUri": "file:///home/naveen-gloify/flutter/packages/flutter_test",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "flutter_typeahead",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_typeahead-4.8.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "flutter_web_plugins",
+ "rootUri": "file:///home/naveen-gloify/flutter/packages/flutter_web_plugins",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "fluttertoast",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/fluttertoast-8.2.4",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "freezed",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/freezed-2.4.7",
+ "packageUri": "lib/",
+ "languageVersion": "2.17"
+ },
+ {
+ "name": "freezed_annotation",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/freezed_annotation-2.4.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.17"
+ },
+ {
+ "name": "frontend_server_client",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/frontend_server_client-3.2.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "glob",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/glob-2.1.2",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "graphs",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/graphs-2.3.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "horizontal_data_table",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/horizontal_data_table-4.3.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "http_multi_server",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/http_multi_server-3.2.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "http_parser",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/http_parser-4.0.2",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "intl",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/intl-0.18.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "io",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/io-1.0.4",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "js",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/js-0.6.7",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "json_annotation",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/json_annotation-4.8.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "json_serializable",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/json_serializable-6.7.1",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "lints",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/lints-2.1.1",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "location",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/location-5.0.3",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "location_platform_interface",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/location_platform_interface-3.1.2",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "location_web",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/location_web-4.2.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "logging",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/logging-1.2.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "matcher",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/matcher-0.12.15",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "material_color_utilities",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/material_color_utilities-0.2.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.13"
+ },
+ {
+ "name": "meta",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/meta-1.9.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "mime",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/mime-1.0.4",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "mocktail",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/mocktail-0.3.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "nested",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/nested-1.0.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "node_preamble",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/node_preamble-2.0.2",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "overlay_builder",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/overlay_builder-1.1.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "package_config",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/package_config-2.1.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "path",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/path-1.8.3",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "plugin_platform_interface",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/plugin_platform_interface-2.1.8",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "pointer_interceptor",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/pointer_interceptor-0.9.3+5",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "pool",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/pool-1.5.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "provider",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/provider-6.1.2",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "pub_semver",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/pub_semver-2.1.4",
+ "packageUri": "lib/",
+ "languageVersion": "2.17"
+ },
+ {
+ "name": "pubspec_parse",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/pubspec_parse-1.2.3",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "reactive_flutter_typeahead",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/reactive_flutter_typeahead-0.7.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.17"
+ },
+ {
+ "name": "reactive_forms",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/reactive_forms-14.3.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.17"
+ },
+ {
+ "name": "shelf",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/shelf-1.4.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.17"
+ },
+ {
+ "name": "shelf_packages_handler",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/shelf_packages_handler-3.0.2",
+ "packageUri": "lib/",
+ "languageVersion": "2.17"
+ },
+ {
+ "name": "shelf_static",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/shelf_static-1.1.2",
+ "packageUri": "lib/",
+ "languageVersion": "2.17"
+ },
+ {
+ "name": "shelf_web_socket",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/shelf_web_socket-1.0.4",
+ "packageUri": "lib/",
+ "languageVersion": "2.17"
+ },
+ {
+ "name": "sky_engine",
+ "rootUri": "file:///home/naveen-gloify/flutter/bin/cache/pkg/sky_engine",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "source_gen",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_gen-1.5.0",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "source_helper",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_helper-1.3.4",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "source_map_stack_trace",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_map_stack_trace-2.1.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "source_maps",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_maps-0.10.12",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "source_span",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_span-1.9.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.14"
+ },
+ {
+ "name": "stack_trace",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/stack_trace-1.11.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "stream_channel",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/stream_channel-2.1.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.14"
+ },
+ {
+ "name": "stream_transform",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/stream_transform-2.1.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.14"
+ },
+ {
+ "name": "string_scanner",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/string_scanner-1.2.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "term_glyph",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/term_glyph-1.2.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.12"
+ },
+ {
+ "name": "test",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/test-1.24.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "test_api",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/test_api-0.5.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "test_core",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/test_core-0.5.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.18"
+ },
+ {
+ "name": "timing",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/timing-1.0.1",
+ "packageUri": "lib/",
+ "languageVersion": "2.14"
+ },
+ {
+ "name": "typed_data",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/typed_data-1.3.2",
+ "packageUri": "lib/",
+ "languageVersion": "2.17"
+ },
+ {
+ "name": "vector_math",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/vector_math-2.1.4",
+ "packageUri": "lib/",
+ "languageVersion": "2.14"
+ },
+ {
+ "name": "vm_service",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/vm_service-11.10.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "watcher",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/watcher-1.1.0",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "web_socket_channel",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/web_socket_channel-2.4.0",
+ "packageUri": "lib/",
+ "languageVersion": "2.15"
+ },
+ {
+ "name": "webkit_inspection_protocol",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/webkit_inspection_protocol-1.2.1",
+ "packageUri": "lib/",
+ "languageVersion": "3.0"
+ },
+ {
+ "name": "yaml",
+ "rootUri": "file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/yaml-3.1.2",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ },
+ {
+ "name": "digit_components",
+ "rootUri": "../",
+ "packageUri": "lib/",
+ "languageVersion": "2.19"
+ }
+ ],
+ "generated": "2024-03-21T06:43:24.575479Z",
+ "generator": "pub",
+ "generatorVersion": "3.0.0"
+}
diff --git a/vehicle-tracker/packages/digit_components/.dart_tool/package_config_subset b/vehicle-tracker/packages/digit_components/.dart_tool/package_config_subset
new file mode 100644
index 00000000..b467e737
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/.dart_tool/package_config_subset
@@ -0,0 +1,425 @@
+_fe_analyzer_shared
+2.17
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/_fe_analyzer_shared-61.0.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/_fe_analyzer_shared-61.0.0/lib/
+analyzer
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/analyzer-5.13.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/analyzer-5.13.0/lib/
+args
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/args-2.4.2/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/args-2.4.2/lib/
+async
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/async-2.11.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/async-2.11.0/lib/
+bloc
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/bloc-8.1.3/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/bloc-8.1.3/lib/
+bloc_test
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/bloc_test-9.1.6/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/bloc_test-9.1.6/lib/
+boolean_selector
+2.17
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/boolean_selector-2.1.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/boolean_selector-2.1.1/lib/
+build
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build-2.4.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build-2.4.1/lib/
+build_config
+2.14
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_config-1.1.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_config-1.1.1/lib/
+build_daemon
+3.0
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_daemon-4.0.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_daemon-4.0.1/lib/
+build_resolvers
+3.0
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_resolvers-2.4.2/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_resolvers-2.4.2/lib/
+build_runner
+3.0
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_runner-2.4.8/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_runner-2.4.8/lib/
+build_runner_core
+3.0
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_runner_core-7.3.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/build_runner_core-7.3.0/lib/
+built_collection
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/built_collection-5.1.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/built_collection-5.1.1/lib/
+built_value
+3.0
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/built_value-8.9.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/built_value-8.9.1/lib/
+characters
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/characters-1.3.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/characters-1.3.0/lib/
+checked_yaml
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/checked_yaml-2.0.3/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/checked_yaml-2.0.3/lib/
+clock
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/clock-1.1.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/clock-1.1.1/lib/
+code_builder
+3.0
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/code_builder-4.10.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/code_builder-4.10.0/lib/
+collection
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/collection-1.17.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/collection-1.17.1/lib/
+convert
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/convert-3.1.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/convert-3.1.1/lib/
+coverage
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/coverage-1.6.4/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/coverage-1.6.4/lib/
+crypto
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/crypto-3.0.3/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/crypto-3.0.3/lib/
+dart_style
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/dart_style-2.3.2/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/dart_style-2.3.2/lib/
+diff_match_patch
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/diff_match_patch-0.4.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/diff_match_patch-0.4.1/lib/
+fake_async
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/fake_async-1.3.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/fake_async-1.3.1/lib/
+file
+3.0
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/file-7.0.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/file-7.0.0/lib/
+fixnum
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/fixnum-1.1.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/fixnum-1.1.0/lib/
+flutter_bloc
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_bloc-8.1.4/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_bloc-8.1.4/lib/
+flutter_keyboard_visibility
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility-5.4.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility-5.4.1/lib/
+flutter_keyboard_visibility_linux
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_linux-1.0.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_linux-1.0.0/lib/
+flutter_keyboard_visibility_macos
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_macos-1.0.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_macos-1.0.0/lib/
+flutter_keyboard_visibility_platform_interface
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_platform_interface-2.0.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_platform_interface-2.0.0/lib/
+flutter_keyboard_visibility_web
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_web-2.0.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_web-2.0.0/lib/
+flutter_keyboard_visibility_windows
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_windows-1.0.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_windows-1.0.0/lib/
+flutter_lints
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_lints-2.0.3/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_lints-2.0.3/lib/
+flutter_spinkit
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_spinkit-5.2.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_spinkit-5.2.0/lib/
+flutter_typeahead
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_typeahead-4.8.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_typeahead-4.8.0/lib/
+fluttertoast
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/fluttertoast-8.2.4/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/fluttertoast-8.2.4/lib/
+freezed
+2.17
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/freezed-2.4.7/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/freezed-2.4.7/lib/
+freezed_annotation
+2.17
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/freezed_annotation-2.4.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/freezed_annotation-2.4.1/lib/
+frontend_server_client
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/frontend_server_client-3.2.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/frontend_server_client-3.2.0/lib/
+glob
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/glob-2.1.2/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/glob-2.1.2/lib/
+graphs
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/graphs-2.3.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/graphs-2.3.1/lib/
+horizontal_data_table
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/horizontal_data_table-4.3.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/horizontal_data_table-4.3.1/lib/
+http_multi_server
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/http_multi_server-3.2.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/http_multi_server-3.2.1/lib/
+http_parser
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/http_parser-4.0.2/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/http_parser-4.0.2/lib/
+intl
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/intl-0.18.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/intl-0.18.0/lib/
+io
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/io-1.0.4/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/io-1.0.4/lib/
+js
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/js-0.6.7/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/js-0.6.7/lib/
+json_annotation
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/json_annotation-4.8.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/json_annotation-4.8.1/lib/
+json_serializable
+3.0
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/json_serializable-6.7.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/json_serializable-6.7.1/lib/
+lints
+3.0
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/lints-2.1.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/lints-2.1.1/lib/
+location
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/location-5.0.3/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/location-5.0.3/lib/
+location_platform_interface
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/location_platform_interface-3.1.2/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/location_platform_interface-3.1.2/lib/
+location_web
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/location_web-4.2.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/location_web-4.2.0/lib/
+logging
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/logging-1.2.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/logging-1.2.0/lib/
+matcher
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/matcher-0.12.15/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/matcher-0.12.15/lib/
+material_color_utilities
+2.13
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/material_color_utilities-0.2.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/material_color_utilities-0.2.0/lib/
+meta
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/meta-1.9.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/meta-1.9.1/lib/
+mime
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/mime-1.0.4/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/mime-1.0.4/lib/
+mocktail
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/mocktail-0.3.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/mocktail-0.3.0/lib/
+nested
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/nested-1.0.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/nested-1.0.0/lib/
+node_preamble
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/node_preamble-2.0.2/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/node_preamble-2.0.2/lib/
+overlay_builder
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/overlay_builder-1.1.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/overlay_builder-1.1.0/lib/
+package_config
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/package_config-2.1.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/package_config-2.1.0/lib/
+path
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/path-1.8.3/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/path-1.8.3/lib/
+plugin_platform_interface
+3.0
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/plugin_platform_interface-2.1.8/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/plugin_platform_interface-2.1.8/lib/
+pointer_interceptor
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/pointer_interceptor-0.9.3+5/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/pointer_interceptor-0.9.3+5/lib/
+pool
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/pool-1.5.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/pool-1.5.1/lib/
+provider
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/provider-6.1.2/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/provider-6.1.2/lib/
+pub_semver
+2.17
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/pub_semver-2.1.4/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/pub_semver-2.1.4/lib/
+pubspec_parse
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/pubspec_parse-1.2.3/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/pubspec_parse-1.2.3/lib/
+reactive_flutter_typeahead
+2.17
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/reactive_flutter_typeahead-0.7.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/reactive_flutter_typeahead-0.7.0/lib/
+reactive_forms
+2.17
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/reactive_forms-14.3.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/reactive_forms-14.3.0/lib/
+shelf
+2.17
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/shelf-1.4.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/shelf-1.4.1/lib/
+shelf_packages_handler
+2.17
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/shelf_packages_handler-3.0.2/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/shelf_packages_handler-3.0.2/lib/
+shelf_static
+2.17
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/shelf_static-1.1.2/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/shelf_static-1.1.2/lib/
+shelf_web_socket
+2.17
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/shelf_web_socket-1.0.4/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/shelf_web_socket-1.0.4/lib/
+source_gen
+3.0
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_gen-1.5.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_gen-1.5.0/lib/
+source_helper
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_helper-1.3.4/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_helper-1.3.4/lib/
+source_map_stack_trace
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_map_stack_trace-2.1.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_map_stack_trace-2.1.1/lib/
+source_maps
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_maps-0.10.12/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_maps-0.10.12/lib/
+source_span
+2.14
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_span-1.9.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/source_span-1.9.1/lib/
+stack_trace
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/stack_trace-1.11.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/stack_trace-1.11.0/lib/
+stream_channel
+2.14
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/stream_channel-2.1.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/stream_channel-2.1.1/lib/
+stream_transform
+2.14
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/stream_transform-2.1.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/stream_transform-2.1.0/lib/
+string_scanner
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/string_scanner-1.2.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/string_scanner-1.2.0/lib/
+term_glyph
+2.12
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/term_glyph-1.2.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/term_glyph-1.2.1/lib/
+test
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/test-1.24.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/test-1.24.1/lib/
+test_api
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/test_api-0.5.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/test_api-0.5.1/lib/
+test_core
+2.18
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/test_core-0.5.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/test_core-0.5.1/lib/
+timing
+2.14
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/timing-1.0.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/timing-1.0.1/lib/
+typed_data
+2.17
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/typed_data-1.3.2/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/typed_data-1.3.2/lib/
+vector_math
+2.14
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/vector_math-2.1.4/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/vector_math-2.1.4/lib/
+vm_service
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/vm_service-11.10.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/vm_service-11.10.0/lib/
+watcher
+3.0
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/watcher-1.1.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/watcher-1.1.0/lib/
+web_socket_channel
+2.15
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/web_socket_channel-2.4.0/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/web_socket_channel-2.4.0/lib/
+webkit_inspection_protocol
+3.0
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/webkit_inspection_protocol-1.2.1/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/webkit_inspection_protocol-1.2.1/lib/
+yaml
+2.19
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/yaml-3.1.2/
+file:///home/naveen-gloify/.pub-cache/hosted/pub.dev/yaml-3.1.2/lib/
+digit_components
+2.19
+file:///home/naveen-gloify/Documents/GitHub/egov-rnd/vehicle-tracker/vehicle_tracker_app/packages/digit_components/
+file:///home/naveen-gloify/Documents/GitHub/egov-rnd/vehicle-tracker/vehicle_tracker_app/packages/digit_components/lib/
+sky_engine
+3.0
+file:///home/naveen-gloify/flutter/bin/cache/pkg/sky_engine/
+file:///home/naveen-gloify/flutter/bin/cache/pkg/sky_engine/lib/
+flutter
+3.0
+file:///home/naveen-gloify/flutter/packages/flutter/
+file:///home/naveen-gloify/flutter/packages/flutter/lib/
+flutter_localizations
+3.0
+file:///home/naveen-gloify/flutter/packages/flutter_localizations/
+file:///home/naveen-gloify/flutter/packages/flutter_localizations/lib/
+flutter_test
+3.0
+file:///home/naveen-gloify/flutter/packages/flutter_test/
+file:///home/naveen-gloify/flutter/packages/flutter_test/lib/
+flutter_web_plugins
+3.0
+file:///home/naveen-gloify/flutter/packages/flutter_web_plugins/
+file:///home/naveen-gloify/flutter/packages/flutter_web_plugins/lib/
+2
diff --git a/vehicle-tracker/packages/digit_components/.dart_tool/version b/vehicle-tracker/packages/digit_components/.dart_tool/version
new file mode 100644
index 00000000..e06d07af
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/.dart_tool/version
@@ -0,0 +1 @@
+3.10.0
\ No newline at end of file
diff --git a/vehicle-tracker/packages/digit_components/.flutter-plugins b/vehicle-tracker/packages/digit_components/.flutter-plugins
new file mode 100644
index 00000000..b1d391a2
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/.flutter-plugins
@@ -0,0 +1,9 @@
+# This is a generated file; do not edit or check into version control.
+flutter_keyboard_visibility=/home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility-5.4.1/
+flutter_keyboard_visibility_linux=/home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_linux-1.0.0/
+flutter_keyboard_visibility_macos=/home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_macos-1.0.0/
+flutter_keyboard_visibility_web=/home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_web-2.0.0/
+flutter_keyboard_visibility_windows=/home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_windows-1.0.0/
+fluttertoast=/home/naveen-gloify/.pub-cache/hosted/pub.dev/fluttertoast-8.2.4/
+location=/home/naveen-gloify/.pub-cache/hosted/pub.dev/location-5.0.3/
+location_web=/home/naveen-gloify/.pub-cache/hosted/pub.dev/location_web-4.2.0/
diff --git a/vehicle-tracker/packages/digit_components/.flutter-plugins-dependencies b/vehicle-tracker/packages/digit_components/.flutter-plugins-dependencies
new file mode 100644
index 00000000..04b61cf0
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/.flutter-plugins-dependencies
@@ -0,0 +1 @@
+{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_keyboard_visibility","path":"/home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility-5.4.1/","native_build":true,"dependencies":[]},{"name":"fluttertoast","path":"/home/naveen-gloify/.pub-cache/hosted/pub.dev/fluttertoast-8.2.4/","native_build":true,"dependencies":[]},{"name":"location","path":"/home/naveen-gloify/.pub-cache/hosted/pub.dev/location-5.0.3/","native_build":true,"dependencies":[]}],"android":[{"name":"flutter_keyboard_visibility","path":"/home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility-5.4.1/","native_build":true,"dependencies":[]},{"name":"fluttertoast","path":"/home/naveen-gloify/.pub-cache/hosted/pub.dev/fluttertoast-8.2.4/","native_build":true,"dependencies":[]},{"name":"location","path":"/home/naveen-gloify/.pub-cache/hosted/pub.dev/location-5.0.3/","native_build":true,"dependencies":[]}],"macos":[{"name":"flutter_keyboard_visibility_macos","path":"/home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_macos-1.0.0/","native_build":false,"dependencies":[]},{"name":"location","path":"/home/naveen-gloify/.pub-cache/hosted/pub.dev/location-5.0.3/","native_build":true,"dependencies":[]}],"linux":[{"name":"flutter_keyboard_visibility_linux","path":"/home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_linux-1.0.0/","native_build":false,"dependencies":[]}],"windows":[{"name":"flutter_keyboard_visibility_windows","path":"/home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_windows-1.0.0/","native_build":false,"dependencies":[]}],"web":[{"name":"flutter_keyboard_visibility_web","path":"/home/naveen-gloify/.pub-cache/hosted/pub.dev/flutter_keyboard_visibility_web-2.0.0/","dependencies":[]},{"name":"fluttertoast","path":"/home/naveen-gloify/.pub-cache/hosted/pub.dev/fluttertoast-8.2.4/","dependencies":[]},{"name":"location_web","path":"/home/naveen-gloify/.pub-cache/hosted/pub.dev/location_web-4.2.0/","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_keyboard_visibility","dependencies":["flutter_keyboard_visibility_linux","flutter_keyboard_visibility_macos","flutter_keyboard_visibility_web","flutter_keyboard_visibility_windows"]},{"name":"flutter_keyboard_visibility_linux","dependencies":[]},{"name":"flutter_keyboard_visibility_macos","dependencies":[]},{"name":"flutter_keyboard_visibility_web","dependencies":[]},{"name":"flutter_keyboard_visibility_windows","dependencies":[]},{"name":"fluttertoast","dependencies":[]},{"name":"location","dependencies":["location_web"]},{"name":"location_web","dependencies":[]}],"date_created":"2024-03-21 12:13:24.771360","version":"3.10.0"}
\ No newline at end of file
diff --git a/vehicle-tracker/packages/digit_components/CHANGELOG.md b/vehicle-tracker/packages/digit_components/CHANGELOG.md
new file mode 100644
index 00000000..d306fc0b
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/CHANGELOG.md
@@ -0,0 +1,31 @@
+## 0.0.1
+
+* Initial Releaase
+
+## 0.0.1+1
+
+* Read Me file added
+
+## 0.0.1+2
+
+* Read Me file modified with Components example
+
+## 0.0.1+3
+
+* example file added
+
+## 0.0.1+4
+
+* dart doc added and auto generated files added
+
+## 0.0.1+5
+
+* Example file with Reactive Forms
+
+## 0.0.1+6
+
+* Updated Components behaviour in ReadMe File
+
+## 0.0.1+8
+
+* Updated Images in ReadMe File
\ No newline at end of file
diff --git a/vehicle-tracker/packages/digit_components/LICENSE b/vehicle-tracker/packages/digit_components/LICENSE
new file mode 100644
index 00000000..331d2b43
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 eGovernments Foundation
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vehicle-tracker/packages/digit_components/README.md b/vehicle-tracker/packages/digit_components/README.md
new file mode 100644
index 00000000..c192395a
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/README.md
@@ -0,0 +1,465 @@
+# **DIGIT Components**
+
+**DigitActionDialog** :
+ A customizable dialog with multiple action buttons / widgets.
+
+ Usage:
+ onPressed : () => DigitActionDialog.show(
+ context,
+ widget: your_widget()
+ )
+
+
+
+**DigitCheckBox** :
+ A single checkbox component.
+
+ Usage:
+ DigitCheckbox(
+ label: “your_label”,
+ value: boolean_value,
+ ),
+
+![CheckBox](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/e1c9a6d1-cb0c-4b95-9dea-dddd6ff98f17)
+
+**DigitDateFormPicker** :
+ A Reactive Date Picker widget with custom label header. The formControlName is required to bind this ReactiveDatePicker to a FormControl.
+
+ Usage:
+ DigitDateFormPicker(
+ label: 'Date of Birth',
+ padding: const EdgeInsets.only(top: 32.0),
+ isRequired: true,
+ cancelText: 'Cancel',
+ fieldHintText: 'dd/mm/yyyy',
+ confirmText: 'OK',
+ icon: Icons.info_outline_rounded,
+ toolTipMsg: 'Age should not be less than 18 years',
+ formControlName: ’dob_key’,
+ autoValidation: AutovalidateMode.always,
+ requiredMessage: 'Date of birth is required',
+ validationMessages: {
+ 'required': (_) => ‘Date of birth is required’,
+ 'max': (_) => ‘Age cannot be greater than 18 years’
+ },)
+
+ ![Date Form Picker](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/93f326aa-df4e-40a6-af5b-34155760c01b)
+
+
+ **DigitDivider** :
+ A simple divider widget
+
+ Usage:
+ DigitDivider()
+
+![Divider](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/e953c1bc-8513-4370-a0f1-aaa8e914a7c0)
+
+**DigitDropdown** :
+ A custom dropdown component with label header.
+
+ Usage:
+ DigitDropdown(
+ value: state.selectedBoundaryMap.entries
+ .firstWhereOrNull(
+ (element) => element.key == label,
+ )
+ ?.value,
+ label: label,
+ menuItems: your_item_list,
+ onChanged: (value) {
+ if (value == null) return;
+ //Any functions to perform on change or on select of the dropdown
+ },
+ valueMapper: (value) {
+ return value.name ?? value.code ?? 'No Value';
+ },)
+
+
+**DigitIconButton** :
+ A custom icon widget with customizable icon, icon Text, icon color, icon Text Color
+
+ Usage:
+ DigitIconButton(
+ icon: Icons.check_circle,
+ iconText: 'delivered',
+ iconTextColor: Colors.white,
+ iconColor: Colors.orange,
+ )
+![DigitIcon Button](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/03cb82f3-500e-41c9-bdb4-f0bfb9b8b7ae)
+
+**DigitIntegerFormPicker** :
+ An Integer Picker with option to select any number by increasing or decreasing the using + and - end buttons
+
+
+
+ Usage:
+ DigitIntegerFormPicker(
+ form: form,
+ minimum: 0,
+ formControlName: _quantityDistributedKey,
+ label: ‘Number of members’,
+ incrementer: true,
+ ),
+
+![Digit Integer Form Picker](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/78a2e080-799c-4146-8e10-0eaea6f732f9)
+
+**DigitOutlineIconButton** :
+ An outlined button with a leading icon based on DIGIT figma.
+
+ Usage:
+ DigitOutlineIconButton(
+ icon: Icons.warning,
+ label:’Decline’,
+ onPressed: () => call_your_on_pressed_function(),
+ )
+
+
+
+
+**DigitReactiveDropdown** :
+ A reactive dropdown form component. The formControlName is required to bind this ReactiveDropdownField to a FormControl
+
+ Usage:
+ DigitReactiveDropdown(
+ label: ‘relationship’,
+ menuItems:
+ relationship.map((e) => e.toString()).toList(),
+ isRequired: true,
+ formControlName: relationshipKey,
+ valueMapper: (value) =>
+ t.translate('CORE_COMMON_$value'),
+ onChanged: (value) {},
+ validationMessages: {
+ 'required': (_) => t.translate(
+ i18.wageSeeker.relationshipRequired,
+ ),
+ },
+ )
+![DigitReactive Type ahead](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/ff5ea5c4-2c9b-4340-a03e-456b4b762894)
+
+**DigitTextFormField** :
+ A reactive form reactive dropdown component. The formControlName is required to bind this ReactiveTextField to a FormControl
+
+ Usage:
+ DigitTextFormField(
+ formControlName: fatherNameKey,
+ isRequired: true,
+ label: t.translate(i18.common.guardianName),
+ inputFormatter: [
+ FilteringTextInputFormatter.allow(RegExp("[A-Za-z ]"))
+ ],
+ validationMessages: {
+ 'required': (_) => t.translate(
+ i18.wageSeeker.fatherNameRequired,
+ ),
+ 'minLength': (_) => t.translate(
+ i18.wageSeeker.minFatherNameCharacters,
+ ),
+ 'maxLength': (_) => t.translate(
+ i18.wageSeeker.maxFatherNameCharacters,
+ ),
+ },
+ )
+
+
+
+
+**DigitToast** :
+ To show a toast message based on completion of user actions
+
+ Usage:
+ DigitToast.show(
+ context,
+ options: DigitToastOptions(
+ message ?? 'Unable to login',
+ true,
+ theme,
+ ),
+ );
+
+![Digit Toast](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/4c29786d-51e9-4583-9ea5-73e4a6b829df)
+
+
+**DigitCard** :
+ A Card component with onPressed functions
+
+ Usage:
+ DigitCard(
+ child: Text(‘Card Details’),
+ onPressed: null)
+
+![Digit Card](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/92a201dc-47a0-42e4-a085-e5faec0d6de5)
+
+
+**DigitCheckBoxTile** :
+ It is a List of checkbox widgets, with label heading
+
+
+ Usage:
+ DigitCheckboxTile(
+ margin: const EdgeInsets.only(top: 16),
+ padding: const EdgeInsets.fromLTRB(0, 8, 8, 8),
+ label: hint ?? 'Some',
+ value: (control.value ?? false) as bool,
+ onChanged: (value) => control.value = value,
+ )
+
+![CheckBox List Tile](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/e6132fb8-00fb-49e9-91c6-20d2555211c8)
+
+
+**DigitDialog** :
+ A customizable dialog with options to add a custom title for the dialog , subtitle for the dialog and action buttons. It can be used on any on Pressed buttons to show this dialog
+
+
+ Usage:
+ () => DigitDialog.show(context,
+ options: DigitDialogOptions(
+ titleIcon: const Icon(
+ Icons.warning,
+ color: Colors.red,
+ ),
+ titleText: ‘Warning’
+ contentText: ‘Are you sure to decline ?’,
+ primaryAction: DigitDialogActions(
+ label: ‘Confirm’,
+ action: (BuildContext context) {
+ //your_primary_action();
+ },
+ ),
+ secondaryAction: DigitDialogActions(
+ label: ‘Cancel’,
+ action: (BuildContext context) =>
+ //your_secondary_action(),
+ )));
+
+
+
+
+**DigitDobPicker** :
+ It is an enhanced component of DigitDateFormPicker. Here we have an additional Text field component which will be displaying the age based on the selected date and validate the age.
+
+ Usage:
+ DigitDobPicker(
+ datePickerFormControl: _dobKey,
+ datePickerLabel: localizations.translate(
+ i18.individualDetails.dobLabelText,
+ ),
+ ageFieldLabel: localizations.translate(
+ i18.individualDetails.ageLabelText,
+ ),
+ separatorLabel: localizations.translate(
+ i18.individualDetails.separatorLabelText,
+ ),
+ ),
+![DOB Picker](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/d1955dac-fba1-443f-bdb9-19c79f81f3e8)
+
+
+**DigitElevatedButton** :
+ It is an elevated button with label widget options and onPressed functions based on DIGIT Figma
+
+ Usage:
+ DigitElevatedButton(
+ onPressed: ()=> your_on_Pressed_function(),
+ child: Center(
+ child: Text(
+ ‘Manage wage seekers’,
+ style: Theme.of(context)
+ .textTheme
+ .titleMedium!
+ .apply(color: Colors.white)),
+ ),);
+
+
+
+
+**DigitIconTile** :
+ A custom list tile widget with options to add leading icon and onPressed function. Mostly it is used in Sidebar tiles.
+
+ Usage:
+ DigitIconTile(
+ title: ‘View Profile’,
+ selected: context.router.currentPath.contains('orgProfile'),
+ icon: Icons.perm_contact_cal_sharp,
+ onPressed: () => your_on_pressed_function())
+
+
+
+
+**DigitInfoCard** :
+ A custom information card based on DIGIT Figma. Here we have options to add the desired icon, background color, icon color, description and heading title of the information card.
+
+ Usage:
+ DigitInfoCard(
+ icon: Icons.info,
+ backgroundColor: theme.colorScheme.tertiaryContainer,
+ iconColor: theme.colorScheme.surfaceTint,
+ description: localizations
+ .translate(i18.home.dataSyncInfoContent)
+ .replaceAll('{}', count.toString()),
+ title: localizations.translate(
+ i18.home.dataSyncInfoLabel,
+ ),
+ ),
+
+![Digit Info Card](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/104d3f92-73c3-4800-b6ac-b1cd22af1ad4)
+
+**DigitOutlineButton** :
+ An outlined button based on DIGIT figma.
+
+ Usage:
+ DigitOutLineButton(
+ label: localizations.translate(
+ i18.memberCard.deliverDetailsUpdateLabel,
+ ),
+ onPressed: () => your_on_pressed_function(),
+ )
+
+
+**DigitReactiveTypeAhead** :
+A TypeAhead (autocomplete) widget , where you can show suggestions to users as they type
+
+ Usage:
+ DigitReactiveTypeAhead(
+ formControlName: formControlName,
+ stringify: valueMapper,
+ initialValue: initialValue,
+ initialValueText: initialValueText,
+ onSuggestionSelected: onSuggestionSelected,
+ debounceDuration: const Duration(milliseconds: 100),
+ suggestionsCallback: (pattern) => suggestionsCallback(
+ menuItems,
+ pattern,
+ ),
+ itemBuilder: (context, item) {
+ return Padding(
+ padding: const EdgeInsets.all(kPadding * 2),
+ child: Text(
+ valueMapper(item),
+ style: Theme.of(context).textTheme.bodyLarge,
+ ),
+ );
+ },
+ )
+![DigitReactive Type ahead](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/ed1426eb-d0ce-4b72-9391-5c51a8ca566c)
+
+**DigitSearchBar** :
+ A search bar based on DIGIT figma. It has on change functionality to perform actions on text change. A hint text can also be added on the search bar
+
+ Usage:
+ DigitSearchBar(
+ controller: searchController,
+ hintText: localizations.translate(
+ i18.searchBeneficiary.beneficiarySearchHintText,
+ ),
+ textCapitalization: TextCapitalization.words,
+ onChanged: (value) => your_on_change_function()
+ ),
+
+![Digit Search Bar](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/6f5bb44f-49f4-4710-9349-a794294d46f5)
+
+**DigitSearchDropdown** :
+ A searchable drop down that wraps DigitReactiveTypeAhead and has a custom label widget.
+
+ Usage:
+ DigitSearchDropdown(
+ label: ‘City’
+ formControlName: formControlName,
+ stringify: valueMapper,
+ initialValue: initialValue,
+ initialValueText: initialValueText,
+ onSuggestionSelected: onSuggestionSelected,
+ debounceDuration: const Duration(milliseconds: 100),
+ suggestionsCallback: (pattern) => suggestionsCallback(
+ menuItems,
+ pattern,
+ ),
+ itemBuilder: (context, item) {
+ return Padding(
+ padding: const EdgeInsets.all(kPadding * 2),
+ child: Text(
+ valueMapper(item),
+ style: Theme.of(context).textTheme.bodyLarge,
+ ),
+ );
+ },
+ )
+
+![Digit Search Dropdown](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/ec616ced-5b7b-4f9c-86f7-e054c358bc80)
+
+
+**DigitSyncDialogContent** :
+ A sync dialog with options to add a custom label for the dialog , and type of the dialog. There are 3 types of sync dialog :
+ **In Progress**
+ **Completed**
+ **Failed**
+
+ Usage:
+ () => DigitSyncDialogContent.show(
+ context,
+ type: DigitSyncDialogType.complete,
+ label: 'Data Synced',
+ primaryAction: DigitDialogActions(
+ label: 'Close',
+ action: (act) {
+ Navigator.pop(act);
+ },
+ ),
+ );
+
+![Digit SyncDialog](https://github.com/egovernments/health-campaign-field-worker-app/assets/85437265/7e6a400b-1417-4310-85fb-2c4a38af505f)
+
+
+**DigitTextField** :
+ A text field component similar to DigitTextFormField. It can be used without wrapping under reactive form
+
+ Usage:
+ DigitTextField(
+ readOnly: true,
+ label: localizations.translate(
+ i18.householdLocation.administrationAreaFormLabel,
+ ),
+ controller: TextEditingController(
+ text: context.boundary.code,
+ ),
+ ),
+
+
+
+**LabeledField** :
+ A labeled widget which has a child widget. If any field need to have a label on its top, then that widget can be wrapped with this LabeledField.
+
+ Usage:
+ LabeledField(
+ label: '$label ${isRequired ? '*' : ''}',
+ padding: padding,
+ labelStyle: Theme.of(context).textTheme.bodyLarge,
+ icon: icon,
+ toolTipMsg: toolTipMsg,
+ child: ReactiveDatePicker())
+
+
+
+
+**PoweredByDigit** :
+ Generic Digit Footer.
+
+ Usage:
+ PoweredByDigit(
+ isWhiteLogo: true,
+ )
+
+
+
+**ScrollableContent** :
+ A Scrollable wrapper widget. All children widgets can be wrapped inside a single scroll card.
+
+ Usage:
+ ScrollableContent(
+ header: Column(children: const [
+ Back(),
+ DigitCard(),
+ ]),
+ footer: PoweredByDigit()
+ )
+
+
diff --git a/vehicle-tracker/packages/digit_components/analysis_options.yaml b/vehicle-tracker/packages/digit_components/analysis_options.yaml
new file mode 100644
index 00000000..9d8e06d9
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/analysis_options.yaml
@@ -0,0 +1,6 @@
+include: package:flutter_lints/flutter.yaml
+
+analyzer:
+ exclude:
+ - "**/*.g.dart"
+ - "**/*.freezed.dart"
\ No newline at end of file
diff --git a/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/LICENSE.txt b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/LICENSE.txt
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Black.ttf b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Black.ttf
new file mode 100644
index 00000000..0112e7da
Binary files /dev/null and b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Black.ttf differ
diff --git a/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-BlackItalic.ttf b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-BlackItalic.ttf
new file mode 100644
index 00000000..b2c6aca5
Binary files /dev/null and b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-BlackItalic.ttf differ
diff --git a/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Bold.ttf b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Bold.ttf
new file mode 100644
index 00000000..43da14d8
Binary files /dev/null and b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Bold.ttf differ
diff --git a/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-BoldItalic.ttf b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-BoldItalic.ttf
new file mode 100644
index 00000000..bcfdab43
Binary files /dev/null and b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-BoldItalic.ttf differ
diff --git a/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Italic.ttf b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Italic.ttf
new file mode 100644
index 00000000..1b5eaa36
Binary files /dev/null and b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Italic.ttf differ
diff --git a/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Light.ttf b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Light.ttf
new file mode 100644
index 00000000..e7307e72
Binary files /dev/null and b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Light.ttf differ
diff --git a/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-LightItalic.ttf b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-LightItalic.ttf
new file mode 100644
index 00000000..2d277afb
Binary files /dev/null and b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-LightItalic.ttf differ
diff --git a/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Medium.ttf b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Medium.ttf
new file mode 100644
index 00000000..ac0f908b
Binary files /dev/null and b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Medium.ttf differ
diff --git a/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-MediumItalic.ttf b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-MediumItalic.ttf
new file mode 100644
index 00000000..fc36a478
Binary files /dev/null and b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-MediumItalic.ttf differ
diff --git a/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Regular.ttf b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Regular.ttf
new file mode 100644
index 00000000..ddf4bfac
Binary files /dev/null and b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Regular.ttf differ
diff --git a/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Thin.ttf b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Thin.ttf
new file mode 100644
index 00000000..2e0dee6a
Binary files /dev/null and b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/Roboto-Thin.ttf differ
diff --git a/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/RobotoCondensed-Regular.ttf b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/RobotoCondensed-Regular.ttf
new file mode 100644
index 00000000..17e8ea57
Binary files /dev/null and b/vehicle-tracker/packages/digit_components/assets/fonts/Roboto/RobotoCondensed-Regular.ttf differ
diff --git a/vehicle-tracker/packages/digit_components/assets/images/powered_by_digit.png b/vehicle-tracker/packages/digit_components/assets/images/powered_by_digit.png
new file mode 100644
index 00000000..f83334fd
Binary files /dev/null and b/vehicle-tracker/packages/digit_components/assets/images/powered_by_digit.png differ
diff --git a/vehicle-tracker/packages/digit_components/lib/blocs/location/location.dart b/vehicle-tracker/packages/digit_components/lib/blocs/location/location.dart
new file mode 100644
index 00000000..0e4096ba
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/blocs/location/location.dart
@@ -0,0 +1,189 @@
+import 'dart:async';
+
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:location/location.dart';
+
+part 'location.freezed.dart';
+
+typedef LocationStateEmitter = Emitter;
+
+/// LocationBLoC
+///
+/// Follow setup instructions for appropriate platform
+/// Android -
+/// iOS -
+/// macOS -
+class LocationBloc extends Bloc {
+ final Location location;
+
+ LocationBloc({required this.location}) : super(const LocationState()) {
+ on(_handleLoadLocation);
+ on(_handleRequestPermission);
+ on(_handleRequestService);
+ on(_handleSetLatLng);
+ on(_handleListenLocation);
+ }
+
+ FutureOr _handleRequestPermission(
+ RequestLocationPermissionEvent event,
+ LocationStateEmitter emit,
+ ) async {
+ emit(state.copyWith(loading: true));
+ try {
+ final permissions = await location.hasPermission();
+ final hasPermissions = [
+ PermissionStatus.granted,
+ PermissionStatus.grantedLimited,
+ ].contains(permissions);
+ emit(state.copyWith(hasPermissions: hasPermissions));
+ if (!hasPermissions) {
+ if (event.retry > 0) {
+ await location.requestPermission();
+ add(RequestLocationPermissionEvent(retry: event.retry - 1));
+ } else {
+ throw Exception('Location permission request rejected');
+ }
+ } else {
+ add(const LoadLocationEvent());
+ }
+ } catch (error) {
+ rethrow;
+ } finally {
+ emit(state.copyWith(loading: false));
+ }
+ }
+
+ FutureOr _handleRequestService(
+ RequestLocationServiceEvent event,
+ LocationStateEmitter emit,
+ ) async {
+ emit(state.copyWith(loading: true));
+ try {
+ final serviceEnabled = await location.serviceEnabled();
+ emit(state.copyWith(serviceEnabled: serviceEnabled));
+ if (!serviceEnabled) {
+ if (event.retry > 0) {
+ await location.requestService();
+ add(RequestLocationServiceEvent(retry: event.retry - 1));
+ } else {
+ throw Exception('Location service request rejected');
+ }
+ } else {
+ add(const LoadLocationEvent());
+ }
+ } catch (error) {
+ rethrow;
+ } finally {
+ emit(state.copyWith(loading: false));
+ }
+ }
+
+ FutureOr _handleListenLocation(
+ ListenLocationEvent event,
+ LocationStateEmitter emit,
+ ) async {
+ location.onLocationChanged.listen((locationData) {
+ add(LocationSetLatLngEvent(locationData: locationData));
+ });
+ }
+
+ FutureOr _handleLoadLocation(
+ LoadLocationEvent event,
+ LocationStateEmitter emit,
+ ) async {
+ emit(state.copyWith(loading: true));
+ try {
+ final permissions = await location.hasPermission();
+
+ if (![
+ PermissionStatus.granted,
+ PermissionStatus.grantedLimited,
+ ].contains(permissions)) {
+ emit(state.copyWith(hasPermissions: false));
+ add(const RequestLocationPermissionEvent());
+ return;
+ } else {
+ emit(state.copyWith(hasPermissions: true));
+ }
+
+ final serviceEnabled = await location.serviceEnabled();
+ emit(state.copyWith(serviceEnabled: serviceEnabled));
+
+ if (!serviceEnabled) {
+ add(const RequestLocationServiceEvent());
+ return;
+ }
+
+ add(const ListenLocationEvent());
+ final locationData = await location.getLocation();
+ add(LocationSetLatLngEvent(locationData: locationData));
+ } catch (error) {
+ rethrow;
+ } finally {
+ emit(state.copyWith(loading: false));
+ }
+ }
+
+ void _handleSetLatLng(
+ LocationSetLatLngEvent event,
+ LocationStateEmitter emit,
+ ) {
+ final locationData = event.locationData;
+
+ if ([
+ locationData.latitude,
+ locationData.longitude,
+ ].any((element) => element == null)) {
+ throw Exception('Could not fetch location data');
+ }
+
+ emit(state.copyWith(
+ latitude: locationData.latitude,
+ longitude: locationData.longitude,
+ accuracy: locationData.accuracy,
+ ));
+ }
+}
+
+@freezed
+class LocationEvent with _$LocationEvent {
+ const factory LocationEvent.load({
+ @Default(5) int retry,
+ }) = LoadLocationEvent;
+
+ const factory LocationEvent.listen({
+ @Default(5) int retry,
+ }) = ListenLocationEvent;
+
+ const factory LocationEvent.requestService({
+ @Default(5) int retry,
+ }) = RequestLocationServiceEvent;
+
+ const factory LocationEvent.requestPermission({
+ @Default(1) int retry,
+ }) = RequestLocationPermissionEvent;
+
+ const factory LocationEvent.setLatLng({
+ required LocationData locationData,
+ }) = LocationSetLatLngEvent;
+}
+
+@freezed
+class LocationState with _$LocationState {
+ const LocationState._();
+
+ const factory LocationState({
+ double? latitude,
+ double? longitude,
+ double? accuracy,
+ @Default(false) hasPermissions,
+ @Default(false) bool serviceEnabled,
+ @Default(false) bool loading,
+ }) = _LocationState;
+
+ String get latLngString {
+ if (latitude == null || longitude == null) return 'Undefined';
+ return [latitude, longitude].join(', ');
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/blocs/location/location.freezed.dart b/vehicle-tracker/packages/digit_components/lib/blocs/location/location.freezed.dart
new file mode 100644
index 00000000..895d57b4
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/blocs/location/location.freezed.dart
@@ -0,0 +1,1107 @@
+// coverage:ignore-file
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of 'location.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+ 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
+
+/// @nodoc
+mixin _$LocationEvent {
+ @optionalTypeArgs
+ TResult when({
+ required TResult Function(int retry) load,
+ required TResult Function(int retry) listen,
+ required TResult Function(int retry) requestService,
+ required TResult Function(int retry) requestPermission,
+ required TResult Function(LocationData locationData) setLatLng,
+ }) =>
+ throw _privateConstructorUsedError;
+ @optionalTypeArgs
+ TResult? whenOrNull({
+ TResult? Function(int retry)? load,
+ TResult? Function(int retry)? listen,
+ TResult? Function(int retry)? requestService,
+ TResult? Function(int retry)? requestPermission,
+ TResult? Function(LocationData locationData)? setLatLng,
+ }) =>
+ throw _privateConstructorUsedError;
+ @optionalTypeArgs
+ TResult maybeWhen({
+ TResult Function(int retry)? load,
+ TResult Function(int retry)? listen,
+ TResult Function(int retry)? requestService,
+ TResult Function(int retry)? requestPermission,
+ TResult Function(LocationData locationData)? setLatLng,
+ required TResult orElse(),
+ }) =>
+ throw _privateConstructorUsedError;
+ @optionalTypeArgs
+ TResult map({
+ required TResult Function(LoadLocationEvent value) load,
+ required TResult Function(ListenLocationEvent value) listen,
+ required TResult Function(RequestLocationServiceEvent value) requestService,
+ required TResult Function(RequestLocationPermissionEvent value)
+ requestPermission,
+ required TResult Function(LocationSetLatLngEvent value) setLatLng,
+ }) =>
+ throw _privateConstructorUsedError;
+ @optionalTypeArgs
+ TResult? mapOrNull({
+ TResult? Function(LoadLocationEvent value)? load,
+ TResult? Function(ListenLocationEvent value)? listen,
+ TResult? Function(RequestLocationServiceEvent value)? requestService,
+ TResult? Function(RequestLocationPermissionEvent value)? requestPermission,
+ TResult? Function(LocationSetLatLngEvent value)? setLatLng,
+ }) =>
+ throw _privateConstructorUsedError;
+ @optionalTypeArgs
+ TResult maybeMap({
+ TResult Function(LoadLocationEvent value)? load,
+ TResult Function(ListenLocationEvent value)? listen,
+ TResult Function(RequestLocationServiceEvent value)? requestService,
+ TResult Function(RequestLocationPermissionEvent value)? requestPermission,
+ TResult Function(LocationSetLatLngEvent value)? setLatLng,
+ required TResult orElse(),
+ }) =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $LocationEventCopyWith<$Res> {
+ factory $LocationEventCopyWith(
+ LocationEvent value, $Res Function(LocationEvent) then) =
+ _$LocationEventCopyWithImpl<$Res, LocationEvent>;
+}
+
+/// @nodoc
+class _$LocationEventCopyWithImpl<$Res, $Val extends LocationEvent>
+ implements $LocationEventCopyWith<$Res> {
+ _$LocationEventCopyWithImpl(this._value, this._then);
+
+ // ignore: unused_field
+ final $Val _value;
+ // ignore: unused_field
+ final $Res Function($Val) _then;
+}
+
+/// @nodoc
+abstract class _$$LoadLocationEventCopyWith<$Res> {
+ factory _$$LoadLocationEventCopyWith(
+ _$LoadLocationEvent value, $Res Function(_$LoadLocationEvent) then) =
+ __$$LoadLocationEventCopyWithImpl<$Res>;
+ @useResult
+ $Res call({int retry});
+}
+
+/// @nodoc
+class __$$LoadLocationEventCopyWithImpl<$Res>
+ extends _$LocationEventCopyWithImpl<$Res, _$LoadLocationEvent>
+ implements _$$LoadLocationEventCopyWith<$Res> {
+ __$$LoadLocationEventCopyWithImpl(
+ _$LoadLocationEvent _value, $Res Function(_$LoadLocationEvent) _then)
+ : super(_value, _then);
+
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? retry = null,
+ }) {
+ return _then(_$LoadLocationEvent(
+ retry: null == retry
+ ? _value.retry
+ : retry // ignore: cast_nullable_to_non_nullable
+ as int,
+ ));
+ }
+}
+
+/// @nodoc
+
+class _$LoadLocationEvent implements LoadLocationEvent {
+ const _$LoadLocationEvent({this.retry = 5});
+
+ @override
+ @JsonKey()
+ final int retry;
+
+ @override
+ String toString() {
+ return 'LocationEvent.load(retry: $retry)';
+ }
+
+ @override
+ bool operator ==(dynamic other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$LoadLocationEvent &&
+ (identical(other.retry, retry) || other.retry == retry));
+ }
+
+ @override
+ int get hashCode => Object.hash(runtimeType, retry);
+
+ @JsonKey(ignore: true)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$LoadLocationEventCopyWith<_$LoadLocationEvent> get copyWith =>
+ __$$LoadLocationEventCopyWithImpl<_$LoadLocationEvent>(this, _$identity);
+
+ @override
+ @optionalTypeArgs
+ TResult when({
+ required TResult Function(int retry) load,
+ required TResult Function(int retry) listen,
+ required TResult Function(int retry) requestService,
+ required TResult Function(int retry) requestPermission,
+ required TResult Function(LocationData locationData) setLatLng,
+ }) {
+ return load(retry);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? whenOrNull({
+ TResult? Function(int retry)? load,
+ TResult? Function(int retry)? listen,
+ TResult? Function(int retry)? requestService,
+ TResult? Function(int retry)? requestPermission,
+ TResult? Function(LocationData locationData)? setLatLng,
+ }) {
+ return load?.call(retry);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeWhen({
+ TResult Function(int retry)? load,
+ TResult Function(int retry)? listen,
+ TResult Function(int retry)? requestService,
+ TResult Function(int retry)? requestPermission,
+ TResult Function(LocationData locationData)? setLatLng,
+ required TResult orElse(),
+ }) {
+ if (load != null) {
+ return load(retry);
+ }
+ return orElse();
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult map({
+ required TResult Function(LoadLocationEvent value) load,
+ required TResult Function(ListenLocationEvent value) listen,
+ required TResult Function(RequestLocationServiceEvent value) requestService,
+ required TResult Function(RequestLocationPermissionEvent value)
+ requestPermission,
+ required TResult Function(LocationSetLatLngEvent value) setLatLng,
+ }) {
+ return load(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? mapOrNull({
+ TResult? Function(LoadLocationEvent value)? load,
+ TResult? Function(ListenLocationEvent value)? listen,
+ TResult? Function(RequestLocationServiceEvent value)? requestService,
+ TResult? Function(RequestLocationPermissionEvent value)? requestPermission,
+ TResult? Function(LocationSetLatLngEvent value)? setLatLng,
+ }) {
+ return load?.call(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeMap({
+ TResult Function(LoadLocationEvent value)? load,
+ TResult Function(ListenLocationEvent value)? listen,
+ TResult Function(RequestLocationServiceEvent value)? requestService,
+ TResult Function(RequestLocationPermissionEvent value)? requestPermission,
+ TResult Function(LocationSetLatLngEvent value)? setLatLng,
+ required TResult orElse(),
+ }) {
+ if (load != null) {
+ return load(this);
+ }
+ return orElse();
+ }
+}
+
+abstract class LoadLocationEvent implements LocationEvent {
+ const factory LoadLocationEvent({final int retry}) = _$LoadLocationEvent;
+
+ int get retry;
+ @JsonKey(ignore: true)
+ _$$LoadLocationEventCopyWith<_$LoadLocationEvent> get copyWith =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class _$$ListenLocationEventCopyWith<$Res> {
+ factory _$$ListenLocationEventCopyWith(_$ListenLocationEvent value,
+ $Res Function(_$ListenLocationEvent) then) =
+ __$$ListenLocationEventCopyWithImpl<$Res>;
+ @useResult
+ $Res call({int retry});
+}
+
+/// @nodoc
+class __$$ListenLocationEventCopyWithImpl<$Res>
+ extends _$LocationEventCopyWithImpl<$Res, _$ListenLocationEvent>
+ implements _$$ListenLocationEventCopyWith<$Res> {
+ __$$ListenLocationEventCopyWithImpl(
+ _$ListenLocationEvent _value, $Res Function(_$ListenLocationEvent) _then)
+ : super(_value, _then);
+
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? retry = null,
+ }) {
+ return _then(_$ListenLocationEvent(
+ retry: null == retry
+ ? _value.retry
+ : retry // ignore: cast_nullable_to_non_nullable
+ as int,
+ ));
+ }
+}
+
+/// @nodoc
+
+class _$ListenLocationEvent implements ListenLocationEvent {
+ const _$ListenLocationEvent({this.retry = 5});
+
+ @override
+ @JsonKey()
+ final int retry;
+
+ @override
+ String toString() {
+ return 'LocationEvent.listen(retry: $retry)';
+ }
+
+ @override
+ bool operator ==(dynamic other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$ListenLocationEvent &&
+ (identical(other.retry, retry) || other.retry == retry));
+ }
+
+ @override
+ int get hashCode => Object.hash(runtimeType, retry);
+
+ @JsonKey(ignore: true)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$ListenLocationEventCopyWith<_$ListenLocationEvent> get copyWith =>
+ __$$ListenLocationEventCopyWithImpl<_$ListenLocationEvent>(
+ this, _$identity);
+
+ @override
+ @optionalTypeArgs
+ TResult when({
+ required TResult Function(int retry) load,
+ required TResult Function(int retry) listen,
+ required TResult Function(int retry) requestService,
+ required TResult Function(int retry) requestPermission,
+ required TResult Function(LocationData locationData) setLatLng,
+ }) {
+ return listen(retry);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? whenOrNull({
+ TResult? Function(int retry)? load,
+ TResult? Function(int retry)? listen,
+ TResult? Function(int retry)? requestService,
+ TResult? Function(int retry)? requestPermission,
+ TResult? Function(LocationData locationData)? setLatLng,
+ }) {
+ return listen?.call(retry);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeWhen({
+ TResult Function(int retry)? load,
+ TResult Function(int retry)? listen,
+ TResult Function(int retry)? requestService,
+ TResult Function(int retry)? requestPermission,
+ TResult Function(LocationData locationData)? setLatLng,
+ required TResult orElse(),
+ }) {
+ if (listen != null) {
+ return listen(retry);
+ }
+ return orElse();
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult map({
+ required TResult Function(LoadLocationEvent value) load,
+ required TResult Function(ListenLocationEvent value) listen,
+ required TResult Function(RequestLocationServiceEvent value) requestService,
+ required TResult Function(RequestLocationPermissionEvent value)
+ requestPermission,
+ required TResult Function(LocationSetLatLngEvent value) setLatLng,
+ }) {
+ return listen(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? mapOrNull({
+ TResult? Function(LoadLocationEvent value)? load,
+ TResult? Function(ListenLocationEvent value)? listen,
+ TResult? Function(RequestLocationServiceEvent value)? requestService,
+ TResult? Function(RequestLocationPermissionEvent value)? requestPermission,
+ TResult? Function(LocationSetLatLngEvent value)? setLatLng,
+ }) {
+ return listen?.call(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeMap({
+ TResult Function(LoadLocationEvent value)? load,
+ TResult Function(ListenLocationEvent value)? listen,
+ TResult Function(RequestLocationServiceEvent value)? requestService,
+ TResult Function(RequestLocationPermissionEvent value)? requestPermission,
+ TResult Function(LocationSetLatLngEvent value)? setLatLng,
+ required TResult orElse(),
+ }) {
+ if (listen != null) {
+ return listen(this);
+ }
+ return orElse();
+ }
+}
+
+abstract class ListenLocationEvent implements LocationEvent {
+ const factory ListenLocationEvent({final int retry}) = _$ListenLocationEvent;
+
+ int get retry;
+ @JsonKey(ignore: true)
+ _$$ListenLocationEventCopyWith<_$ListenLocationEvent> get copyWith =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class _$$RequestLocationServiceEventCopyWith<$Res> {
+ factory _$$RequestLocationServiceEventCopyWith(
+ _$RequestLocationServiceEvent value,
+ $Res Function(_$RequestLocationServiceEvent) then) =
+ __$$RequestLocationServiceEventCopyWithImpl<$Res>;
+ @useResult
+ $Res call({int retry});
+}
+
+/// @nodoc
+class __$$RequestLocationServiceEventCopyWithImpl<$Res>
+ extends _$LocationEventCopyWithImpl<$Res, _$RequestLocationServiceEvent>
+ implements _$$RequestLocationServiceEventCopyWith<$Res> {
+ __$$RequestLocationServiceEventCopyWithImpl(
+ _$RequestLocationServiceEvent _value,
+ $Res Function(_$RequestLocationServiceEvent) _then)
+ : super(_value, _then);
+
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? retry = null,
+ }) {
+ return _then(_$RequestLocationServiceEvent(
+ retry: null == retry
+ ? _value.retry
+ : retry // ignore: cast_nullable_to_non_nullable
+ as int,
+ ));
+ }
+}
+
+/// @nodoc
+
+class _$RequestLocationServiceEvent implements RequestLocationServiceEvent {
+ const _$RequestLocationServiceEvent({this.retry = 5});
+
+ @override
+ @JsonKey()
+ final int retry;
+
+ @override
+ String toString() {
+ return 'LocationEvent.requestService(retry: $retry)';
+ }
+
+ @override
+ bool operator ==(dynamic other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$RequestLocationServiceEvent &&
+ (identical(other.retry, retry) || other.retry == retry));
+ }
+
+ @override
+ int get hashCode => Object.hash(runtimeType, retry);
+
+ @JsonKey(ignore: true)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$RequestLocationServiceEventCopyWith<_$RequestLocationServiceEvent>
+ get copyWith => __$$RequestLocationServiceEventCopyWithImpl<
+ _$RequestLocationServiceEvent>(this, _$identity);
+
+ @override
+ @optionalTypeArgs
+ TResult when({
+ required TResult Function(int retry) load,
+ required TResult Function(int retry) listen,
+ required TResult Function(int retry) requestService,
+ required TResult Function(int retry) requestPermission,
+ required TResult Function(LocationData locationData) setLatLng,
+ }) {
+ return requestService(retry);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? whenOrNull({
+ TResult? Function(int retry)? load,
+ TResult? Function(int retry)? listen,
+ TResult? Function(int retry)? requestService,
+ TResult? Function(int retry)? requestPermission,
+ TResult? Function(LocationData locationData)? setLatLng,
+ }) {
+ return requestService?.call(retry);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeWhen({
+ TResult Function(int retry)? load,
+ TResult Function(int retry)? listen,
+ TResult Function(int retry)? requestService,
+ TResult Function(int retry)? requestPermission,
+ TResult Function(LocationData locationData)? setLatLng,
+ required TResult orElse(),
+ }) {
+ if (requestService != null) {
+ return requestService(retry);
+ }
+ return orElse();
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult map({
+ required TResult Function(LoadLocationEvent value) load,
+ required TResult Function(ListenLocationEvent value) listen,
+ required TResult Function(RequestLocationServiceEvent value) requestService,
+ required TResult Function(RequestLocationPermissionEvent value)
+ requestPermission,
+ required TResult Function(LocationSetLatLngEvent value) setLatLng,
+ }) {
+ return requestService(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? mapOrNull({
+ TResult? Function(LoadLocationEvent value)? load,
+ TResult? Function(ListenLocationEvent value)? listen,
+ TResult? Function(RequestLocationServiceEvent value)? requestService,
+ TResult? Function(RequestLocationPermissionEvent value)? requestPermission,
+ TResult? Function(LocationSetLatLngEvent value)? setLatLng,
+ }) {
+ return requestService?.call(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeMap({
+ TResult Function(LoadLocationEvent value)? load,
+ TResult Function(ListenLocationEvent value)? listen,
+ TResult Function(RequestLocationServiceEvent value)? requestService,
+ TResult Function(RequestLocationPermissionEvent value)? requestPermission,
+ TResult Function(LocationSetLatLngEvent value)? setLatLng,
+ required TResult orElse(),
+ }) {
+ if (requestService != null) {
+ return requestService(this);
+ }
+ return orElse();
+ }
+}
+
+abstract class RequestLocationServiceEvent implements LocationEvent {
+ const factory RequestLocationServiceEvent({final int retry}) =
+ _$RequestLocationServiceEvent;
+
+ int get retry;
+ @JsonKey(ignore: true)
+ _$$RequestLocationServiceEventCopyWith<_$RequestLocationServiceEvent>
+ get copyWith => throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class _$$RequestLocationPermissionEventCopyWith<$Res> {
+ factory _$$RequestLocationPermissionEventCopyWith(
+ _$RequestLocationPermissionEvent value,
+ $Res Function(_$RequestLocationPermissionEvent) then) =
+ __$$RequestLocationPermissionEventCopyWithImpl<$Res>;
+ @useResult
+ $Res call({int retry});
+}
+
+/// @nodoc
+class __$$RequestLocationPermissionEventCopyWithImpl<$Res>
+ extends _$LocationEventCopyWithImpl<$Res, _$RequestLocationPermissionEvent>
+ implements _$$RequestLocationPermissionEventCopyWith<$Res> {
+ __$$RequestLocationPermissionEventCopyWithImpl(
+ _$RequestLocationPermissionEvent _value,
+ $Res Function(_$RequestLocationPermissionEvent) _then)
+ : super(_value, _then);
+
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? retry = null,
+ }) {
+ return _then(_$RequestLocationPermissionEvent(
+ retry: null == retry
+ ? _value.retry
+ : retry // ignore: cast_nullable_to_non_nullable
+ as int,
+ ));
+ }
+}
+
+/// @nodoc
+
+class _$RequestLocationPermissionEvent
+ implements RequestLocationPermissionEvent {
+ const _$RequestLocationPermissionEvent({this.retry = 1});
+
+ @override
+ @JsonKey()
+ final int retry;
+
+ @override
+ String toString() {
+ return 'LocationEvent.requestPermission(retry: $retry)';
+ }
+
+ @override
+ bool operator ==(dynamic other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$RequestLocationPermissionEvent &&
+ (identical(other.retry, retry) || other.retry == retry));
+ }
+
+ @override
+ int get hashCode => Object.hash(runtimeType, retry);
+
+ @JsonKey(ignore: true)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$RequestLocationPermissionEventCopyWith<_$RequestLocationPermissionEvent>
+ get copyWith => __$$RequestLocationPermissionEventCopyWithImpl<
+ _$RequestLocationPermissionEvent>(this, _$identity);
+
+ @override
+ @optionalTypeArgs
+ TResult when({
+ required TResult Function(int retry) load,
+ required TResult Function(int retry) listen,
+ required TResult Function(int retry) requestService,
+ required TResult Function(int retry) requestPermission,
+ required TResult Function(LocationData locationData) setLatLng,
+ }) {
+ return requestPermission(retry);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? whenOrNull({
+ TResult? Function(int retry)? load,
+ TResult? Function(int retry)? listen,
+ TResult? Function(int retry)? requestService,
+ TResult? Function(int retry)? requestPermission,
+ TResult? Function(LocationData locationData)? setLatLng,
+ }) {
+ return requestPermission?.call(retry);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeWhen({
+ TResult Function(int retry)? load,
+ TResult Function(int retry)? listen,
+ TResult Function(int retry)? requestService,
+ TResult Function(int retry)? requestPermission,
+ TResult Function(LocationData locationData)? setLatLng,
+ required TResult orElse(),
+ }) {
+ if (requestPermission != null) {
+ return requestPermission(retry);
+ }
+ return orElse();
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult map({
+ required TResult Function(LoadLocationEvent value) load,
+ required TResult Function(ListenLocationEvent value) listen,
+ required TResult Function(RequestLocationServiceEvent value) requestService,
+ required TResult Function(RequestLocationPermissionEvent value)
+ requestPermission,
+ required TResult Function(LocationSetLatLngEvent value) setLatLng,
+ }) {
+ return requestPermission(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? mapOrNull({
+ TResult? Function(LoadLocationEvent value)? load,
+ TResult? Function(ListenLocationEvent value)? listen,
+ TResult? Function(RequestLocationServiceEvent value)? requestService,
+ TResult? Function(RequestLocationPermissionEvent value)? requestPermission,
+ TResult? Function(LocationSetLatLngEvent value)? setLatLng,
+ }) {
+ return requestPermission?.call(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeMap({
+ TResult Function(LoadLocationEvent value)? load,
+ TResult Function(ListenLocationEvent value)? listen,
+ TResult Function(RequestLocationServiceEvent value)? requestService,
+ TResult Function(RequestLocationPermissionEvent value)? requestPermission,
+ TResult Function(LocationSetLatLngEvent value)? setLatLng,
+ required TResult orElse(),
+ }) {
+ if (requestPermission != null) {
+ return requestPermission(this);
+ }
+ return orElse();
+ }
+}
+
+abstract class RequestLocationPermissionEvent implements LocationEvent {
+ const factory RequestLocationPermissionEvent({final int retry}) =
+ _$RequestLocationPermissionEvent;
+
+ int get retry;
+ @JsonKey(ignore: true)
+ _$$RequestLocationPermissionEventCopyWith<_$RequestLocationPermissionEvent>
+ get copyWith => throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class _$$LocationSetLatLngEventCopyWith<$Res> {
+ factory _$$LocationSetLatLngEventCopyWith(_$LocationSetLatLngEvent value,
+ $Res Function(_$LocationSetLatLngEvent) then) =
+ __$$LocationSetLatLngEventCopyWithImpl<$Res>;
+ @useResult
+ $Res call({LocationData locationData});
+}
+
+/// @nodoc
+class __$$LocationSetLatLngEventCopyWithImpl<$Res>
+ extends _$LocationEventCopyWithImpl<$Res, _$LocationSetLatLngEvent>
+ implements _$$LocationSetLatLngEventCopyWith<$Res> {
+ __$$LocationSetLatLngEventCopyWithImpl(_$LocationSetLatLngEvent _value,
+ $Res Function(_$LocationSetLatLngEvent) _then)
+ : super(_value, _then);
+
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? locationData = null,
+ }) {
+ return _then(_$LocationSetLatLngEvent(
+ locationData: null == locationData
+ ? _value.locationData
+ : locationData // ignore: cast_nullable_to_non_nullable
+ as LocationData,
+ ));
+ }
+}
+
+/// @nodoc
+
+class _$LocationSetLatLngEvent implements LocationSetLatLngEvent {
+ const _$LocationSetLatLngEvent({required this.locationData});
+
+ @override
+ final LocationData locationData;
+
+ @override
+ String toString() {
+ return 'LocationEvent.setLatLng(locationData: $locationData)';
+ }
+
+ @override
+ bool operator ==(dynamic other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$LocationSetLatLngEvent &&
+ (identical(other.locationData, locationData) ||
+ other.locationData == locationData));
+ }
+
+ @override
+ int get hashCode => Object.hash(runtimeType, locationData);
+
+ @JsonKey(ignore: true)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$LocationSetLatLngEventCopyWith<_$LocationSetLatLngEvent> get copyWith =>
+ __$$LocationSetLatLngEventCopyWithImpl<_$LocationSetLatLngEvent>(
+ this, _$identity);
+
+ @override
+ @optionalTypeArgs
+ TResult when({
+ required TResult Function(int retry) load,
+ required TResult Function(int retry) listen,
+ required TResult Function(int retry) requestService,
+ required TResult Function(int retry) requestPermission,
+ required TResult Function(LocationData locationData) setLatLng,
+ }) {
+ return setLatLng(locationData);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? whenOrNull({
+ TResult? Function(int retry)? load,
+ TResult? Function(int retry)? listen,
+ TResult? Function(int retry)? requestService,
+ TResult? Function(int retry)? requestPermission,
+ TResult? Function(LocationData locationData)? setLatLng,
+ }) {
+ return setLatLng?.call(locationData);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeWhen({
+ TResult Function(int retry)? load,
+ TResult Function(int retry)? listen,
+ TResult Function(int retry)? requestService,
+ TResult Function(int retry)? requestPermission,
+ TResult Function(LocationData locationData)? setLatLng,
+ required TResult orElse(),
+ }) {
+ if (setLatLng != null) {
+ return setLatLng(locationData);
+ }
+ return orElse();
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult map({
+ required TResult Function(LoadLocationEvent value) load,
+ required TResult Function(ListenLocationEvent value) listen,
+ required TResult Function(RequestLocationServiceEvent value) requestService,
+ required TResult Function(RequestLocationPermissionEvent value)
+ requestPermission,
+ required TResult Function(LocationSetLatLngEvent value) setLatLng,
+ }) {
+ return setLatLng(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult? mapOrNull({
+ TResult? Function(LoadLocationEvent value)? load,
+ TResult? Function(ListenLocationEvent value)? listen,
+ TResult? Function(RequestLocationServiceEvent value)? requestService,
+ TResult? Function(RequestLocationPermissionEvent value)? requestPermission,
+ TResult? Function(LocationSetLatLngEvent value)? setLatLng,
+ }) {
+ return setLatLng?.call(this);
+ }
+
+ @override
+ @optionalTypeArgs
+ TResult maybeMap({
+ TResult Function(LoadLocationEvent value)? load,
+ TResult Function(ListenLocationEvent value)? listen,
+ TResult Function(RequestLocationServiceEvent value)? requestService,
+ TResult Function(RequestLocationPermissionEvent value)? requestPermission,
+ TResult Function(LocationSetLatLngEvent value)? setLatLng,
+ required TResult orElse(),
+ }) {
+ if (setLatLng != null) {
+ return setLatLng(this);
+ }
+ return orElse();
+ }
+}
+
+abstract class LocationSetLatLngEvent implements LocationEvent {
+ const factory LocationSetLatLngEvent(
+ {required final LocationData locationData}) = _$LocationSetLatLngEvent;
+
+ LocationData get locationData;
+ @JsonKey(ignore: true)
+ _$$LocationSetLatLngEventCopyWith<_$LocationSetLatLngEvent> get copyWith =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+mixin _$LocationState {
+ double? get latitude => throw _privateConstructorUsedError;
+ double? get longitude => throw _privateConstructorUsedError;
+ double? get accuracy => throw _privateConstructorUsedError;
+ dynamic get hasPermissions => throw _privateConstructorUsedError;
+ bool get serviceEnabled => throw _privateConstructorUsedError;
+ bool get loading => throw _privateConstructorUsedError;
+
+ @JsonKey(ignore: true)
+ $LocationStateCopyWith get copyWith =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $LocationStateCopyWith<$Res> {
+ factory $LocationStateCopyWith(
+ LocationState value, $Res Function(LocationState) then) =
+ _$LocationStateCopyWithImpl<$Res, LocationState>;
+ @useResult
+ $Res call(
+ {double? latitude,
+ double? longitude,
+ double? accuracy,
+ dynamic hasPermissions,
+ bool serviceEnabled,
+ bool loading});
+}
+
+/// @nodoc
+class _$LocationStateCopyWithImpl<$Res, $Val extends LocationState>
+ implements $LocationStateCopyWith<$Res> {
+ _$LocationStateCopyWithImpl(this._value, this._then);
+
+ // ignore: unused_field
+ final $Val _value;
+ // ignore: unused_field
+ final $Res Function($Val) _then;
+
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? latitude = freezed,
+ Object? longitude = freezed,
+ Object? accuracy = freezed,
+ Object? hasPermissions = freezed,
+ Object? serviceEnabled = null,
+ Object? loading = null,
+ }) {
+ return _then(_value.copyWith(
+ latitude: freezed == latitude
+ ? _value.latitude
+ : latitude // ignore: cast_nullable_to_non_nullable
+ as double?,
+ longitude: freezed == longitude
+ ? _value.longitude
+ : longitude // ignore: cast_nullable_to_non_nullable
+ as double?,
+ accuracy: freezed == accuracy
+ ? _value.accuracy
+ : accuracy // ignore: cast_nullable_to_non_nullable
+ as double?,
+ hasPermissions: freezed == hasPermissions
+ ? _value.hasPermissions
+ : hasPermissions // ignore: cast_nullable_to_non_nullable
+ as dynamic,
+ serviceEnabled: null == serviceEnabled
+ ? _value.serviceEnabled
+ : serviceEnabled // ignore: cast_nullable_to_non_nullable
+ as bool,
+ loading: null == loading
+ ? _value.loading
+ : loading // ignore: cast_nullable_to_non_nullable
+ as bool,
+ ) as $Val);
+ }
+}
+
+/// @nodoc
+abstract class _$$_LocationStateCopyWith<$Res>
+ implements $LocationStateCopyWith<$Res> {
+ factory _$$_LocationStateCopyWith(
+ _$_LocationState value, $Res Function(_$_LocationState) then) =
+ __$$_LocationStateCopyWithImpl<$Res>;
+ @override
+ @useResult
+ $Res call(
+ {double? latitude,
+ double? longitude,
+ double? accuracy,
+ dynamic hasPermissions,
+ bool serviceEnabled,
+ bool loading});
+}
+
+/// @nodoc
+class __$$_LocationStateCopyWithImpl<$Res>
+ extends _$LocationStateCopyWithImpl<$Res, _$_LocationState>
+ implements _$$_LocationStateCopyWith<$Res> {
+ __$$_LocationStateCopyWithImpl(
+ _$_LocationState _value, $Res Function(_$_LocationState) _then)
+ : super(_value, _then);
+
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? latitude = freezed,
+ Object? longitude = freezed,
+ Object? accuracy = freezed,
+ Object? hasPermissions = freezed,
+ Object? serviceEnabled = null,
+ Object? loading = null,
+ }) {
+ return _then(_$_LocationState(
+ latitude: freezed == latitude
+ ? _value.latitude
+ : latitude // ignore: cast_nullable_to_non_nullable
+ as double?,
+ longitude: freezed == longitude
+ ? _value.longitude
+ : longitude // ignore: cast_nullable_to_non_nullable
+ as double?,
+ accuracy: freezed == accuracy
+ ? _value.accuracy
+ : accuracy // ignore: cast_nullable_to_non_nullable
+ as double?,
+ hasPermissions:
+ freezed == hasPermissions ? _value.hasPermissions! : hasPermissions,
+ serviceEnabled: null == serviceEnabled
+ ? _value.serviceEnabled
+ : serviceEnabled // ignore: cast_nullable_to_non_nullable
+ as bool,
+ loading: null == loading
+ ? _value.loading
+ : loading // ignore: cast_nullable_to_non_nullable
+ as bool,
+ ));
+ }
+}
+
+/// @nodoc
+
+class _$_LocationState extends _LocationState {
+ const _$_LocationState(
+ {this.latitude,
+ this.longitude,
+ this.accuracy,
+ this.hasPermissions = false,
+ this.serviceEnabled = false,
+ this.loading = false})
+ : super._();
+
+ @override
+ final double? latitude;
+ @override
+ final double? longitude;
+ @override
+ final double? accuracy;
+ @override
+ @JsonKey()
+ final dynamic hasPermissions;
+ @override
+ @JsonKey()
+ final bool serviceEnabled;
+ @override
+ @JsonKey()
+ final bool loading;
+
+ @override
+ String toString() {
+ return 'LocationState(latitude: $latitude, longitude: $longitude, accuracy: $accuracy, hasPermissions: $hasPermissions, serviceEnabled: $serviceEnabled, loading: $loading)';
+ }
+
+ @override
+ bool operator ==(dynamic other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$_LocationState &&
+ (identical(other.latitude, latitude) ||
+ other.latitude == latitude) &&
+ (identical(other.longitude, longitude) ||
+ other.longitude == longitude) &&
+ (identical(other.accuracy, accuracy) ||
+ other.accuracy == accuracy) &&
+ const DeepCollectionEquality()
+ .equals(other.hasPermissions, hasPermissions) &&
+ (identical(other.serviceEnabled, serviceEnabled) ||
+ other.serviceEnabled == serviceEnabled) &&
+ (identical(other.loading, loading) || other.loading == loading));
+ }
+
+ @override
+ int get hashCode => Object.hash(
+ runtimeType,
+ latitude,
+ longitude,
+ accuracy,
+ const DeepCollectionEquality().hash(hasPermissions),
+ serviceEnabled,
+ loading);
+
+ @JsonKey(ignore: true)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$_LocationStateCopyWith<_$_LocationState> get copyWith =>
+ __$$_LocationStateCopyWithImpl<_$_LocationState>(this, _$identity);
+}
+
+abstract class _LocationState extends LocationState {
+ const factory _LocationState(
+ {final double? latitude,
+ final double? longitude,
+ final double? accuracy,
+ final dynamic hasPermissions,
+ final bool serviceEnabled,
+ final bool loading}) = _$_LocationState;
+ const _LocationState._() : super._();
+
+ @override
+ double? get latitude;
+ @override
+ double? get longitude;
+ @override
+ double? get accuracy;
+ @override
+ dynamic get hasPermissions;
+ @override
+ bool get serviceEnabled;
+ @override
+ bool get loading;
+ @override
+ @JsonKey(ignore: true)
+ _$$_LocationStateCopyWith<_$_LocationState> get copyWith =>
+ throw _privateConstructorUsedError;
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/digit_components.dart b/vehicle-tracker/packages/digit_components/lib/digit_components.dart
new file mode 100644
index 00000000..57976831
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/digit_components.dart
@@ -0,0 +1,15 @@
+library digit_components;
+
+export 'blocs/location/location.dart';
+export 'theme/theme.dart';
+export 'utils/app_logger.dart';
+export 'widgets/widgets.dart';
+
+class DigitUi {
+ static const DigitUi _instance = DigitUi._internal();
+ static DigitUi get instance => _instance;
+
+ const DigitUi._internal();
+
+ Future initThemeComponents() async {}
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/models/digit_row_card/digit_row_card_model.dart b/vehicle-tracker/packages/digit_components/lib/models/digit_row_card/digit_row_card_model.dart
new file mode 100644
index 00000000..0fb63b1d
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/models/digit_row_card/digit_row_card_model.dart
@@ -0,0 +1,17 @@
+// ignore_for_file: invalid_annotation_target
+import 'package:freezed_annotation/freezed_annotation.dart';
+part 'digit_row_card_model.g.dart';
+part 'digit_row_card_model.freezed.dart';
+
+@freezed
+class DigitRowCardModel with _$DigitRowCardModel {
+ @JsonSerializable(explicitToJson: true)
+ const factory DigitRowCardModel({
+ required String label,
+ required String value,
+ @Default(false) bool isSelected,
+ }) = _DigitRowCardModel;
+
+ factory DigitRowCardModel.fromJson(Map json) =>
+ _$DigitRowCardModelFromJson(json);
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/models/digit_row_card/digit_row_card_model.freezed.dart b/vehicle-tracker/packages/digit_components/lib/models/digit_row_card/digit_row_card_model.freezed.dart
new file mode 100644
index 00000000..c31f3df0
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/models/digit_row_card/digit_row_card_model.freezed.dart
@@ -0,0 +1,191 @@
+// coverage:ignore-file
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of 'digit_row_card_model.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+ 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
+
+DigitRowCardModel _$DigitRowCardModelFromJson(Map json) {
+ return _DigitRowCardModel.fromJson(json);
+}
+
+/// @nodoc
+mixin _$DigitRowCardModel {
+ String get label => throw _privateConstructorUsedError;
+ String get value => throw _privateConstructorUsedError;
+ bool get isSelected => throw _privateConstructorUsedError;
+
+ Map toJson() => throw _privateConstructorUsedError;
+ @JsonKey(ignore: true)
+ $DigitRowCardModelCopyWith get copyWith =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $DigitRowCardModelCopyWith<$Res> {
+ factory $DigitRowCardModelCopyWith(
+ DigitRowCardModel value, $Res Function(DigitRowCardModel) then) =
+ _$DigitRowCardModelCopyWithImpl<$Res, DigitRowCardModel>;
+ @useResult
+ $Res call({String label, String value, bool isSelected});
+}
+
+/// @nodoc
+class _$DigitRowCardModelCopyWithImpl<$Res, $Val extends DigitRowCardModel>
+ implements $DigitRowCardModelCopyWith<$Res> {
+ _$DigitRowCardModelCopyWithImpl(this._value, this._then);
+
+ // ignore: unused_field
+ final $Val _value;
+ // ignore: unused_field
+ final $Res Function($Val) _then;
+
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? label = null,
+ Object? value = null,
+ Object? isSelected = null,
+ }) {
+ return _then(_value.copyWith(
+ label: null == label
+ ? _value.label
+ : label // ignore: cast_nullable_to_non_nullable
+ as String,
+ value: null == value
+ ? _value.value
+ : value // ignore: cast_nullable_to_non_nullable
+ as String,
+ isSelected: null == isSelected
+ ? _value.isSelected
+ : isSelected // ignore: cast_nullable_to_non_nullable
+ as bool,
+ ) as $Val);
+ }
+}
+
+/// @nodoc
+abstract class _$$_DigitRowCardModelCopyWith<$Res>
+ implements $DigitRowCardModelCopyWith<$Res> {
+ factory _$$_DigitRowCardModelCopyWith(_$_DigitRowCardModel value,
+ $Res Function(_$_DigitRowCardModel) then) =
+ __$$_DigitRowCardModelCopyWithImpl<$Res>;
+ @override
+ @useResult
+ $Res call({String label, String value, bool isSelected});
+}
+
+/// @nodoc
+class __$$_DigitRowCardModelCopyWithImpl<$Res>
+ extends _$DigitRowCardModelCopyWithImpl<$Res, _$_DigitRowCardModel>
+ implements _$$_DigitRowCardModelCopyWith<$Res> {
+ __$$_DigitRowCardModelCopyWithImpl(
+ _$_DigitRowCardModel _value, $Res Function(_$_DigitRowCardModel) _then)
+ : super(_value, _then);
+
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? label = null,
+ Object? value = null,
+ Object? isSelected = null,
+ }) {
+ return _then(_$_DigitRowCardModel(
+ label: null == label
+ ? _value.label
+ : label // ignore: cast_nullable_to_non_nullable
+ as String,
+ value: null == value
+ ? _value.value
+ : value // ignore: cast_nullable_to_non_nullable
+ as String,
+ isSelected: null == isSelected
+ ? _value.isSelected
+ : isSelected // ignore: cast_nullable_to_non_nullable
+ as bool,
+ ));
+ }
+}
+
+/// @nodoc
+
+@JsonSerializable(explicitToJson: true)
+class _$_DigitRowCardModel implements _DigitRowCardModel {
+ const _$_DigitRowCardModel(
+ {required this.label, required this.value, this.isSelected = false});
+
+ factory _$_DigitRowCardModel.fromJson(Map json) =>
+ _$$_DigitRowCardModelFromJson(json);
+
+ @override
+ final String label;
+ @override
+ final String value;
+ @override
+ @JsonKey()
+ final bool isSelected;
+
+ @override
+ String toString() {
+ return 'DigitRowCardModel(label: $label, value: $value, isSelected: $isSelected)';
+ }
+
+ @override
+ bool operator ==(dynamic other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$_DigitRowCardModel &&
+ (identical(other.label, label) || other.label == label) &&
+ (identical(other.value, value) || other.value == value) &&
+ (identical(other.isSelected, isSelected) ||
+ other.isSelected == isSelected));
+ }
+
+ @JsonKey(ignore: true)
+ @override
+ int get hashCode => Object.hash(runtimeType, label, value, isSelected);
+
+ @JsonKey(ignore: true)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$_DigitRowCardModelCopyWith<_$_DigitRowCardModel> get copyWith =>
+ __$$_DigitRowCardModelCopyWithImpl<_$_DigitRowCardModel>(
+ this, _$identity);
+
+ @override
+ Map toJson() {
+ return _$$_DigitRowCardModelToJson(
+ this,
+ );
+ }
+}
+
+abstract class _DigitRowCardModel implements DigitRowCardModel {
+ const factory _DigitRowCardModel(
+ {required final String label,
+ required final String value,
+ final bool isSelected}) = _$_DigitRowCardModel;
+
+ factory _DigitRowCardModel.fromJson(Map json) =
+ _$_DigitRowCardModel.fromJson;
+
+ @override
+ String get label;
+ @override
+ String get value;
+ @override
+ bool get isSelected;
+ @override
+ @JsonKey(ignore: true)
+ _$$_DigitRowCardModelCopyWith<_$_DigitRowCardModel> get copyWith =>
+ throw _privateConstructorUsedError;
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/models/digit_row_card/digit_row_card_model.g.dart b/vehicle-tracker/packages/digit_components/lib/models/digit_row_card/digit_row_card_model.g.dart
new file mode 100644
index 00000000..a23a338a
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/models/digit_row_card/digit_row_card_model.g.dart
@@ -0,0 +1,22 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'digit_row_card_model.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+_$_DigitRowCardModel _$$_DigitRowCardModelFromJson(Map json) =>
+ _$_DigitRowCardModel(
+ label: json['label'] as String,
+ value: json['value'] as String,
+ isSelected: json['isSelected'] as bool? ?? false,
+ );
+
+Map _$$_DigitRowCardModelToJson(
+ _$_DigitRowCardModel instance) =>
+ {
+ 'label': instance.label,
+ 'value': instance.value,
+ 'isSelected': instance.isSelected,
+ };
diff --git a/vehicle-tracker/packages/digit_components/lib/models/digit_table_model.dart b/vehicle-tracker/packages/digit_components/lib/models/digit_table_model.dart
new file mode 100644
index 00000000..d0aff146
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/models/digit_table_model.dart
@@ -0,0 +1,29 @@
+import 'package:flutter/material.dart';
+
+class TableHeader {
+ final String label;
+ final ValueChanged? callBack;
+ bool? isSortingRequired = false;
+ bool? isAscendingOrder;
+ String? cellKey;
+ TableHeader(
+ this.label, {
+ this.callBack,
+ this.isSortingRequired,
+ this.isAscendingOrder,
+ this.cellKey,
+ });
+}
+
+class TableDataRow {
+ final List tableRow;
+ TableDataRow(this.tableRow);
+}
+
+class TableData {
+ final String label;
+ final TextStyle? style;
+ final String? cellKey;
+ ValueChanged? callBack;
+ TableData(this.label, {this.style, this.callBack, this.cellKey});
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/theme/colors.dart b/vehicle-tracker/packages/digit_components/lib/theme/colors.dart
new file mode 100644
index 00000000..b5159031
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/theme/colors.dart
@@ -0,0 +1,28 @@
+import 'dart:ui';
+
+class DigitColors {
+ const DigitColors();
+
+ Color get burningOrange => const Color(0xFFF47738);
+ Color get regalBlue => const Color(0xFF0B4B66);
+ Color get woodsmokeBlack => const Color(0xFF0B0C0C);
+ Color get black => const Color(0xFF000000);
+ Color get davyGray => const Color(0xFF505A5F);
+ Color get cloudGray => const Color(0xFFB1B4B6);
+ Color get seaShellGray => const Color(0xFFEEEEEE);
+ Color get white => const Color(0xFFFFFFFF);
+ Color get alabasterWhite => const Color(0xFFFAFAFA);
+ Color get quillGray => const Color(0xFFD6D5D4);
+ Color get paleRose => const Color(0xFFEFC7C1);
+ Color get paleLeafGreen => const Color(0xFFBAD6C9);
+ Color get tropicalBlue => const Color(0xFFC7E0F1);
+ Color get bonePink => const Color(0xFFE6D7D0);
+ Color get lavaRed => const Color(0xFFD4351C);
+ Color get darkSpringGreen => const Color(0xFF00703C);
+ Color get curiousBlue => const Color(0xFF3498DB);
+ Color get waterBlue => const Color(0xFF048BD0);
+ Color get sunGlowYellow => const Color(0xFFFBC02D);
+ Color get darkOrchid => const Color(0xFF8E29BF);
+ Color get mangoOrange => const Color(0xFFEA8A3B);
+ Color get pacificBlue => const Color(0xFF0BABDE);
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/theme/digit_theme.dart b/vehicle-tracker/packages/digit_components/lib/theme/digit_theme.dart
new file mode 100644
index 00000000..a9dda5bb
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/theme/digit_theme.dart
@@ -0,0 +1,202 @@
+library digit_theme;
+
+import 'package:digit_components/theme/colors.dart';
+import 'package:digit_components/theme/typography.dart';
+import 'package:flutter/material.dart';
+
+const kPadding = 8.0;
+
+class DigitTheme {
+ static const DigitTheme _instance = DigitTheme._internal();
+
+ static DigitTheme get instance => _instance;
+
+ DigitColors get colors => const DigitColors();
+
+ DigitMobileTypography get mobileTypography => DigitMobileTypography(
+ normalBase: const TextStyle(
+ fontFamily: 'Roboto',
+ ),
+ displayBase: const TextStyle(
+ fontFamily: 'Roboto',
+ ),
+ light: colors.davyGray,
+ normal: colors.woodsmokeBlack,
+ );
+
+ const DigitTheme._internal();
+
+ ThemeData get mobileTheme {
+ const Border(top: BorderSide());
+
+ return ThemeData(
+ colorScheme: colorScheme,
+ scaffoldBackgroundColor: colorScheme.background,
+ textTheme: mobileTypography.textTheme,
+ appBarTheme: const AppBarTheme(elevation: 0),
+ elevatedButtonTheme: elevatedButtonTheme,
+ outlinedButtonTheme: outlinedButtonTheme,
+ textButtonTheme: textButtonTheme,
+ cardTheme: cardTheme,
+ inputDecorationTheme: inputDecorationTheme,
+ dialogTheme: dialogTheme,
+ );
+ }
+
+ ColorScheme get colorScheme => ColorScheme(
+ brightness: Brightness.light,
+ primary: colors.regalBlue,
+ onPrimary: colors.white,
+ secondary: colors.burningOrange,
+ onSecondary: colors.white,
+ error: colors.lavaRed,
+ onError: colors.white,
+ background: colors.seaShellGray,
+ onBackground: colors.woodsmokeBlack,
+ surface: colors.alabasterWhite,
+ onSurface: colors.woodsmokeBlack,
+ onSurfaceVariant: colors.darkSpringGreen,
+ tertiaryContainer: colors.tropicalBlue,
+ inversePrimary: colors.paleLeafGreen,
+ surfaceTint: colors.waterBlue,
+ outline: colors.quillGray,
+ shadow: colors.davyGray,
+ tertiary: colors.paleRose,
+ onTertiaryContainer: colors.curiousBlue,
+ );
+
+ EdgeInsets get buttonPadding => const EdgeInsets.symmetric(
+ vertical: kPadding,
+ horizontal: kPadding * 2,
+ );
+
+ EdgeInsets get containerMargin => const EdgeInsets.all(kPadding);
+
+ EdgeInsets get verticalMargin => const EdgeInsets.symmetric(
+ vertical: kPadding,
+ );
+
+ Duration get toastDuration => const Duration(seconds: 2);
+
+ OutlinedBorder get buttonBorder => const RoundedRectangleBorder(
+ borderRadius: BorderRadius.all(Radius.zero),
+ );
+
+ ElevatedButtonThemeData get elevatedButtonTheme => ElevatedButtonThemeData(
+ style: ElevatedButton.styleFrom(
+ shape: buttonBorder,
+ padding: buttonPadding,
+ backgroundColor: colorScheme.secondary,
+ foregroundColor: colorScheme.onSecondary,
+ disabledBackgroundColor: colorScheme.secondary.withOpacity(
+ 0.5,
+ ),
+ disabledForegroundColor: colorScheme.onSecondary,
+ elevation: 0,
+ ),
+ );
+
+ OutlinedButtonThemeData get outlinedButtonTheme => OutlinedButtonThemeData(
+ style: OutlinedButton.styleFrom(
+ foregroundColor: colorScheme.secondary,
+ // side: BorderSide(color: colorScheme.secondary),
+ padding: buttonPadding,
+ ),
+ );
+
+ TextButtonThemeData get textButtonTheme => TextButtonThemeData(
+ style: TextButton.styleFrom(
+ shape: buttonBorder,
+ padding: buttonPadding,
+ textStyle: const TextStyle(fontSize: 16),
+ foregroundColor: colorScheme.secondary,
+ ),
+ );
+
+ CardTheme get cardTheme => const CardTheme(
+ margin: EdgeInsets.fromLTRB(kPadding, kPadding * 2, kPadding, 0),
+ elevation: 1,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(4),
+ ),
+ ),
+ );
+
+ InputDecorationTheme get inputDecorationTheme => InputDecorationTheme(
+ enabledBorder: OutlineInputBorder(
+ borderRadius: const BorderRadius.all(
+ Radius.circular(
+ 0,
+ ),
+ ),
+ borderSide: BorderSide(
+ color: colors.davyGray,
+ ),
+ ),
+ focusedBorder: OutlineInputBorder(
+ borderRadius: const BorderRadius.all(
+ Radius.circular(
+ 0,
+ ),
+ ),
+ borderSide: BorderSide(
+ color: colors.burningOrange,
+ width: 2,
+ ),
+ ),
+ disabledBorder: OutlineInputBorder(
+ borderRadius: const BorderRadius.all(
+ Radius.circular(
+ 0,
+ ),
+ ),
+ borderSide: BorderSide(color: colors.cloudGray, width: 2),
+ ),
+ contentPadding: const EdgeInsets.all(12),
+ isDense: true,
+ isCollapsed: true,
+ floatingLabelBehavior: FloatingLabelBehavior.never,
+ errorBorder: OutlineInputBorder(
+ borderRadius: const BorderRadius.all(
+ Radius.circular(
+ 0,
+ ),
+ ),
+ borderSide: BorderSide(
+ color: colors.lavaRed,
+ ),
+ ),
+ focusedErrorBorder: OutlineInputBorder(
+ borderRadius: const BorderRadius.all(
+ Radius.circular(
+ 0,
+ ),
+ ),
+ borderSide: BorderSide(color: colors.lavaRed, width: 2),
+ ),
+ );
+
+ DialogTheme get dialogTheme => DialogTheme(
+ titleTextStyle: mobileTypography.headingL,
+ contentTextStyle: mobileTypography.bodyL,
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.all(
+ Radius.circular(
+ 4,
+ ),
+ ),
+ ),
+ actionsPadding: const EdgeInsets.all(kPadding),
+ );
+
+ BorderSide get tableCellBorder => BorderSide(
+ color: colorScheme.outline,
+ width: 0.5,
+ );
+
+ BorderSide get tableCellStrongBorder => BorderSide(
+ color: colorScheme.outline,
+ width: 2,
+ );
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/theme/theme.dart b/vehicle-tracker/packages/digit_components/lib/theme/theme.dart
new file mode 100644
index 00000000..e69bdb61
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/theme/theme.dart
@@ -0,0 +1,3 @@
+export './typography.dart';
+export './digit_theme.dart';
+export './colors.dart';
diff --git a/vehicle-tracker/packages/digit_components/lib/theme/typography.dart b/vehicle-tracker/packages/digit_components/lib/theme/typography.dart
new file mode 100644
index 00000000..d87a7f51
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/theme/typography.dart
@@ -0,0 +1,117 @@
+import 'package:flutter/material.dart';
+
+abstract class DigitTypography {
+ final Color? _textColorNormal;
+ final Color? _textColorLight;
+ final TextStyle _normalBase;
+ final TextStyle _displayBase;
+
+ const DigitTypography({
+ required TextStyle normalBase,
+ required TextStyle displayBase,
+ Color? normal,
+ Color? light,
+ }) : _normalBase = normalBase,
+ _displayBase = displayBase,
+ _textColorLight = light,
+ _textColorNormal = normal;
+
+ TextStyle get _normal =>
+ _normalBase.copyWith(color: _textColorNormal, fontFamily: 'Roboto');
+ TextStyle get _light =>
+ _normalBase.copyWith(color: _textColorLight, fontFamily: 'Roboto');
+ TextStyle get _big =>
+ _displayBase.copyWith(color: _textColorNormal, fontFamily: 'Roboto');
+
+ /// Follows Digit Typography standards
+ ///
+ ///
+ TextTheme get textTheme;
+
+ /// Heading styles
+ TextStyle get headingXl => textTheme.displayMedium!;
+ TextStyle get headingL => textTheme.headlineLarge!;
+ TextStyle get headingM => textTheme.headlineMedium!;
+ TextStyle get headingS => textTheme.headlineSmall!;
+
+ /// Caption styles
+ TextStyle get captionXL => textTheme.labelLarge!;
+ TextStyle get captionL => textTheme.labelMedium!;
+ TextStyle get captionM => textTheme.labelSmall!;
+
+ /// Body styles
+ TextStyle get bodyL => textTheme.bodyLarge!;
+ TextStyle get bodyM => textTheme.bodyMedium!;
+ TextStyle get bodyS => textTheme.bodySmall!;
+
+ /// Miscellaneous styles
+ TextStyle get label => textTheme.bodyLarge!;
+ TextStyle get link => textTheme.bodyLarge!;
+}
+
+class DigitMobileTypography extends DigitTypography {
+ const DigitMobileTypography({
+ required TextStyle normalBase,
+ required TextStyle displayBase,
+ Color? normal,
+ Color? light,
+ }) : super(
+ normal: normal,
+ light: light,
+ displayBase: displayBase,
+ normalBase: normalBase,
+ );
+
+ @override
+ TextTheme get textTheme {
+ return TextTheme(
+ displayMedium: _big.copyWith(
+ fontSize: 32,
+ fontWeight: FontWeight.w700,
+ fontFamily: 'Roboto Condensed',
+ ),
+ headlineLarge: _normal.copyWith(
+ fontSize: 24,
+ fontWeight: FontWeight.w700,
+ ),
+ headlineMedium: _normal.copyWith(
+ fontSize: 18,
+ fontWeight: FontWeight.w700,
+ ),
+ headlineSmall: _normal.copyWith(
+ fontSize: 16,
+ fontWeight: FontWeight.w700,
+ fontFamily: 'Roboto',
+ ),
+ bodyLarge: _normal.copyWith(
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ fontFamily: 'Roboto',
+ ),
+ bodyMedium: _normal.copyWith(
+ fontSize: 14,
+ fontWeight: FontWeight.w400,
+ fontFamily: 'Roboto',
+ ),
+ bodySmall: _normal.copyWith(
+ fontSize: 12,
+ fontWeight: FontWeight.w400,
+ fontFamily: 'Roboto',
+ ),
+ labelLarge: _normal.copyWith(
+ fontSize: 19,
+ fontWeight: FontWeight.w500,
+ ),
+ labelMedium: _light.copyWith(
+ fontSize: 18,
+ fontWeight: FontWeight.w400,
+ fontFamily: 'Roboto',
+ ),
+ labelSmall: _normal.copyWith(
+ fontSize: 16,
+ fontWeight: FontWeight.w400,
+ fontFamily: 'Roboto',
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/utils/app_logger.dart b/vehicle-tracker/packages/digit_components/lib/utils/app_logger.dart
new file mode 100644
index 00000000..80ab8e35
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/utils/app_logger.dart
@@ -0,0 +1,59 @@
+import 'package:flutter/foundation.dart';
+import 'package:logging/logging.dart';
+
+class AppLogger {
+ static AppLogger get instance => _instance;
+ static const _instance = AppLogger._();
+
+ const AppLogger._();
+
+ void debug(dynamic input, {String? title}) =>
+ _printMessage(input, title: title, level: Level.CONFIG);
+
+ void info(dynamic input, {String? title}) =>
+ _printMessage(input, title: title, level: Level.INFO);
+
+ void error({
+ required String title,
+ String? message,
+ StackTrace? stackTrace,
+ }) =>
+ _printError(
+ message: message,
+ title: title,
+ stackTrace: stackTrace,
+ );
+
+ void _printError({
+ required String title,
+ String? message,
+ StackTrace? stackTrace,
+ }) {
+ if (stackTrace != null) {
+ debugPrintStack(
+ label: title,
+ stackTrace: stackTrace,
+ );
+ } else {
+ _printMessage(
+ message,
+ title: title,
+ level: Level.SEVERE,
+ );
+ }
+ }
+
+ void _printMessage(
+ dynamic input, {
+ String? title,
+ required Level level,
+ }) =>
+ debugPrint(
+ [
+ '[${level.name.padRight(4, ' ').substring(0, 4)}] ',
+ '${(title ?? runtimeType.toString())}\n',
+ '${input.toString()}\n',
+ ].join(''),
+ wrapWidth: 120,
+ );
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/utils/constants.dart b/vehicle-tracker/packages/digit_components/lib/utils/constants.dart
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/utils/constants.dart
@@ -0,0 +1 @@
+
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/anchor_overlay.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/anchor_overlay.dart
new file mode 100644
index 00000000..5051ade1
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/anchor_overlay.dart
@@ -0,0 +1,115 @@
+import 'package:flutter/material.dart';
+import './triangularpointer.dart';
+import 'package:digit_components/digit_components.dart';
+import 'package:overlay_builder/overlay_builder.dart';
+
+class AnchoredOverlay extends StatelessWidget {
+ final bool showOverlay;
+ final Widget child;
+ final String description;
+ final GlobalKey paramKey;
+ final VoidCallback onTap;
+ final Offset position;
+ final double childHeight;
+
+ const AnchoredOverlay(
+ {super.key,
+ required this.showOverlay,
+ required this.child,
+ required this.description,
+ required this.paramKey,
+ required this.onTap,
+ required this.position,
+ required this.childHeight});
+
+ @override
+ Widget build(BuildContext context) {
+ return OverlayBuilder(
+ type: OverlayType.fullscreen,
+ opaque: false,
+ key: paramKey,
+ overlayChild: GestureDetector(
+ onTap: onTap,
+ child: Stack(children: [
+ Positioned(
+ top: position.dy + childHeight - 20,
+ left: 100,
+ child: const TriangularShape(),
+ ),
+ Positioned(
+ top: position.dy + childHeight,
+ child: Card(
+ elevation: 10,
+ margin: DigitTheme.instance.containerMargin,
+ child: Padding(
+ padding: const EdgeInsets.all(8),
+ child: Column(
+ children: [
+ const SizedBox(
+ height: 8,
+ ),
+ Text(
+ description,
+ style: Theme.of(context).textTheme.bodyMedium,
+ ),
+ const SizedBox(
+ height: 8,
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ SizedBox(
+ height: 30, //height of button
+ child: TextButton(
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ child: const Text(
+ 'Skip',
+ style: TextStyle(fontSize: 10),
+ ),
+ )),
+ SizedBox(
+ height: 30, //height of button
+ child: ElevatedButton(
+ style: ElevatedButton.styleFrom(
+ elevation: 3, //elevation of button
+ shape: RoundedRectangleBorder(
+ //to set border radius to button
+ borderRadius:
+ BorderRadius.circular(10)),
+ //content padding inside button
+ ),
+ onPressed: onTap,
+ child: const Text(
+ 'Next',
+ style: TextStyle(fontSize: 10),
+ ),
+ ))
+ ])
+ ],
+ ))))
+ ])),
+ child: showOverlay
+ ? Scaffold(
+ body: GestureDetector(
+ onTap: onTap,
+ child: Card(
+ margin: EdgeInsets.zero,
+ elevation: 10,
+ child: Material(
+ elevation: 6,
+ type: MaterialType.transparency,
+ child: child,
+ ),
+ ),
+ ))
+ : GestureDetector(
+ onTap: onTap,
+ child: SizedBox(
+ height: childHeight,
+ child: child,
+ )),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_action_dialog.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_action_dialog.dart
new file mode 100644
index 00000000..70aa738d
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_action_dialog.dart
@@ -0,0 +1,21 @@
+import 'package:digit_components/theme/digit_theme.dart';
+import 'package:flutter/material.dart';
+
+class DigitActionDialog extends StatelessWidget {
+ final Widget widget;
+
+ const DigitActionDialog({super.key, required this.widget});
+
+ static Future show(BuildContext context,
+ {required final Widget widget}) =>
+ showDialog(
+ context: context,
+ barrierColor: DigitTheme.instance.colors.black.withOpacity(0.7),
+ builder: (context) => DigitActionDialog(widget: widget));
+
+ @override
+ Widget build(BuildContext context) => AlertDialog(
+ insetPadding: EdgeInsets.zero,
+ content: widget,
+ );
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_checkbox.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_checkbox.dart
new file mode 100644
index 00000000..1c7e1202
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_checkbox.dart
@@ -0,0 +1,60 @@
+import 'package:flutter/material.dart';
+
+class DigitCheckbox extends StatelessWidget {
+ final bool value;
+ final String label;
+ final ValueChanged? onChanged;
+
+ const DigitCheckbox({
+ super.key,
+ required this.label,
+ this.onChanged,
+ this.value = false,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+
+ return Padding(
+ padding: const EdgeInsets.only(top: 4),
+ child: InkWell(
+ onTap: () => onChanged?.call(!value),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ SizedBox(
+ height: 24,
+ width: 24,
+ child: Checkbox(
+ value: value,
+ side: MaterialStateBorderSide.resolveWith((states) {
+ if (states.contains(MaterialState.selected)) {
+ return BorderSide(
+ width: 2.0,
+ color: theme.colorScheme.secondary,
+ );
+ }
+ return const BorderSide(width: 1.0);
+ }),
+ fillColor: MaterialStateProperty.resolveWith((states) {
+ if (states.contains(MaterialState.selected)) {
+ return theme.colorScheme.surface;
+ }
+ return theme.colorScheme.secondary;
+ }),
+ checkColor: theme.colorScheme.secondary,
+ onChanged: onChanged,
+ ),
+ ),
+ const SizedBox(width: 16),
+ Text(
+ label,
+ style: Theme.of(context).textTheme.bodyLarge,
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_date_form_picker.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_date_form_picker.dart
new file mode 100644
index 00000000..cad2b8a1
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_date_form_picker.dart
@@ -0,0 +1,85 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+import 'package:intl/intl.dart';
+import 'package:reactive_forms/reactive_forms.dart';
+
+class DigitDateFormPicker extends StatelessWidget {
+ final String label;
+ final bool isRequired;
+ final DateTime? firstDate;
+ final DateTime? lastDate;
+ final DateTime? initialDate;
+
+ final ValueChanged? onChangeOfDate;
+ final String formControlName;
+ final bool? isEnabled;
+ final String? requiredMessage;
+ final String? Function(DateTime?)? validator;
+ final AutovalidateMode? autoValidation;
+ final EdgeInsets? margin;
+ final DateTime? start;
+ final DateTime? end;
+ final String? hint;
+ final IconData? icon;
+ final String? tooltipMessage;
+
+ const DigitDateFormPicker({
+ super.key,
+ required this.label,
+ this.isRequired = false,
+ this.firstDate,
+ this.lastDate,
+ this.initialDate,
+ this.onChangeOfDate,
+ required this.formControlName,
+ this.isEnabled = true,
+ this.requiredMessage,
+ this.validator,
+ this.autoValidation,
+ this.margin,
+ this.end,
+ this.start,
+ this.hint,
+ this.icon,
+ this.tooltipMessage,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return LabeledField(
+ label: '$label ${isRequired ? '*' : ''}',
+ icon: icon,
+ tooltipMessage: tooltipMessage,
+ child: ReactiveDatePicker(
+ formControlName: formControlName,
+ firstDate: start ?? DateTime(1900),
+ lastDate: end ?? DateTime.now(),
+ builder: (context, picker, child) {
+ return ReactiveTextField(
+ style: TextStyle(
+ color: isEnabled == true
+ ? DigitTheme.instance.colorScheme.onBackground
+ : DigitTheme.instance.colorScheme.shadow,
+ ),
+ formControlName: formControlName,
+ readOnly: true,
+ valueAccessor: DateTimeValueAccessor(
+ dateTimeFormat: DateFormat('dd MMM yyyy'),
+ ),
+ decoration: InputDecoration(
+ contentPadding: const EdgeInsets.fromLTRB(16, 12, 0, 12),
+ suffixIcon: const Icon(Icons.date_range),
+ label: hint == null ? null : Text(hint!),
+ ),
+ enableInteractiveSelection: isEnabled!,
+ onTap: isEnabled == true
+ ? (control) {
+ picker.showPicker();
+ }
+ : null,
+ );
+ },
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_divider.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_divider.dart
new file mode 100644
index 00000000..4d9d2f5f
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_divider.dart
@@ -0,0 +1,14 @@
+import 'package:flutter/material.dart';
+
+class DigitDivider extends StatelessWidget {
+ const DigitDivider({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Divider(
+ key: const ValueKey('neo_divider'),
+ color: Theme.of(context).colorScheme.outline,
+ height: 1.0,
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_dropdown.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_dropdown.dart
new file mode 100644
index 00000000..01aeaab6
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_dropdown.dart
@@ -0,0 +1,52 @@
+import 'package:flutter/material.dart';
+
+class DigitDropdown extends StatelessWidget {
+ final String label;
+ final T? value;
+ final List menuItems;
+ final bool isRequired;
+ final ValueChanged? onChanged;
+ final String Function(T value) valueMapper;
+
+ const DigitDropdown({
+ super.key,
+ required this.value,
+ required this.label,
+ required this.menuItems,
+ this.isRequired = false,
+ required this.valueMapper,
+ this.onChanged,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.only(top: 16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ '$label${isRequired ? ' *' : ''}',
+ style: Theme.of(context).textTheme.bodyLarge,
+ ),
+ const SizedBox(height: 8),
+ DropdownButtonFormField(
+ value: value,
+ onChanged: onChanged,
+ decoration: const InputDecoration(
+ contentPadding: EdgeInsets.fromLTRB(16, 12, 0, 12),
+ ),
+ items: menuItems
+ .map(
+ (e) => DropdownMenuItem(
+ value: e,
+ child: Text(valueMapper(e)),
+ ),
+ )
+ .toList(),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_icon_button.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_icon_button.dart
new file mode 100644
index 00000000..0bf643f6
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_icon_button.dart
@@ -0,0 +1,55 @@
+import 'package:flutter/material.dart';
+
+class DigitIconButton extends StatelessWidget {
+ final VoidCallback? onPressed;
+ final String? iconText;
+ final IconData? icon;
+ final Color? iconColor;
+ final Color? iconTextColor;
+ final TextDecoration? textDecoration;
+
+ const DigitIconButton({
+ super.key,
+ this.onPressed,
+ this.iconText,
+ this.icon,
+ this.iconColor,
+ this.iconTextColor,
+ this.textDecoration,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ return TextButton(
+ style: TextButton.styleFrom(
+ foregroundColor: theme.colorScheme.onBackground,
+ padding: EdgeInsets.zero,
+ ),
+ onPressed: onPressed,
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Flexible(
+ child: Icon(
+ icon,
+ color: iconColor ?? theme.colorScheme.secondary,
+ ),
+ ),
+ const SizedBox(
+ width: 4,
+ ),
+ if (iconText != null)
+ Flexible(
+ child: Text(
+ iconText!,
+ style: TextStyle(
+ color: iconTextColor ?? theme.colorScheme.secondary,
+ decoration: textDecoration ?? TextDecoration.none,
+ fontSize: 16),
+ )),
+ ],
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_integer_form_picker.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_integer_form_picker.dart
new file mode 100644
index 00000000..ae6fad2d
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_integer_form_picker.dart
@@ -0,0 +1,94 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+import 'package:reactive_forms/reactive_forms.dart';
+
+class DigitIntegerFormPicker extends StatelessWidget {
+ final int? minimum;
+ final int? maximum;
+ final bool incrementer;
+ final String? hint;
+ final String label;
+ final FormGroup form;
+ final String formControlName;
+ const DigitIntegerFormPicker({
+ super.key,
+ this.minimum,
+ this.maximum,
+ required this.incrementer,
+ required this.formControlName,
+ this.hint,
+ required this.form,
+ required this.label,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ if (!incrementer) {
+ return LabeledField(
+ label: label,
+ child: ReactiveTextField(
+ formControlName: formControlName,
+ decoration: InputDecoration(labelText: hint),
+ keyboardType: TextInputType.number,
+ ));
+ }
+
+ return LabeledField(
+ label: label,
+ child: IntrinsicHeight(
+ child: Row(
+ children: [
+ _buildButton(
+ context,
+ border: Border(
+ left: _borderSide,
+ bottom: _borderSide,
+ top: _borderSide,
+ ),
+ icon: Icons.remove,
+ onPressed: () => minimum != null
+ ? form.control(formControlName).value > minimum ||
+ form.control(formControlName).value == null
+ ? form.control(formControlName).value -= 1
+ : 1
+ : form.control(formControlName).value -= 1,
+ ),
+ Expanded(
+ child: ReactiveTextField(
+ readOnly: true,
+ textAlign: TextAlign.center,
+ formControlName: formControlName,
+ decoration: InputDecoration(labelText: hint),
+ keyboardType: TextInputType.number,
+ ),
+ ),
+ _buildButton(context,
+ border: Border(
+ right: _borderSide,
+ bottom: _borderSide,
+ top: _borderSide,
+ ),
+ icon: Icons.add,
+ onPressed: () => form.control(formControlName).value += 1),
+ ],
+ ),
+ ));
+ }
+
+ Widget _buildButton(
+ BuildContext context, {
+ required Border border,
+ required IconData icon,
+ VoidCallback? onPressed,
+ }) =>
+ AspectRatio(
+ aspectRatio: 1,
+ child: Material(
+ shape: border,
+ color: Theme.of(context).colorScheme.background,
+ child: InkWell(onTap: onPressed, child: Icon(icon)),
+ ),
+ );
+
+ BorderSide get _borderSide => const BorderSide(width: 1);
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_outline_icon_button.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_outline_icon_button.dart
new file mode 100644
index 00000000..294bc2fb
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_outline_icon_button.dart
@@ -0,0 +1,32 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+
+class DigitOutlineIconButton extends StatelessWidget {
+ final String label;
+ final IconData icon;
+ final VoidCallback? onPressed;
+ final Color? iconColor;
+
+ const DigitOutlineIconButton({
+ super.key,
+ this.iconColor,
+ required this.label,
+ required this.icon,
+ required this.onPressed,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return OutlinedButton(
+ onPressed: onPressed,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Flexible(child: Icon(icon)),
+ const SizedBox(width: kPadding),
+ Text(label),
+ ],
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_reactive_dropdown.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_reactive_dropdown.dart
new file mode 100644
index 00000000..b309452f
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_reactive_dropdown.dart
@@ -0,0 +1,72 @@
+import 'package:flutter/material.dart';
+import 'package:reactive_forms/reactive_forms.dart';
+
+class DigitReactiveDropdown extends StatelessWidget {
+ final String label;
+ final T? initialValue;
+ final List menuItems;
+ final String formControlName;
+ final bool isRequired;
+ final ValueChanged? onChanged;
+ final String Function(T value) valueMapper;
+ final Map? validationMessages;
+
+ const DigitReactiveDropdown({
+ super.key,
+ required this.label,
+ required this.menuItems,
+ required this.formControlName,
+ this.isRequired = false,
+ required this.valueMapper,
+ this.initialValue,
+ this.onChanged,
+ this.validationMessages,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.only(top: 16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ '$label${isRequired ? ' *' : ''}',
+ style: Theme.of(context).textTheme.bodyLarge,
+ ),
+ const SizedBox(height: 8),
+ ReactiveDropdownField(
+ onChanged: (control) {
+ final value = control.value;
+ if (value == null) return;
+ onChanged?.call(value);
+ },
+ validationMessages: validationMessages,
+ formControlName: formControlName,
+ decoration: const InputDecoration(
+ contentPadding: EdgeInsets.fromLTRB(16, 12, 0, 12),
+ ),
+ items: menuItems
+ .map(
+ (e) => DropdownMenuItem(
+ value: e,
+ child: Text(valueMapper(e)),
+ ),
+ )
+ .toList(),
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+class MenuItemModel {
+ final String name;
+ final String code;
+
+ const MenuItemModel({
+ required this.name,
+ required this.code,
+ });
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_table_item.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_table_item.dart
new file mode 100644
index 00000000..d078efb3
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_table_item.dart
@@ -0,0 +1,37 @@
+import 'package:flutter/material.dart';
+
+class DigitTableItem extends StatelessWidget {
+ final double? width;
+ final double? height;
+ final String content;
+ final int index;
+ final Widget? child;
+ final bool? isColored;
+
+ const DigitTableItem({
+ super.key,
+ this.width,
+ this.height,
+ required this.content,
+ required this.index,
+ this.child,
+ this.isColored,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ return Container(
+ color: isColored != null
+ ? index % 2 == 0
+ ? theme.scaffoldBackgroundColor
+ : theme.cardColor
+ : null,
+ width: width ?? 100,
+ height: height ?? 50,
+ padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
+ alignment: Alignment.centerLeft,
+ child: child ?? Text(content),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_table_item_title.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_table_item_title.dart
new file mode 100644
index 00000000..bffb7259
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_table_item_title.dart
@@ -0,0 +1,31 @@
+import 'package:flutter/material.dart';
+
+class DigitTableItemTitle extends StatelessWidget {
+ final double? width;
+ final String label;
+ final double? height;
+ final EdgeInsets? padding;
+
+ const DigitTableItemTitle({
+ super.key,
+ this.width,
+ required this.label,
+ this.height,
+ this.padding,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ return Container(
+ width: width,
+ height: height,
+ padding: padding ?? const EdgeInsets.fromLTRB(5, 0, 0, 0),
+ alignment: Alignment.centerLeft,
+ child: Text(
+ label,
+ style: theme.textTheme.bodyMedium,
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_text_form_field.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_text_form_field.dart
new file mode 100644
index 00000000..73114e26
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_text_form_field.dart
@@ -0,0 +1,96 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+import 'package:reactive_forms/reactive_forms.dart';
+
+class DigitTextFormField extends StatelessWidget {
+ final bool readOnly;
+ final String formControlName;
+ final String? hint;
+ final Widget? suffix;
+ final bool isRequired;
+ final int minLines;
+ final int maxLines;
+ final int? maxLength;
+ final TextInputType keyboardType;
+ final FocusNode? focusNode;
+ final VoidCallback? onTap;
+ final bool obscureText;
+ final String label;
+ final int? minLength;
+ final Widget? suffixIcon;
+ final void Function(FormControl)? onChanged;
+ final TextCapitalization textCapitalization;
+ final ControlValueAccessor? valueAccessor;
+ final Map? validationMessages;
+
+ const DigitTextFormField({
+ super.key,
+ required this.label,
+ required this.formControlName,
+ this.hint,
+ this.suffix,
+ this.minLines = 1,
+ this.maxLines = 1,
+ this.valueAccessor,
+ this.maxLength,
+ this.onTap,
+ this.focusNode,
+ this.validationMessages,
+ this.suffixIcon,
+ this.keyboardType = TextInputType.text,
+ this.textCapitalization = TextCapitalization.sentences,
+ this.obscureText = false,
+ this.isRequired = false,
+ this.readOnly = false,
+ this.onChanged,
+ this.minLength,
+ });
+
+ @override
+ Widget build(BuildContext context) => LabeledField(
+ label: '$label ${isRequired ? '*' : ''}',
+ child: ReactiveTextField(
+ onChanged: onChanged,
+ readOnly: readOnly,
+ formControlName: formControlName,
+ maxLength: maxLength,
+ validationMessages: validationMessages,
+ autofocus: false,
+ textCapitalization: textCapitalization,
+ minLines: minLines,
+ maxLines: maxLines,
+ obscureText: obscureText,
+ focusNode: focusNode,
+ keyboardType: keyboardType,
+ valueAccessor: valueAccessor,
+ decoration: readOnly == true
+ ? InputDecoration(
+ enabledBorder:
+ DigitTheme.instance.inputDecorationTheme.disabledBorder,
+ fillColor: DigitTheme.instance.colors.cloudGray,
+ focusedBorder:
+ DigitTheme.instance.inputDecorationTheme.disabledBorder,
+ focusColor: DigitTheme.instance.colors.cloudGray,
+ suffixIcon: suffix == null
+ ? null
+ : InkWell(
+ onTap: onTap,
+ child: suffix,
+ ),
+ )
+ : InputDecoration(
+ labelText: hint,
+ contentPadding: const EdgeInsets.fromLTRB(16, 12, 0, 12),
+ suffixIconConstraints: const BoxConstraints(
+ maxHeight: 40,
+ maxWidth: 40,
+ ),
+ suffixIcon: suffix == null
+ ? null
+ : InkWell(
+ onTap: onTap,
+ child: suffix,
+ ),
+ )),
+ );
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_toast_helper.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_toast_helper.dart
new file mode 100644
index 00000000..9d971271
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_toast_helper.dart
@@ -0,0 +1,36 @@
+import 'package:digit_components/widgets/atoms/digit_toaster.dart';
+import 'package:flutter/material.dart';
+
+class ToastHelper extends StatefulWidget {
+ final String message;
+
+ const ToastHelper({
+ super.key,
+ required this.message,
+ });
+
+ @override
+ State createState() {
+ return ToastHelperState();
+ }
+}
+
+class ToastHelperState extends State {
+ @override
+ void initState() {
+ super.initState();
+
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ final theme = Theme.of(context);
+ DigitToast.show(
+ context,
+ options: DigitToastOptions(widget.message, true, theme),
+ );
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_toaster.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_toaster.dart
new file mode 100644
index 00000000..2c6d8b69
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/digit_toaster.dart
@@ -0,0 +1,57 @@
+import 'package:digit_components/theme/digit_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:fluttertoast/fluttertoast.dart';
+
+class DigitToast {
+ final DigitToastOptions options;
+
+ static FToast ftoast = FToast();
+
+ @visibleForTesting
+ DigitToast({
+ required this.options,
+ });
+
+ static show(
+ BuildContext context, {
+ required DigitToastOptions options,
+ }) {
+ ftoast.init(context);
+
+ return ftoast.showToast(
+ child: Container(
+ color: options.isError
+ ? options.theme.colorScheme.error
+ : options.theme.colorScheme.surfaceVariant,
+ padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Icon(
+ options.isError ? Icons.info_outline_rounded : Icons.check,
+ color: Colors.white,
+ ),
+ const SizedBox(
+ width: 12.0,
+ ),
+ Expanded(
+ child: Text(
+ options.message,
+ style: const TextStyle(color: Colors.white),
+ )),
+ ],
+ ),
+ ),
+ gravity: ToastGravity.BOTTOM,
+ toastDuration: DigitTheme.instance.toastDuration,
+ );
+ }
+}
+
+class DigitToastOptions {
+ final String message;
+ final bool isError;
+ ThemeData theme;
+
+ DigitToastOptions(this.message, this.isError, this.theme);
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/atoms/triangularpointer.dart b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/triangularpointer.dart
new file mode 100644
index 00000000..b9b1b4db
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/atoms/triangularpointer.dart
@@ -0,0 +1,63 @@
+import 'package:flutter/material.dart';
+
+class TriangularShape extends StatelessWidget {
+ const TriangularShape({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Material(
+ elevation: 4,
+ shape: TriangularBorder(),
+ child: ClipPath(
+ clipper: TriangularClipper(),
+ child: Container(
+ margin: EdgeInsets.zero,
+ padding: EdgeInsets.zero,
+ height: 40,
+ width: 30,
+ color: Colors.white,
+ ),
+ ),
+ );
+ }
+}
+
+class TriangularClipper extends CustomClipper {
+ @override
+ Path getClip(Size size) {
+ var path = Path();
+ path.moveTo(size.width / 2, 0);
+ path.lineTo(0, size.height);
+ path.lineTo(size.width, size.height);
+ path.close();
+
+ return path;
+ }
+
+ @override
+ bool shouldReclip(CustomClipper oldClipper) => false;
+}
+
+class TriangularBorder extends ShapeBorder {
+ @override
+ EdgeInsetsGeometry get dimensions => const EdgeInsets.only();
+
+ @override
+ Path getInnerPath(Rect rect, {TextDirection? textDirection}) => Path();
+
+ @override
+ Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
+ var path = Path();
+ path.moveTo(rect.width / 2, 0);
+ path.lineTo(0, rect.height);
+ path.lineTo(rect.width, rect.height);
+ path.close();
+
+ return path;
+ }
+
+ @override
+ void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {}
+ @override
+ ShapeBorder scale(double t) => TriangularBorder();
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_banner.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_banner.dart
new file mode 100644
index 00000000..c5db11fc
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_banner.dart
@@ -0,0 +1,34 @@
+import 'package:flutter/material.dart';
+
+class DigitBanner extends StatelessWidget {
+ final Widget child;
+ final Color color;
+ final String imageUrl;
+ final double height;
+
+ const DigitBanner({
+ super.key,
+ required this.child,
+ required this.color,
+ required this.imageUrl,
+ required this.height,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ height: height,
+ decoration: BoxDecoration(
+ color: color,
+ image: DecorationImage(
+ image: NetworkImage(
+ imageUrl,
+ ),
+ fit: BoxFit.cover,
+ opacity: .2,
+ ),
+ ),
+ child: child,
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_card.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_card.dart
new file mode 100644
index 00000000..5400ece2
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_card.dart
@@ -0,0 +1,33 @@
+import 'package:flutter/material.dart';
+
+class DigitCard extends StatelessWidget {
+ final Widget child;
+ final EdgeInsets? padding;
+ final EdgeInsets? margin;
+ final VoidCallback? onPressed;
+
+ const DigitCard({
+ required this.child,
+ super.key,
+ this.padding,
+ this.margin,
+ this.onPressed,
+ });
+
+ @override
+ Widget build(BuildContext context) => Card(
+ margin: margin,
+ child: InkWell(
+ borderRadius: BorderRadius.circular(4),
+ onTap: onPressed,
+ child: Padding(
+ padding: padding ??
+ const EdgeInsets.symmetric(
+ vertical: 16,
+ horizontal: 8,
+ ),
+ child: child,
+ ),
+ ),
+ );
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_checkbox_tile.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_checkbox_tile.dart
new file mode 100644
index 00000000..6d965343
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_checkbox_tile.dart
@@ -0,0 +1,56 @@
+import 'package:flutter/material.dart';
+
+class DigitCheckboxTile extends StatelessWidget {
+ final bool value;
+ final String label;
+ final ValueChanged? onChanged;
+ final EdgeInsets? margin;
+ final EdgeInsets? padding;
+
+ const DigitCheckboxTile({
+ this.value = false,
+ required this.label,
+ this.onChanged,
+ this.padding,
+ this.margin,
+ super.key,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+
+ return Padding(
+ padding: margin ?? const EdgeInsets.all(8.0),
+ child: Material(
+ child: InkWell(
+ onTap: () => onChanged?.call(!value),
+ child: Padding(
+ padding: padding ??
+ const EdgeInsets.symmetric(
+ horizontal: 8,
+ vertical: 8,
+ ),
+ child: Row(
+ children: [
+ Icon(
+ value
+ ? Icons.check_box_outline_blank_sharp
+ : Icons.check_box_outlined,
+ color: theme.colorScheme.secondary,
+ ),
+ const SizedBox(width: 16),
+ Expanded(
+ child: Text(
+ label,
+ style: Theme.of(context).textTheme.bodyLarge,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_dialog.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_dialog.dart
new file mode 100644
index 00000000..eb47b676
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_dialog.dart
@@ -0,0 +1,121 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+
+class DigitDialog extends StatelessWidget {
+ final DigitDialogOptions options;
+
+ const DigitDialog({
+ super.key,
+ required this.options,
+ });
+
+ static Future show(
+ BuildContext context, {
+ required DigitDialogOptions options,
+ }) {
+ return showDialog(
+ context: context,
+ barrierDismissible: options.barrierDismissible,
+ barrierColor: options.barrierColor ??
+ DigitTheme.instance.colors.black.withOpacity(0.7),
+ builder: (context) => DigitDialog(
+ key: options.key,
+ options: options,
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) => AlertDialog(
+ title: options.title,
+ content: options.content,
+ actionsAlignment: MainAxisAlignment.spaceBetween,
+ actions: [
+ if (options.primaryAction != null)
+ DigitElevatedButton(
+ onPressed: () => options.primaryAction!.action?.call(context),
+ child: Center(child: Text(options.primaryAction!.label)),
+ ),
+ if (options.secondaryAction != null)
+ TextButton(
+ onPressed: () => options.secondaryAction!.action?.call(context),
+ child: Center(child: Text(options.secondaryAction!.label)),
+ ),
+ ],
+ titlePadding: options.titlePadding,
+ contentPadding: options.contentPadding,
+ );
+}
+
+class DigitDialogOptions {
+ final Icon? titleIcon;
+ final EdgeInsets titlePadding;
+ final EdgeInsets contentPadding;
+ final String? titleText;
+ final String? contentText;
+ final Widget? _titleWidget;
+ final Widget? _contentWidget;
+ final DigitDialogActions? primaryAction;
+ final DigitDialogActions? secondaryAction;
+ final bool barrierDismissible;
+ final Color? barrierColor;
+ final Key? key;
+
+ const DigitDialogOptions({
+ this.titleText,
+ this.contentText,
+ this.titleIcon,
+ Widget? title,
+ Widget? content,
+ this.primaryAction,
+ this.secondaryAction,
+ this.barrierDismissible = false,
+ this.titlePadding = const EdgeInsets.all(kPadding),
+ this.contentPadding = const EdgeInsets.all(kPadding),
+ this.barrierColor,
+ this.key,
+ }) : _titleWidget = title,
+ _contentWidget = content;
+
+ Widget? get title {
+ if (_titleWidget != null) return _titleWidget;
+ if (titleText != null) {
+ return Row(
+ children: [
+ if (titleIcon != null) ...[
+ titleIcon!,
+ const SizedBox(width: 8),
+ ],
+ Expanded(
+ child: Text(
+ titleText!,
+ textAlign: TextAlign.left,
+ ),
+ ),
+ ],
+ );
+ }
+ return null;
+ }
+
+ Widget? get content {
+ if (_contentWidget != null) return _contentWidget;
+ if (contentText != null) {
+ return Text(
+ contentText!,
+ textAlign: TextAlign.left,
+ );
+ }
+ return null;
+ }
+}
+
+class DigitDialogActions {
+ final String label;
+ final T Function(BuildContext context)? action;
+
+ const DigitDialogActions({
+ required this.label,
+ this.action,
+ });
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_dob_picker.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_dob_picker.dart
new file mode 100644
index 00000000..a30c5dc8
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_dob_picker.dart
@@ -0,0 +1,102 @@
+import 'package:digit_components/widgets/atoms/digit_date_form_picker.dart';
+import 'package:digit_components/widgets/atoms/digit_text_form_field.dart';
+import 'package:flutter/material.dart';
+import 'package:reactive_forms/reactive_forms.dart';
+
+class DigitDobPicker extends StatelessWidget {
+ final String datePickerFormControl;
+
+ final bool isVerified;
+
+ final ControlValueAccessor? valueAccessor;
+ final String datePickerLabel;
+ final String ageFieldLabel;
+ final String separatorLabel;
+
+ const DigitDobPicker({
+ super.key,
+ required this.datePickerFormControl,
+ this.isVerified = false,
+ this.valueAccessor,
+ required this.datePickerLabel,
+ required this.ageFieldLabel,
+ required this.separatorLabel,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+
+ return Padding(
+ padding: const EdgeInsets.only(top: 16),
+ child: Container(
+ padding: const EdgeInsets.only(left: 8, right: 8, bottom: 16),
+ decoration: BoxDecoration(
+ color: theme.colorScheme.surface,
+ borderRadius: BorderRadius.circular(4),
+ border: Border.all(
+ color: Colors.grey, style: BorderStyle.solid, width: 1.0),
+ ),
+ child: Column(
+ children: [
+ DigitDateFormPicker(
+ label: datePickerLabel,
+ formControlName: datePickerFormControl,
+ ),
+ const SizedBox(height: 16),
+ Text(
+ separatorLabel,
+ style: theme.textTheme.bodyLarge,
+ ),
+ DigitTextFormField(
+ maxLength: 3,
+ valueAccessor: DobValueAccessor(),
+ formControlName: datePickerFormControl,
+ label: ageFieldLabel,
+ keyboardType: TextInputType.number,
+ readOnly: isVerified,
+ onChanged: (formControl) {
+ /// Validates that control's value must be `true`
+ Map? requiredTrue(
+ AbstractControl control) {
+ String value =
+ (DateTime.now().difference(formControl.value).inDays /
+ 365)
+ .round()
+ .toStringAsFixed(0);
+ return int.parse(value) <= 150
+ ? null
+ : {'Age Shoud be less than 150': true};
+ }
+
+ formControl.setValidators([requiredTrue]);
+ },
+ )
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+class DobValueAccessor extends ControlValueAccessor {
+ @override
+ String? modelToViewValue(DateTime? modelValue) {
+ if (modelValue == null) return null;
+ return (DateTime.now().difference(modelValue).inDays / 365)
+ .round()
+ .toStringAsFixed(0);
+ }
+
+ @override
+ DateTime? viewToModelValue(String? viewValue) {
+ if (viewValue == null) return null;
+ final value = int.tryParse(viewValue);
+ if (value == null) return null;
+ return DateTime(
+ DateTime.now().subtract(Duration(days: value * 365)).year,
+ 1,
+ 1,
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_elevated_button.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_elevated_button.dart
new file mode 100644
index 00000000..9e4381e1
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_elevated_button.dart
@@ -0,0 +1,34 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+
+class DigitElevatedButton extends StatelessWidget {
+ final Widget child;
+ final VoidCallback? onPressed;
+
+ const DigitElevatedButton({
+ super.key,
+ required this.child,
+ required this.onPressed,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ decoration: BoxDecoration(
+ border: Border(
+ bottom: BorderSide(
+ color: DigitTheme.instance.colors.woodsmokeBlack,
+ width: 2,
+ ),
+ ),
+ ),
+ child: ElevatedButton(
+ style: ElevatedButton.styleFrom(
+ tapTargetSize: MaterialTapTargetSize.shrinkWrap,
+ ),
+ onPressed: onPressed,
+ child: Center(child: child),
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_icon_tile.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_icon_tile.dart
new file mode 100644
index 00000000..967457a5
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_icon_tile.dart
@@ -0,0 +1,38 @@
+import 'package:flutter/material.dart';
+
+class DigitIconTile extends StatelessWidget {
+ final String title;
+ final Widget? content;
+ final IconData? icon;
+ final VoidCallback onPressed;
+
+ const DigitIconTile({
+ super.key,
+ required this.title,
+ this.content,
+ this.icon,
+ required this.onPressed,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return ListTile(
+ minVerticalPadding: 16,
+ leading: Icon(
+ icon,
+ color: Theme.of(context).colorScheme.shadow,
+ ),
+ title: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ title,
+ textAlign: TextAlign.start,
+ ),
+ content ?? const Offstage()
+ ],
+ ),
+ onTap: onPressed,
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_info_card.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_info_card.dart
new file mode 100644
index 00000000..4ee189d8
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_info_card.dart
@@ -0,0 +1,70 @@
+import 'package:digit_components/theme/digit_theme.dart';
+import 'package:flutter/material.dart';
+
+class DigitInfoCard extends StatelessWidget {
+ final String title;
+ final String description;
+ final Color? backgroundColor;
+ final IconData? icon;
+ final Color? iconColor;
+ final EdgeInsets? padding;
+ final EdgeInsets? margin;
+
+ const DigitInfoCard({
+ super.key,
+ required this.title,
+ required this.description,
+ this.backgroundColor,
+ this.iconColor,
+ this.icon,
+ this.padding,
+ this.margin,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+
+ return Container(
+ margin: margin ?? DigitTheme.instance.containerMargin,
+ padding: padding ?? const EdgeInsets.all(8),
+ decoration: ShapeDecoration(
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.all(Radius.circular(4)),
+ ),
+ color: backgroundColor ?? theme.colorScheme.tertiaryContainer,
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ Padding(
+ padding: padding ?? const EdgeInsets.all(8),
+ child: Icon(
+ icon ?? Icons.info,
+ color: iconColor ?? theme.colorScheme.surfaceTint,
+ ),
+ ),
+ Expanded(
+ child: Text(
+ title,
+ style: theme.textTheme.headlineMedium,
+ ),
+ )
+ ],
+ ),
+ Padding(
+ padding: const EdgeInsets.all(kPadding),
+ child: Text(
+ description,
+ style: theme.textTheme.bodyLarge,
+ textAlign: TextAlign.start,
+ ),
+ )
+ ],
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_outline_button.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_outline_button.dart
new file mode 100644
index 00000000..6aaaa557
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_outline_button.dart
@@ -0,0 +1,51 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+
+class DigitOutLineButton extends StatelessWidget {
+ final String label;
+ final VoidCallback? onPressed;
+
+ const DigitOutLineButton({
+ super.key,
+ required this.label,
+ this.onPressed,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ return OutlinedButton(
+ onPressed: onPressed,
+ style: OutlinedButton.styleFrom(
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.zero,
+ ),
+ backgroundColor: Colors.white,
+ side: BorderSide(
+ width: 1.0,
+ color: onPressed != null
+ ? theme.colorScheme.secondary
+ : theme.colorScheme.outline,
+ ),
+ minimumSize: kIsWeb
+ ? Size(
+ MediaQuery.of(context).size.width / 11.25,
+ 40,
+ )
+ : Size(
+ MediaQuery.of(context).size.width / 1.25,
+ 50,
+ ),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.only(left: 15, right: 15),
+ child: Text(
+ label,
+ style: TextStyle(
+ color: theme.colorScheme.secondary,
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_project_cell.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_project_cell.dart
new file mode 100644
index 00000000..30719286
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_project_cell.dart
@@ -0,0 +1,72 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+
+class DigitProjectCell extends StatelessWidget {
+ final VoidCallback onTap;
+ final String projectText;
+
+ const DigitProjectCell({
+ super.key,
+ required this.projectText,
+ required this.onTap,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return InkWell(
+ onTap: onTap,
+ child: DigitCard(
+ margin: const EdgeInsets.only(
+ left: 16,
+ right: 16,
+ ),
+ child: Padding(
+ padding:
+ const EdgeInsets.only(left: 16, right: 16, top: 12, bottom: 12),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ Icon(
+ Icons.article,
+ size: 30,
+ color: Theme.of(context).colorScheme.secondary,
+ ),
+ const SizedBox(
+ width: 14,
+ ),
+ SizedBox(
+ width: MediaQuery.of(context).size.width / 1.8,
+ child: Row(
+ children: [
+ Flexible(
+ child: Text(
+ projectText,
+ style: Theme.of(context).textTheme.headlineLarge,
+ ),
+ )
+ ],
+ ),
+ ),
+ ],
+ ),
+ Container(
+ height: 32,
+ width: 32,
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.secondary,
+ shape: BoxShape.circle),
+ child: Icon(
+ Icons.arrow_forward,
+ color: Theme.of(context).colorScheme.onPrimary,
+ ),
+ )
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_reactive_type_ahead.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_reactive_type_ahead.dart
new file mode 100644
index 00000000..d91b9cb8
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_reactive_type_ahead.dart
@@ -0,0 +1,211 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_typeahead/flutter_typeahead.dart';
+import 'package:reactive_forms/reactive_forms.dart';
+
+/// A [ReactiveForm] ancestor is required.
+///
+class DigitReactiveTypeAhead extends ReactiveFormField {
+ DigitReactiveTypeAhead({
+ Key? key,
+ String? formControlName,
+ FormControl? formControl,
+ Map? validationMessages,
+ ControlValueAccessor? valueAccessor,
+ ShowErrorsFunction? showErrors,
+ SuggestionSelectionCallback? onSuggestionSelected,
+ required this.stringify,
+ V Function(String)? viewDataTypeFromTextEditingValue,
+ required SuggestionsCallback suggestionsCallback,
+ required ItemBuilder itemBuilder,
+ SuggestionsBoxDecoration suggestionsBoxDecoration =
+ const SuggestionsBoxDecoration(),
+ Duration debounceDuration = const Duration(milliseconds: 300),
+ WidgetBuilder? loadingBuilder,
+ WidgetBuilder? noItemsFoundBuilder,
+ ErrorBuilder? errorBuilder,
+ AnimationTransitionBuilder? transitionBuilder,
+ double animationStart = 0.25,
+ Duration animationDuration = const Duration(milliseconds: 500),
+ bool getImmediateSuggestions = false,
+ double suggestionsBoxVerticalOffset = 5.0,
+ AxisDirection direction = AxisDirection.down,
+ bool hideOnLoading = false,
+ bool hideOnEmpty = false,
+ bool hideOnError = false,
+ bool hideSuggestionsOnKeyboardHide = true,
+ bool keepSuggestionsOnLoading = true,
+ bool keepSuggestionsOnSuggestionSelected = false,
+ bool autoFlipDirection = false,
+ bool hideKeyboard = false,
+ TextFieldConfiguration textFieldConfiguration =
+ const TextFieldConfiguration(),
+ SuggestionsBoxController? suggestionsBoxController,
+ InputDecoration decoration = const InputDecoration(),
+ TextInputType? keyboardType,
+ TextCapitalization textCapitalization = TextCapitalization.none,
+ TextInputAction? textInputAction,
+ TextStyle? style,
+ StrutStyle? strutStyle,
+ TextDirection? textDirection,
+ TextAlign textAlign = TextAlign.start,
+ TextAlignVertical? textAlignVertical,
+ bool autofocus = false,
+ bool readOnly = false,
+ bool? showCursor,
+ bool obscureText = false,
+ String obscuringCharacter = '•',
+ bool autocorrect = true,
+ V? initialValue,
+ String? initialValueText,
+ }) : super(
+ key: key,
+ formControl: formControl,
+ formControlName: formControlName,
+ valueAccessor: valueAccessor,
+ validationMessages: validationMessages,
+ showErrors: showErrors,
+ builder: (field) {
+ final state = field as _ReactiveTypeaheadState;
+ final effectiveDecoration = textFieldConfiguration.decoration
+ .applyDefaults(Theme.of(state.context).inputDecorationTheme);
+
+ state._setFocusNode(textFieldConfiguration.focusNode);
+ final controller =
+ textFieldConfiguration.controller ?? state._textController;
+ if (initialValue != null && initialValueText != null) {
+ controller.text = initialValueText;
+ field.didChange(initialValue);
+ onSuggestionSelected?.call(initialValue);
+ }
+
+ return TypeAheadField(
+ suggestionsCallback: suggestionsCallback,
+ itemBuilder: itemBuilder,
+ autoFlipListDirection: true,
+ onSuggestionSelected: (value) {
+ controller.text = stringify(value);
+ field.didChange(value);
+
+ onSuggestionSelected?.call(value);
+ },
+ textFieldConfiguration: textFieldConfiguration.copyWith(
+ focusNode: textFieldConfiguration.focusNode ?? state.focusNode,
+ controller: controller,
+ decoration: effectiveDecoration.copyWith(
+ errorText: state.errorText,
+ ),
+ onChanged: (value) {
+ if (viewDataTypeFromTextEditingValue != null) {
+ field.didChange(
+ viewDataTypeFromTextEditingValue.call(value));
+ }
+ },
+ ),
+ suggestionsBoxDecoration: suggestionsBoxDecoration,
+ debounceDuration: debounceDuration,
+ suggestionsBoxController: suggestionsBoxController,
+ loadingBuilder: loadingBuilder,
+ noItemsFoundBuilder: noItemsFoundBuilder,
+ errorBuilder: errorBuilder,
+ transitionBuilder: transitionBuilder,
+ animationStart: animationStart,
+ animationDuration: animationDuration,
+ getImmediateSuggestions: getImmediateSuggestions,
+ suggestionsBoxVerticalOffset: suggestionsBoxVerticalOffset,
+ direction: direction,
+ hideOnLoading: hideOnLoading,
+ hideOnEmpty: hideOnEmpty,
+ hideOnError: hideOnError,
+ hideSuggestionsOnKeyboardHide: hideSuggestionsOnKeyboardHide,
+ keepSuggestionsOnLoading: keepSuggestionsOnLoading,
+ keepSuggestionsOnSuggestionSelected:
+ keepSuggestionsOnSuggestionSelected,
+ autoFlipDirection: autoFlipDirection,
+ hideKeyboard: hideKeyboard,
+ );
+ },
+ );
+
+ final String Function(V value) stringify;
+
+ @override
+ ReactiveFormFieldState createState() => _ReactiveTypeaheadState();
+}
+
+class _ReactiveTypeaheadState extends ReactiveFormFieldState {
+ late TextEditingController _textController;
+ FocusNode? _focusNode;
+ late FocusController _focusController;
+
+ @override
+ FocusNode get focusNode => _focusNode ?? _focusController.focusNode;
+
+ @override
+ void initState() {
+ super.initState();
+
+ final initialValue = value;
+ _textController = TextEditingController(
+ text: initialValue == null
+ ? ''
+ : (widget as DigitReactiveTypeAhead).stringify(initialValue),
+ );
+ }
+
+ @override
+ void subscribeControl() {
+ _registerFocusController(FocusController());
+ super.subscribeControl();
+ }
+
+ @override
+ void unsubscribeControl() {
+ _unregisterFocusController();
+ super.unsubscribeControl();
+ }
+
+ @override
+ void onControlValueChanged(dynamic value) {
+ final effectiveValue = (value == null) ? '' : value.toString();
+ _textController.value = _textController.value.copyWith(
+ text: effectiveValue,
+ selection: TextSelection.collapsed(offset: effectiveValue.length),
+ composing: TextRange.empty,
+ );
+
+ super.onControlValueChanged(value);
+ }
+
+ // @override
+ // ControlValueAccessor selectValueAccessor() {
+ // if (control is FormControl) {
+ // return IntValueAccessor() as ControlValueAccessor;
+ // } else if (control is FormControl) {
+ // return DoubleValueAccessor() as ControlValueAccessor;
+ // } else if (control is FormControl) {
+ // return DateTimeValueAccessor() as ControlValueAccessor;
+ // } else if (control is FormControl) {
+ // return TimeOfDayValueAccessor() as ControlValueAccessor;
+ // }
+ //
+ // return super.selectValueAccessor();
+ // }
+
+ void _registerFocusController(FocusController focusController) {
+ _focusController = focusController;
+ control.registerFocusController(focusController);
+ }
+
+ void _unregisterFocusController() {
+ control.unregisterFocusController(_focusController);
+ _focusController.dispose();
+ }
+
+ void _setFocusNode(FocusNode? focusNode) {
+ if (_focusNode != focusNode) {
+ _focusNode = focusNode;
+ _unregisterFocusController();
+ _registerFocusController(FocusController(focusNode: _focusNode));
+ }
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_row_card/digit_row_card.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_row_card/digit_row_card.dart
new file mode 100644
index 00000000..8a38f2a2
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_row_card/digit_row_card.dart
@@ -0,0 +1,58 @@
+import 'package:digit_components/models/digit_row_card/digit_row_card_model.dart';
+import 'package:flutter/material.dart';
+
+class DigitRowCard extends StatelessWidget {
+ final List rowItems;
+ final ValueChanged? onChanged;
+ final double width;
+
+ const DigitRowCard({
+ super.key,
+ this.onChanged,
+ required this.rowItems,
+ required this.width,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+
+ return Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: rowItems
+ .map(
+ (e) => SizedBox(
+ height: 38,
+ width: width,
+ child: GestureDetector(
+ onTap: () => onChanged?.call(e),
+ child: Container(
+ decoration: BoxDecoration(
+ border: Border.all(
+ color: e.isSelected
+ ? theme.colorScheme.secondary
+ : theme.colorScheme.onBackground,
+ ),
+ color: e.isSelected
+ ? theme.colorScheme.secondary
+ : theme.colorScheme.onSecondary,
+ ),
+ child: Center(
+ child: Text(
+ e.label,
+ style: TextStyle(
+ fontWeight: FontWeight.w400,
+ color: e.isSelected
+ ? theme.colorScheme.onSecondary
+ : theme.colorScheme.onBackground,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+ .toList(),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_search_bar.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_search_bar.dart
new file mode 100644
index 00000000..bfd6fc02
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_search_bar.dart
@@ -0,0 +1,65 @@
+import 'package:flutter/material.dart';
+
+class DigitSearchBar extends StatelessWidget {
+ final TextEditingController? controller;
+ final EdgeInsets? padding;
+ final EdgeInsets? margin;
+ final String? hintText;
+ final EdgeInsets? contentPadding;
+ final double? borderRadius;
+ final ValueChanged? onChanged;
+ final TextCapitalization textCapitalization;
+
+ const DigitSearchBar({
+ super.key,
+ this.controller,
+ this.padding,
+ this.margin,
+ this.hintText,
+ this.contentPadding,
+ this.borderRadius,
+ this.onChanged,
+ this.textCapitalization = TextCapitalization.none,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ return Card(
+ shape: RoundedRectangleBorder(
+ side: BorderSide(color: theme.scaffoldBackgroundColor, width: 1),
+ borderRadius: BorderRadius.circular(
+ borderRadius != null ? (borderRadius! * 3) : 30),
+ ),
+ margin: margin,
+ child: Padding(
+ padding: const EdgeInsets.all(10),
+ child: TextField(
+ controller: controller,
+ onChanged: onChanged,
+ textCapitalization: textCapitalization,
+ decoration: InputDecoration(
+ border: InputBorder.none,
+ hintText: hintText ?? 'Enter the field details',
+ filled: true,
+ fillColor: theme.cardColor,
+ contentPadding: contentPadding ??
+ const EdgeInsets.only(
+ left: 14.0,
+ bottom: 6.0,
+ top: 8.0,
+ ),
+ focusedBorder: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(borderRadius ?? 10.0),
+ borderSide: BorderSide(color: theme.cardColor),
+ ),
+ enabledBorder: UnderlineInputBorder(
+ borderSide: BorderSide(color: theme.scaffoldBackgroundColor),
+ borderRadius: BorderRadius.circular(borderRadius ?? 10.0),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_search_dropdown.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_search_dropdown.dart
new file mode 100644
index 00000000..04aab65c
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_search_dropdown.dart
@@ -0,0 +1,73 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+import 'package:reactive_flutter_typeahead/reactive_flutter_typeahead.dart';
+
+import 'digit_reactive_type_ahead.dart';
+
+class DigitSearchDropdown extends StatelessWidget {
+ final Iterable Function(Iterable items, String pattern)
+ suggestionsCallback;
+ final SuggestionSelectionCallback? onSuggestionSelected;
+ final String label;
+ final T? initialValue;
+ final String? initialValueText;
+ final Iterable menuItems;
+ final String formControlName;
+ final bool isRequired;
+ final VoidCallback? onChanged;
+ final String Function(T value) valueMapper;
+ final Map? validationMessages;
+
+ const DigitSearchDropdown({
+ Key? key,
+ required this.suggestionsCallback,
+ this.onSuggestionSelected,
+ required this.label,
+ required this.menuItems,
+ required this.formControlName,
+ this.isRequired = false,
+ required this.valueMapper,
+ this.initialValue,
+ this.onChanged,
+ this.validationMessages,
+ this.initialValueText,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.only(top: 16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ '$label${isRequired ? ' *' : ''}',
+ style: Theme.of(context).textTheme.bodyLarge,
+ ),
+ const SizedBox(height: 8),
+ DigitReactiveTypeAhead(
+ formControlName: formControlName,
+ stringify: valueMapper,
+ initialValue: initialValue,
+ initialValueText: initialValueText,
+ onSuggestionSelected: onSuggestionSelected,
+ debounceDuration: const Duration(milliseconds: 100),
+ suggestionsCallback: (pattern) => suggestionsCallback(
+ menuItems,
+ pattern,
+ ),
+ itemBuilder: (context, item) {
+ return Padding(
+ padding: const EdgeInsets.all(kPadding * 2),
+ child: Text(
+ valueMapper(item),
+ style: Theme.of(context).textTheme.bodyLarge,
+ ),
+ );
+ },
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_sync_dialog.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_sync_dialog.dart
new file mode 100644
index 00000000..be56bde4
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_sync_dialog.dart
@@ -0,0 +1,131 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+
+class DigitSyncDialog {
+ static Future show(
+ BuildContext context, {
+ Key? key,
+ bool barrierDismissible = false,
+ required DigitSyncDialogType type,
+ required String label,
+ DigitDialogActions? primaryAction,
+ DigitDialogActions? secondaryAction,
+ }) async {
+ return await DigitDialog.show(
+ context,
+ options: getDigitDialog(
+ type: type,
+ label: label,
+ barrierDismissible: barrierDismissible,
+ key: key,
+ primaryAction: primaryAction,
+ secondaryAction: secondaryAction,
+ ),
+ );
+ }
+
+ static DigitDialogOptions getDigitDialog({
+ Key? key,
+ bool barrierDismissible = false,
+ required DigitSyncDialogType type,
+ required String label,
+ DigitDialogActions? primaryAction,
+ DigitDialogActions? secondaryAction,
+ }) {
+ final content = DigitSyncDialogContent(
+ label: label,
+ type: type,
+ key: key,
+ );
+
+ switch (type) {
+ case DigitSyncDialogType.inProgress:
+ break;
+ case DigitSyncDialogType.complete:
+ assert(
+ primaryAction != null,
+ 'Primary Action cannot be null for sync complete dialog',
+ );
+
+ break;
+ case DigitSyncDialogType.failed:
+ assert(
+ primaryAction != null,
+ 'Primary Action cannot be null for sync failed dialog',
+ );
+ assert(
+ secondaryAction != null,
+ 'Secondary Action cannot be null for sync failed dialog',
+ );
+
+ break;
+ }
+
+ return DigitDialogOptions(
+ content: content,
+ primaryAction: primaryAction,
+ secondaryAction: secondaryAction,
+ barrierDismissible: barrierDismissible,
+ contentPadding: const EdgeInsets.fromLTRB(
+ kPadding,
+ kPadding * 3,
+ kPadding,
+ kPadding * 2,
+ ),
+ key: key,
+ );
+ }
+}
+
+class DigitSyncDialogContent extends StatelessWidget {
+ final String label;
+ final DigitSyncDialogType type;
+
+ final DigitDialogActions? primaryAction;
+ final DigitDialogActions? secondaryAction;
+
+ const DigitSyncDialogContent({
+ super.key,
+ required this.label,
+ required this.type,
+ this.primaryAction,
+ this.secondaryAction,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ IconData icon;
+ Color color;
+ TextStyle? labelStyle;
+
+ switch (type) {
+ case DigitSyncDialogType.inProgress:
+ icon = Icons.autorenew;
+ color = theme.colorScheme.secondary;
+ labelStyle = theme.textTheme.headlineSmall;
+ break;
+ case DigitSyncDialogType.complete:
+ icon = Icons.check_circle_outline;
+ color = theme.colorScheme.onSurfaceVariant;
+ labelStyle = theme.textTheme.headlineLarge;
+ break;
+ case DigitSyncDialogType.failed:
+ icon = Icons.error_outline;
+ color = theme.colorScheme.error;
+ labelStyle = theme.textTheme.headlineLarge;
+ break;
+ }
+
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Icon(icon, size: 32, color: color),
+ const SizedBox(height: kPadding * 2),
+ Text(label, style: labelStyle?.copyWith(color: color)),
+ ],
+ );
+ }
+}
+
+enum DigitSyncDialogType { inProgress, complete, failed }
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/digit_text_field.dart b/vehicle-tracker/packages/digit_components/lib/widgets/digit_text_field.dart
new file mode 100644
index 00000000..a0a07f3c
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/digit_text_field.dart
@@ -0,0 +1,85 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+class DigitTextField extends StatelessWidget {
+ final String label;
+ final TextEditingController? controller;
+ final String prefixText;
+ final int? maxLength;
+ final int? maxLines;
+ final bool isRequired;
+ final ValueChanged? onChange;
+ final List? inputFormatter;
+ final FocusNode? focusNode;
+ final TextInputType? textInputType;
+ final String? pattern;
+ final String? message;
+ final String? Function(String? value)? validator;
+ final TextCapitalization? textCapitalization;
+ final AutovalidateMode? autoValidation;
+ final bool obscureText;
+ final bool isDisabled;
+ final bool readOnly;
+ final bool? isFilled;
+ final Widget? suffixIcon;
+
+ const DigitTextField({
+ super.key,
+ required this.label,
+ this.prefixText = '',
+ this.maxLength,
+ this.controller,
+ this.isRequired = false,
+ this.textInputType,
+ this.inputFormatter,
+ this.pattern,
+ this.validator,
+ this.message,
+ this.focusNode,
+ this.textCapitalization,
+ this.onChange,
+ this.maxLines,
+ this.autoValidation,
+ this.obscureText = false,
+ this.isDisabled = false,
+ this.readOnly = false,
+ this.isFilled,
+ this.suffixIcon,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return LabeledField(
+ label: isRequired ? "$label *" : label,
+ child: TextFormField(
+ style: TextStyle(
+ color: readOnly == true
+ ? DigitTheme.instance.colorScheme.shadow
+ : DigitTheme.instance.colorScheme.onSurface),
+ controller: controller,
+ enabled: !isDisabled,
+ maxLength: maxLength,
+ keyboardType: textInputType ?? TextInputType.text,
+ autofocus: false,
+ inputFormatters: inputFormatter,
+ textCapitalization: textCapitalization ?? TextCapitalization.none,
+ onChanged: onChange,
+ maxLines: maxLines,
+ focusNode: focusNode,
+ obscureText: obscureText,
+ autovalidateMode: autoValidation,
+ readOnly: readOnly,
+ validator: (value) => validator?.call(value),
+ decoration: InputDecoration(
+ suffixIconConstraints: const BoxConstraints(
+ maxHeight: 48,
+ maxWidth: 48,
+ ),
+ //maxLines = 1 if suffixIcon != null
+ suffixIcon: suffixIcon,
+ ),
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/labeled_field.dart b/vehicle-tracker/packages/digit_components/lib/widgets/labeled_field.dart
new file mode 100644
index 00000000..7f63468d
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/labeled_field.dart
@@ -0,0 +1,45 @@
+import 'package:flutter/material.dart';
+
+class LabeledField extends StatelessWidget {
+ final Widget child;
+ final String label;
+ final IconData? icon;
+ final String? tooltipMessage;
+
+ const LabeledField(
+ {super.key,
+ required this.child,
+ required this.label,
+ this.icon,
+ this.tooltipMessage});
+
+ @override
+ Widget build(BuildContext context) => Padding(
+ padding: const EdgeInsets.only(top: 16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Row(
+ children: [
+ Flexible(
+ child: Text(
+ label,
+ style: Theme.of(context).textTheme.labelSmall,
+ ),
+ ),
+ icon != null
+ ? Tooltip(
+ message: tooltipMessage ?? '',
+ preferBelow: false,
+ triggerMode: TooltipTriggerMode.tap,
+ child: Icon(icon ?? Icons.info_outline))
+ : const SizedBox.shrink()
+ ],
+ ),
+ const SizedBox(height: 8),
+ child,
+ ],
+ ),
+ );
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_acknowledgement.dart b/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_acknowledgement.dart
new file mode 100644
index 00000000..22e6311b
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_acknowledgement.dart
@@ -0,0 +1,118 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+
+class DigitAcknowledgement extends StatelessWidget {
+ final String label;
+ final String? subLabel;
+ final String description;
+ final IconData icon;
+ final VoidCallback action;
+ final String actionLabel;
+ final Color color;
+
+ DigitAcknowledgement.success({
+ super.key,
+ required this.label,
+ this.subLabel,
+ required this.description,
+ required this.action,
+ required this.actionLabel,
+ IconData? icon,
+ Color? color,
+ }) : color = color ?? DigitTheme.instance.colors.darkSpringGreen,
+ icon = icon ?? Icons.check_circle;
+
+ DigitAcknowledgement.error({
+ super.key,
+ required this.label,
+ this.subLabel,
+ required this.description,
+ required this.action,
+ required this.actionLabel,
+ IconData? icon,
+ Color? color,
+ }) : color = color ?? DigitTheme.instance.colors.lavaRed,
+ icon = icon ?? Icons.error;
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+
+ return ScrollableContent(
+ children: [
+ DigitCard(
+ padding: EdgeInsets.zero,
+ child: Column(
+ children: [
+ Container(
+ padding: const EdgeInsets.symmetric(
+ horizontal: kPadding * 2,
+ vertical: kPadding * 4,
+ ),
+ constraints: BoxConstraints(
+ minHeight: MediaQuery.of(context).size.height / 3,
+ ),
+ color: color,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ textAlign: TextAlign.center,
+ label,
+ style: TextStyle(
+ fontSize: 32,
+ fontWeight: FontWeight.w400,
+ color: theme.colorScheme.onPrimary,
+ ),
+ ),
+ Padding(
+ padding: DigitTheme.instance.containerMargin,
+ child: Icon(
+ icon,
+ size: 32,
+ color: theme.colorScheme.onPrimary,
+ ),
+ ),
+ if (subLabel != null) ...[
+ Text(
+ textAlign: TextAlign.center,
+ subLabel!,
+ style: TextStyle(
+ fontSize: 26,
+ fontWeight: FontWeight.w700,
+ color: theme.colorScheme.onPrimary,
+ ),
+ )
+ ],
+ ],
+ ),
+ ),
+ Padding(
+ padding: DigitTheme.instance.containerMargin,
+ child: Align(
+ alignment: Alignment.centerLeft,
+ child: Text(
+ description,
+ style: theme.textTheme.bodyMedium,
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.fromLTRB(
+ kPadding,
+ kPadding,
+ kPadding,
+ kPadding * 2,
+ ),
+ child: DigitElevatedButton(
+ onPressed: action,
+ child: Text(actionLabel),
+ ),
+ )
+ ],
+ ),
+ ),
+ ],
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_language_card.dart b/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_language_card.dart
new file mode 100644
index 00000000..cb72a54f
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_language_card.dart
@@ -0,0 +1,46 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+import '../../models/digit_row_card/digit_row_card_model.dart';
+
+class DigitLanguageCard extends StatelessWidget {
+ final List digitRowCardItems;
+ final ValueChanged? onLanguageChange;
+ final VoidCallback onLanguageSubmit;
+ final String languageSubmitLabel;
+
+ const DigitLanguageCard({
+ super.key,
+ required this.digitRowCardItems,
+ this.onLanguageChange,
+ required this.onLanguageSubmit,
+ required this.languageSubmitLabel,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return DigitCard(
+ margin: const EdgeInsets.all(8),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.end,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ DigitRowCard(
+ onChanged: onLanguageChange,
+ rowItems: digitRowCardItems,
+ width:
+ (MediaQuery.of(context).size.width / digitRowCardItems.length) -
+ 16 * digitRowCardItems.length,
+ ),
+ const SizedBox(
+ height: 24,
+ width: 0,
+ ),
+ DigitElevatedButton(
+ onPressed: onLanguageSubmit,
+ child: Center(child: Text(languageSubmitLabel)),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_list_view.dart b/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_list_view.dart
new file mode 100644
index 00000000..1e64d9b6
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_list_view.dart
@@ -0,0 +1,62 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+
+class DigitListView extends StatelessWidget {
+ final IconData? prefixIcon;
+ final String title;
+ final String? description;
+ final IconData? sufixIcon;
+ final VoidCallback? onPressed;
+
+ const DigitListView(
+ {super.key,
+ this.prefixIcon,
+ required this.title,
+ this.description,
+ this.sufixIcon,
+ this.onPressed});
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ return DigitCard(
+ onPressed: onPressed,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ SizedBox(
+ width: MediaQuery.of(context).size.width - 100,
+ child: Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ Icon(
+ prefixIcon,
+ color: theme.colorScheme.secondary,
+ size: 24,
+ ),
+ const SizedBox(
+ width: 14,
+ ),
+ Text(
+ title,
+ style: theme.textTheme.headlineMedium,
+ ),
+ ],
+ ),
+ Offstage(
+ offstage: description == null,
+ child: Text(description!),
+ ),
+ ],
+ )),
+ Icon(
+ sufixIcon,
+ color: theme.colorScheme.secondary,
+ size: 36,
+ ),
+ ],
+ ));
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_loader.dart b/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_loader.dart
new file mode 100644
index 00000000..8af8995f
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_loader.dart
@@ -0,0 +1,141 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_spinkit/flutter_spinkit.dart';
+
+class Loaders {
+ static circularLoader(BuildContext context) {
+ return WillPopScope(
+ onWillPop: () async => false,
+ child: SizedBox(
+ height: MediaQuery.of(context).size.height,
+ width: MediaQuery.of(context).size.width,
+ child: SimpleDialog(
+ elevation: 0.0,
+ backgroundColor: Colors.transparent,
+ children: [
+ Center(
+ child: Column(children: [
+ CircularProgressIndicator(
+ color: Theme.of(context).colorScheme.secondary,
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ const Text(
+ 'Loading...',
+ style: TextStyle(
+ color: Color.fromRGBO(0, 0, 0, 1),
+ fontFamily: 'Roboto',
+ fontSize: 16,
+ fontWeight: FontWeight.w700),
+ )
+ ]),
+ )
+ ]),
+ ));
+ }
+
+ static Future showLoadingDialog(
+ BuildContext context, {
+ String? label,
+ }) async {
+ return showDialog(
+ context: context,
+ barrierDismissible: false,
+ builder: (BuildContext context) {
+ return WillPopScope(
+ onWillPop: () async => false,
+ child: SizedBox(
+ height: MediaQuery.of(context).size.height,
+ width: MediaQuery.of(context).size.width,
+ child: SimpleDialog(
+ elevation: 0.0,
+ backgroundColor: Colors.transparent,
+ children: [
+ Center(
+ child: Column(children: [
+ CircularProgressIndicator(
+ color: Theme.of(context).colorScheme.secondary,
+ ),
+ const SizedBox(
+ height: 10,
+ ),
+ Text(
+ label ?? 'Loading...',
+ style: const TextStyle(
+ color: Color(0xffFFFFFF),
+ fontFamily: 'Roboto',
+ fontSize: 16,
+ fontWeight: FontWeight.w700),
+ )
+ ]),
+ )
+ ]),
+ ));
+ });
+ }
+
+ static void showLoader(BuildContext context, {String? text}) {
+ showDialog(
+ context: context,
+ barrierColor: Colors.transparent,
+ barrierDismissible: false,
+ builder: (BuildContext context) {
+ return WillPopScope(
+ onWillPop: () async => false,
+ child: Dialog(
+ // backgroundColor:CustomColors.BLACK,
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(15)),
+ child: WillPopScope(
+ onWillPop: () async {
+ return true;
+ },
+ child: Container(
+ padding: const EdgeInsets.symmetric(
+ vertical: 10, horizontal: 10),
+ child: Wrap(
+ runSpacing: 15,
+ alignment: WrapAlignment.center,
+ children: [
+ const SpinKitCircle(
+ color: Colors.white,
+ size: 50.0,
+ ),
+ Text(
+ text ??
+ ' Getting image data \n Please check the values once done. ',
+ textAlign: TextAlign.center,
+ style: const TextStyle(color: Colors.white),
+ ),
+ ],
+ )),
+ ),
+ ),
+ );
+ });
+ }
+}
+
+class DigitLoader extends StatefulWidget {
+ const DigitLoader({super.key});
+
+ @override
+ State createState() {
+ return DigitLoaderState();
+ }
+}
+
+class DigitLoaderState extends State {
+ @override
+ void initState() {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ Loaders.showLoadingDialog(context);
+ });
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_table.dart b/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_table.dart
new file mode 100644
index 00000000..0df29e96
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_table.dart
@@ -0,0 +1,188 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:digit_components/models/digit_table_model.dart';
+import 'package:digit_components/theme/digit_theme.dart';
+import 'package:flutter/material.dart';
+import 'package:horizontal_data_table/horizontal_data_table.dart';
+
+class DigitTable extends StatelessWidget {
+ final double columnRowFixedHeight = 52.0;
+ final List headerList;
+ final List tableData;
+ final double leftColumnWidth;
+ final double rightColumnWidth;
+ final double? height;
+
+ const DigitTable({
+ Key? key,
+ required this.headerList,
+ required this.tableData,
+ required this.leftColumnWidth,
+ required this.rightColumnWidth,
+ this.height,
+ }) : super(key: key);
+
+ List? _getTitleWidget(ThemeData theme) {
+ var index = 0;
+ return headerList.map((e) {
+ index++;
+
+ if (e.isSortingRequired ?? false) {
+ return TextButton(
+ style: TextButton.styleFrom(
+ padding: EdgeInsets.zero,
+ ),
+ onPressed: e.callBack == null ? null : () => e.callBack!(e),
+ child: _getTitleItemWidget((e.label), theme,
+ isAscending: e.isAscendingOrder,
+ isBorderRequired: (index - 1) == 0));
+ } else {
+ return _getTitleItemWidget(e.label, theme,
+ isBorderRequired: (index - 1) == 0);
+ }
+ }).toList();
+ }
+
+ Widget _getTitleItemWidget(String label, ThemeData theme,
+ {bool? isAscending, bool isBorderRequired = false}) {
+ var textWidget = Text((label), style: theme.textTheme.headlineSmall);
+ final tableCellBorder = DigitTheme.instance.tableCellBorder;
+ final tableCellStrongBorder = DigitTheme.instance.tableCellStrongBorder;
+ final surfaceColor = DigitTheme.instance.colorScheme.surface;
+
+ return Container(
+ decoration: isBorderRequired
+ ? BoxDecoration(
+ border: Border(
+ left: tableCellBorder,
+ bottom: tableCellBorder,
+ right: tableCellStrongBorder,
+ ),
+ color: surfaceColor)
+ : null,
+ width: leftColumnWidth,
+ height: 56,
+ color: !isBorderRequired ? surfaceColor : null,
+ padding: const EdgeInsets.only(left: 17, right: 5, top: 6, bottom: 6),
+ alignment: Alignment.centerLeft,
+ child: isAscending != null
+ ? Wrap(
+ crossAxisAlignment: WrapCrossAlignment.center,
+ spacing: 5,
+ children: [
+ textWidget,
+ Icon(isAscending
+ ? Icons.arrow_upward
+ : Icons.arrow_downward_sharp)
+ ],
+ )
+ : textWidget,
+ );
+ }
+
+ double columnRowIncreasedHeight(int index) {
+ return (50 +
+ tableData[index].tableRow.first.label.substring(28).length.toDouble());
+ //if greater than 28 characters
+ }
+
+ Widget _generateColumnRow(BuildContext context, int index, String input,
+ {TextStyle? style}) {
+ return Container(
+ width: leftColumnWidth,
+ height: tableData[index].tableRow.first.label.length > 28
+ ? columnRowIncreasedHeight(index)
+ : columnRowFixedHeight,
+ padding: const EdgeInsets.only(left: 17, right: 5, top: 6, bottom: 6),
+ alignment: Alignment.centerLeft,
+ color: index % 2 == 0
+ ? DigitTheme.instance.colorScheme.background
+ : DigitTheme.instance.colorScheme.surface,
+ child: Row(
+ children: [
+ Expanded(
+ child: Text(
+ (input),
+ style: style,
+ maxLines: 2,
+ overflow: TextOverflow.ellipsis,
+ ),
+ )
+ ],
+ ),
+ );
+ }
+
+ Widget _generateRightHandSideColumnRow(BuildContext context, int index) {
+ final theme = Theme.of(context);
+ var data = tableData[index];
+ var list = [];
+ for (int i = 1; i < data.tableRow.length; i++) {
+ list.add(_generateColumnRow(context, index, data.tableRow[i].label,
+ style: data.tableRow[i].style));
+ }
+
+ return Container(
+ color: index % 2 == 0 ? theme.scaffoldBackgroundColor : theme.cardColor,
+ child: Row(children: list));
+ }
+
+ Widget _generateFirstColumnRow(BuildContext context, int index) {
+ var data = tableData[index].tableRow.first;
+ final tableCellBorder = DigitTheme.instance.tableCellBorder;
+ final tableCellStrongBorder = DigitTheme.instance.tableCellStrongBorder;
+ return InkWell(
+ onTap: () {
+ if (data.callBack != null) {
+ data.callBack!(data);
+ }
+ },
+ child: Container(
+ decoration: BoxDecoration(
+ color: DigitTheme.instance.colorScheme.surface,
+ border: Border(
+ left: tableCellBorder,
+ right: tableCellStrongBorder,
+ )),
+ width: leftColumnWidth,
+ height: tableData[index].tableRow.first.label.length > 28
+ ? columnRowIncreasedHeight(index)
+ : columnRowFixedHeight,
+ padding: const EdgeInsets.only(left: 17, right: 5, top: 6, bottom: 6),
+ alignment: Alignment.centerLeft,
+ child: Text(tableData[index].tableRow.first.label,
+ style: tableData[index].tableRow.first.style),
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ return SizedBox(
+ height: height,
+ child: Container(
+ margin: const EdgeInsets.fromLTRB(0, 16, 0, 0),
+ decoration: BoxDecoration(
+ border: Border.all(color: DigitTheme.instance.colorScheme.outline),
+ borderRadius: const BorderRadius.all(Radius.circular(4.0)),
+ ),
+ child: HorizontalDataTable(
+ leftHandSideColumnWidth: leftColumnWidth,
+ rightHandSideColumnWidth: rightColumnWidth,
+ isFixedHeader: true,
+ headerWidgets: _getTitleWidget(theme),
+ leftSideItemBuilder: _generateFirstColumnRow,
+ rightSideItemBuilder: _generateRightHandSideColumnRow,
+ itemCount: tableData.length,
+ rowSeparatorWidget: const Divider(
+ color: Colors.black54,
+ height: 1.0,
+ thickness: 0.0,
+ ),
+ ),
+ ),
+ );
+ }
+
+//Need to move this to theme
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_table_card.dart b/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_table_card.dart
new file mode 100644
index 00000000..67c698d2
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_table_card.dart
@@ -0,0 +1,64 @@
+import 'package:digit_components/theme/digit_theme.dart';
+import 'package:flutter/material.dart';
+
+class DigitTableCard extends StatelessWidget {
+ final Map element;
+ final Border? border;
+ final Color? color;
+ final EdgeInsetsGeometry? padding;
+ final double gap;
+ final num fraction;
+
+ const DigitTableCard({
+ super.key,
+ required this.element,
+ this.border,
+ this.color,
+ this.padding,
+ this.gap = 0,
+ this.fraction = 2,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ return Padding(
+ padding: const EdgeInsets.only(top: 16),
+ child: Container(
+ decoration: BoxDecoration(
+ color: color,
+ borderRadius: BorderRadius.circular(4),
+ border: border),
+ child: Padding(
+ padding: padding ?? const EdgeInsets.only(right: 8, bottom: 16),
+ child: Column(
+ children: element.keys
+ .map((e) => Container(
+ margin: DigitTheme.instance.verticalMargin,
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(
+ width: MediaQuery.of(context).size.width / fraction,
+ child: Text(
+ e,
+ style: theme.textTheme.headlineSmall,
+ textAlign: TextAlign.start,
+ ),
+ ),
+ SizedBox(width: gap),
+ Flexible(
+ child: Padding(
+ padding: const EdgeInsets.only(top: 1.4),
+ child: Text(element[e].toString()),
+ )),
+ ],
+ ),
+ ))
+ .toList(),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_walkthrough.dart b/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_walkthrough.dart
new file mode 100644
index 00000000..2346c7b0
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/molecules/digit_walkthrough.dart
@@ -0,0 +1,75 @@
+import 'package:digit_components/widgets/atoms/anchor_overlay.dart';
+import 'package:flutter/material.dart';
+import 'package:overlay_builder/overlay_builder.dart';
+
+class DigitWalkthrough extends StatefulWidget {
+ final Widget child;
+ final String? title;
+ final GlobalKey overlayWidget;
+ final TextAlign titleAlignment;
+ final String description;
+
+ const DigitWalkthrough(
+ {super.key,
+ required this.child,
+ required this.overlayWidget,
+ this.title,
+ required this.titleAlignment,
+ required this.description});
+
+ @override
+ State createState() => _DigitWalkthroughState();
+}
+
+class _DigitWalkthroughState extends State {
+ final _overlayFullscreen = GlobalKey();
+ bool showOverlay = false;
+ late double childHeight;
+ late Offset position;
+
+ _DigitWalkthroughState();
+ OverlayWidgetState? get overlayWidgetController {
+ return widget.overlayWidget.currentState;
+ }
+
+ OverlayWidgetState? get overlayFullscreenController {
+ return _overlayFullscreen.currentState;
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ initOffsetsPositions();
+ });
+ }
+
+ void initOffsetsPositions() {
+ Offset position = ((widget.child.key as GlobalKey)
+ .currentContext
+ ?.findRenderObject() as RenderBox)
+ .localToGlobal(Offset.zero);
+ childHeight = (widget.child.key as GlobalKey).currentContext!.size!.height;
+ this.position = position;
+ }
+
+ void onTap() {
+ setState(() {
+ showOverlay = !showOverlay;
+ });
+ overlayWidgetController?.toggle();
+ overlayFullscreenController?.toggle();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return AnchoredOverlay(
+ showOverlay: showOverlay,
+ description: widget.description,
+ paramKey: widget.overlayWidget,
+ onTap: onTap,
+ position: position,
+ childHeight: childHeight,
+ child: widget.child);
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/powered_by_digit.dart b/vehicle-tracker/packages/digit_components/lib/widgets/powered_by_digit.dart
new file mode 100644
index 00000000..1496c5da
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/powered_by_digit.dart
@@ -0,0 +1,35 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+
+class PoweredByDigit extends StatefulWidget {
+ final Size? size;
+ final EdgeInsets? padding;
+ final bool isWhiteLogo;
+
+ const PoweredByDigit(
+ {super.key, this.size, this.padding, this.isWhiteLogo = false});
+
+ @override
+ State createState() => _PoweredByDigitState();
+}
+
+class _PoweredByDigitState extends State {
+ @override
+ Widget build(BuildContext context) => SafeArea(
+ child: Center(
+ child: Container(
+ height: 24,
+ padding: widget.padding ?? const EdgeInsets.all(4),
+ alignment: Alignment.center,
+ child: Image.asset(
+ 'assets/images/powered_by_digit.png',
+ package: 'digit_components',
+ fit: BoxFit.contain,
+ color: widget.isWhiteLogo
+ ? DigitTheme.instance.colorScheme.onPrimary
+ : null
+ ),
+ ),
+ ),
+ );
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/scrollable_content.dart b/vehicle-tracker/packages/digit_components/lib/widgets/scrollable_content.dart
new file mode 100644
index 00000000..70527bcf
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/scrollable_content.dart
@@ -0,0 +1,56 @@
+import 'package:flutter/material.dart';
+
+class ScrollableContent extends StatelessWidget {
+ final Widget? header;
+ final Widget? footer;
+ final List slivers;
+ final bool? primary;
+ final ScrollController? controller;
+ final MainAxisAlignment mainAxisAlignment;
+ final CrossAxisAlignment crossAxisAlignment;
+ final List children;
+
+ const ScrollableContent({
+ super.key,
+ this.footer,
+ this.header,
+ this.primary,
+ this.controller,
+ this.mainAxisAlignment = MainAxisAlignment.start,
+ this.crossAxisAlignment = CrossAxisAlignment.start,
+ this.children = const [],
+ this.slivers = const [],
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return CustomScrollView(
+ controller: controller,
+ primary: primary,
+ slivers: [
+ if (header != null) SliverToBoxAdapter(child: header),
+ ...slivers,
+ SliverFillRemaining(
+ hasScrollBody: false,
+ child: Center(
+ child: Column(
+ crossAxisAlignment: crossAxisAlignment,
+ children: [
+ Expanded(
+ child: Column(
+ mainAxisAlignment: mainAxisAlignment,
+ children: children,
+ ),
+ ),
+ if (footer != null) ...[
+ const SizedBox(height: 16),
+ footer!,
+ ],
+ ],
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+}
diff --git a/vehicle-tracker/packages/digit_components/lib/widgets/widgets.dart b/vehicle-tracker/packages/digit_components/lib/widgets/widgets.dart
new file mode 100644
index 00000000..642bf385
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/lib/widgets/widgets.dart
@@ -0,0 +1,32 @@
+export './atoms//digit_icon_button.dart';
+export './atoms/digit_action_dialog.dart';
+export './atoms/digit_date_form_picker.dart';
+export './atoms/digit_dropdown.dart';
+export './atoms/digit_integer_form_picker.dart';
+export './atoms/digit_outline_icon_button.dart';
+export './atoms/digit_reactive_dropdown.dart';
+export './atoms/digit_table_item.dart';
+export './atoms/digit_table_item_title.dart';
+export './atoms/digit_text_form_field.dart';
+export './digit_banner.dart';
+export './digit_card.dart';
+export './digit_checkbox_tile.dart';
+export './digit_dialog.dart';
+export './digit_elevated_button.dart';
+export './digit_icon_tile.dart';
+export './digit_info_card.dart';
+export './digit_outline_button.dart';
+export './digit_search_bar.dart';
+export './digit_text_field.dart';
+export './labeled_field.dart';
+export './molecules/digit_acknowledgement.dart';
+export './molecules/digit_language_card.dart';
+export './molecules/digit_list_view.dart';
+export './molecules/digit_loader.dart';
+export './molecules/digit_table.dart';
+export './molecules/digit_table_card.dart';
+export './powered_by_digit.dart';
+export './scrollable_content.dart';
+export 'atoms/digit_toast_helper.dart';
+export 'digit_row_card/digit_row_card.dart';
+export 'digit_search_dropdown.dart';
diff --git a/vehicle-tracker/packages/digit_components/pubspec.lock b/vehicle-tracker/packages/digit_components/pubspec.lock
new file mode 100644
index 00000000..7eaba03f
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/pubspec.lock
@@ -0,0 +1,831 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ _fe_analyzer_shared:
+ dependency: transitive
+ description:
+ name: _fe_analyzer_shared
+ sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a
+ url: "https://pub.dev"
+ source: hosted
+ version: "61.0.0"
+ analyzer:
+ dependency: transitive
+ description:
+ name: analyzer
+ sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.13.0"
+ args:
+ dependency: transitive
+ description:
+ name: args
+ sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.2"
+ async:
+ dependency: transitive
+ description:
+ name: async
+ sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.11.0"
+ bloc:
+ dependency: transitive
+ description:
+ name: bloc
+ sha256: f53a110e3b48dcd78136c10daa5d51512443cea5e1348c9d80a320095fa2db9e
+ url: "https://pub.dev"
+ source: hosted
+ version: "8.1.3"
+ bloc_test:
+ dependency: "direct dev"
+ description:
+ name: bloc_test
+ sha256: "55a48f69e0d480717067c5377c8485a3fcd41f1701a820deef72fa0f4ee7215f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.1.6"
+ boolean_selector:
+ dependency: transitive
+ description:
+ name: boolean_selector
+ sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
+ build:
+ dependency: transitive
+ description:
+ name: build
+ sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
+ build_config:
+ dependency: transitive
+ description:
+ name: build_config
+ sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ build_daemon:
+ dependency: transitive
+ description:
+ name: build_daemon
+ sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.1"
+ build_resolvers:
+ dependency: transitive
+ description:
+ name: build_resolvers
+ sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.2"
+ build_runner:
+ dependency: "direct main"
+ description:
+ name: build_runner
+ sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.8"
+ build_runner_core:
+ dependency: transitive
+ description:
+ name: build_runner_core
+ sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.3.0"
+ built_collection:
+ dependency: transitive
+ description:
+ name: built_collection
+ sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.1.1"
+ built_value:
+ dependency: transitive
+ description:
+ name: built_value
+ sha256: fedde275e0a6b798c3296963c5cd224e3e1b55d0e478d5b7e65e6b540f363a0e
+ url: "https://pub.dev"
+ source: hosted
+ version: "8.9.1"
+ characters:
+ dependency: transitive
+ description:
+ name: characters
+ sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
+ checked_yaml:
+ dependency: transitive
+ description:
+ name: checked_yaml
+ sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.3"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ code_builder:
+ dependency: transitive
+ description:
+ name: code_builder
+ sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.10.0"
+ collection:
+ dependency: transitive
+ description:
+ name: collection
+ sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.17.1"
+ convert:
+ dependency: transitive
+ description:
+ name: convert
+ sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.1"
+ coverage:
+ dependency: transitive
+ description:
+ name: coverage
+ sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.6.4"
+ crypto:
+ dependency: transitive
+ description:
+ name: crypto
+ sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.3"
+ dart_style:
+ dependency: transitive
+ description:
+ name: dart_style
+ sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.2"
+ diff_match_patch:
+ dependency: transitive
+ description:
+ name: diff_match_patch
+ sha256: "2efc9e6e8f449d0abe15be240e2c2a3bcd977c8d126cfd70598aee60af35c0a4"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.4.1"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.1"
+ file:
+ dependency: transitive
+ description:
+ name: file
+ sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.0"
+ fixnum:
+ dependency: transitive
+ description:
+ name: fixnum
+ sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ flutter:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_bloc:
+ dependency: "direct main"
+ description:
+ name: flutter_bloc
+ sha256: "87325da1ac757fcc4813e6b34ed5dd61169973871fdf181d6c2109dd6935ece1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "8.1.4"
+ flutter_keyboard_visibility:
+ dependency: transitive
+ description:
+ name: flutter_keyboard_visibility
+ sha256: "4983655c26ab5b959252ee204c2fffa4afeb4413cd030455194ec0caa3b8e7cb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.4.1"
+ flutter_keyboard_visibility_linux:
+ dependency: transitive
+ description:
+ name: flutter_keyboard_visibility_linux
+ sha256: "6fba7cd9bb033b6ddd8c2beb4c99ad02d728f1e6e6d9b9446667398b2ac39f08"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.0"
+ flutter_keyboard_visibility_macos:
+ dependency: transitive
+ description:
+ name: flutter_keyboard_visibility_macos
+ sha256: c5c49b16fff453dfdafdc16f26bdd8fb8d55812a1d50b0ce25fc8d9f2e53d086
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.0"
+ flutter_keyboard_visibility_platform_interface:
+ dependency: transitive
+ description:
+ name: flutter_keyboard_visibility_platform_interface
+ sha256: e43a89845873f7be10cb3884345ceb9aebf00a659f479d1c8f4293fcb37022a4
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.0"
+ flutter_keyboard_visibility_web:
+ dependency: transitive
+ description:
+ name: flutter_keyboard_visibility_web
+ sha256: d3771a2e752880c79203f8d80658401d0c998e4183edca05a149f5098ce6e3d1
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.0"
+ flutter_keyboard_visibility_windows:
+ dependency: transitive
+ description:
+ name: flutter_keyboard_visibility_windows
+ sha256: fc4b0f0b6be9b93ae527f3d527fb56ee2d918cd88bbca438c478af7bcfd0ef73
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.0"
+ flutter_lints:
+ dependency: "direct dev"
+ description:
+ name: flutter_lints
+ sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.3"
+ flutter_localizations:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_spinkit:
+ dependency: "direct main"
+ description:
+ name: flutter_spinkit
+ sha256: b39c753e909d4796906c5696a14daf33639a76e017136c8d82bf3e620ce5bb8e
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.2.0"
+ flutter_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_typeahead:
+ dependency: "direct main"
+ description:
+ name: flutter_typeahead
+ sha256: b9942bd5b7611a6ec3f0730c477146cffa4cd4b051077983ba67ddfc9e7ee818
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.8.0"
+ flutter_web_plugins:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ fluttertoast:
+ dependency: "direct main"
+ description:
+ name: fluttertoast
+ sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1
+ url: "https://pub.dev"
+ source: hosted
+ version: "8.2.4"
+ freezed:
+ dependency: "direct dev"
+ description:
+ name: freezed
+ sha256: "57247f692f35f068cae297549a46a9a097100685c6780fe67177503eea5ed4e5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.7"
+ freezed_annotation:
+ dependency: "direct main"
+ description:
+ name: freezed_annotation
+ sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
+ frontend_server_client:
+ dependency: transitive
+ description:
+ name: frontend_server_client
+ sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.2.0"
+ glob:
+ dependency: transitive
+ description:
+ name: glob
+ sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
+ graphs:
+ dependency: transitive
+ description:
+ name: graphs
+ sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.1"
+ horizontal_data_table:
+ dependency: "direct main"
+ description:
+ name: horizontal_data_table
+ sha256: c8ab5256bbced698a729f3e0ff2cb0e8e97416cdbb082860370eaf883badf722
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.3.1"
+ http_multi_server:
+ dependency: transitive
+ description:
+ name: http_multi_server
+ sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.2.1"
+ http_parser:
+ dependency: transitive
+ description:
+ name: http_parser
+ sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.2"
+ intl:
+ dependency: "direct main"
+ description:
+ name: intl
+ sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.18.0"
+ io:
+ dependency: transitive
+ description:
+ name: io
+ sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.4"
+ js:
+ dependency: transitive
+ description:
+ name: js
+ sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.6.7"
+ json_annotation:
+ dependency: "direct main"
+ description:
+ name: json_annotation
+ sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.8.1"
+ json_serializable:
+ dependency: "direct dev"
+ description:
+ name: json_serializable
+ sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.7.1"
+ lints:
+ dependency: transitive
+ description:
+ name: lints
+ sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
+ location:
+ dependency: "direct main"
+ description:
+ name: location
+ sha256: "06be54f682c9073cbfec3899eb9bc8ed90faa0e17735c9d9fa7fe426f5be1dd1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.0.3"
+ location_platform_interface:
+ dependency: "direct main"
+ description:
+ name: location_platform_interface
+ sha256: "8aa1d34eeecc979d7c9fe372931d84f6d2ebbd52226a54fe1620de6fdc0753b1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.2"
+ location_web:
+ dependency: transitive
+ description:
+ name: location_web
+ sha256: ec484c66e8a4ff1ee5d044c203f4b6b71e3a0556a97b739a5bc9616de672412b
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.2.0"
+ logging:
+ dependency: "direct main"
+ description:
+ name: logging
+ sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.0"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.12.15"
+ material_color_utilities:
+ dependency: transitive
+ description:
+ name: material_color_utilities
+ sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.0"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.9.1"
+ mime:
+ dependency: transitive
+ description:
+ name: mime
+ sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.4"
+ mocktail:
+ dependency: "direct dev"
+ description:
+ name: mocktail
+ sha256: "80a996cd9a69284b3dc521ce185ffe9150cde69767c2d3a0720147d93c0cef53"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.0"
+ nested:
+ dependency: transitive
+ description:
+ name: nested
+ sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.0"
+ node_preamble:
+ dependency: transitive
+ description:
+ name: node_preamble
+ sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.2"
+ overlay_builder:
+ dependency: "direct main"
+ description:
+ name: overlay_builder
+ sha256: "58b97bc5f67a2e2bb7006dd88e697ac757dfffc9dbd1e7dfc1917fb510a4b5c8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ package_config:
+ dependency: transitive
+ description:
+ name: package_config
+ sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.0"
+ path:
+ dependency: transitive
+ description:
+ name: path
+ sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.8.3"
+ plugin_platform_interface:
+ dependency: "direct main"
+ description:
+ name: plugin_platform_interface
+ sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.8"
+ pointer_interceptor:
+ dependency: transitive
+ description:
+ name: pointer_interceptor
+ sha256: acfcd63c00ec3d5a7894b0e2a875893716d31958fe03f064734dba7dfd9113d9
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.9.3+5"
+ pool:
+ dependency: transitive
+ description:
+ name: pool
+ sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.5.1"
+ provider:
+ dependency: transitive
+ description:
+ name: provider
+ sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.1.2"
+ pub_semver:
+ dependency: transitive
+ description:
+ name: pub_semver
+ sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.4"
+ pubspec_parse:
+ dependency: transitive
+ description:
+ name: pubspec_parse
+ sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.3"
+ reactive_flutter_typeahead:
+ dependency: "direct main"
+ description:
+ name: reactive_flutter_typeahead
+ sha256: "8806c5c26cce77670daced798163ff758a8b98d25b9971ad32f1ed9cfc3c7546"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.7.0"
+ reactive_forms:
+ dependency: "direct main"
+ description:
+ name: reactive_forms
+ sha256: "5aa9c48a0626c20d00a005e597cb10efbdebbfeecb9c4227b03a5945fbb91ec4"
+ url: "https://pub.dev"
+ source: hosted
+ version: "14.3.0"
+ shelf:
+ dependency: transitive
+ description:
+ name: shelf
+ sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.1"
+ shelf_packages_handler:
+ dependency: transitive
+ description:
+ name: shelf_packages_handler
+ sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.2"
+ shelf_static:
+ dependency: transitive
+ description:
+ name: shelf_static
+ sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.2"
+ shelf_web_socket:
+ dependency: transitive
+ description:
+ name: shelf_web_socket
+ sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.4"
+ sky_engine:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.99"
+ source_gen:
+ dependency: transitive
+ description:
+ name: source_gen
+ sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.5.0"
+ source_helper:
+ dependency: transitive
+ description:
+ name: source_helper
+ sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.4"
+ source_map_stack_trace:
+ dependency: transitive
+ description:
+ name: source_map_stack_trace
+ sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
+ source_maps:
+ dependency: transitive
+ description:
+ name: source_maps
+ sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.10.12"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.9.1"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.11.0"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
+ stream_transform:
+ dependency: transitive
+ description:
+ name: stream_transform
+ sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.0"
+ string_scanner:
+ dependency: transitive
+ description:
+ name: string_scanner
+ sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.0"
+ term_glyph:
+ dependency: transitive
+ description:
+ name: term_glyph
+ sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.1"
+ test:
+ dependency: transitive
+ description:
+ name: test
+ sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.24.1"
+ test_api:
+ dependency: transitive
+ description:
+ name: test_api
+ sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.5.1"
+ test_core:
+ dependency: transitive
+ description:
+ name: test_core
+ sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.5.1"
+ timing:
+ dependency: transitive
+ description:
+ name: timing
+ sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.1"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.2"
+ vector_math:
+ dependency: transitive
+ description:
+ name: vector_math
+ sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.4"
+ vm_service:
+ dependency: transitive
+ description:
+ name: vm_service
+ sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583
+ url: "https://pub.dev"
+ source: hosted
+ version: "11.10.0"
+ watcher:
+ dependency: transitive
+ description:
+ name: watcher
+ sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ web_socket_channel:
+ dependency: transitive
+ description:
+ name: web_socket_channel
+ sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.0"
+ webkit_inspection_protocol:
+ dependency: transitive
+ description:
+ name: webkit_inspection_protocol
+ sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.1"
+ yaml:
+ dependency: transitive
+ description:
+ name: yaml
+ sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.2"
+sdks:
+ dart: ">=3.0.0 <4.0.0"
+ flutter: ">=3.7.0"
diff --git a/vehicle-tracker/packages/digit_components/pubspec.yaml b/vehicle-tracker/packages/digit_components/pubspec.yaml
new file mode 100644
index 00000000..c38f7205
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/pubspec.yaml
@@ -0,0 +1,49 @@
+name: digit_components
+description: Digit UI Design specification Flutter widgets. Import this package by adding digit_components in respective pubspec.yaml dependencies.
+version: 0.0.1+8
+homepage: https://github.com/egovernments/health-campaign-field-worker-app/tree/pub.dev/packages/digit_components
+
+environment:
+ sdk: '>=2.19.0 <4.0.0'
+ flutter: ">=1.17.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+ flutter_bloc: ^8.1.1
+ intl: ^0.18.0
+ freezed_annotation: ^2.2.0
+ horizontal_data_table: ^4.1.3
+ location: ^5.0.0
+ location_platform_interface: ^3.0.0
+ json_annotation: ^4.7.0
+ logging: ^1.1.0
+ build_runner: ^2.2.1
+ reactive_forms: ^14.2.0
+ plugin_platform_interface: ^2.1.3
+ flutter_localizations:
+ sdk: flutter
+ fluttertoast: ^8.1.2
+ flutter_spinkit: ^5.1.0
+ reactive_flutter_typeahead: ^0.7.0
+ flutter_typeahead: ^4.3.7
+ overlay_builder: ^1.1.0
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ flutter_lints: ^2.0.0
+ json_serializable: ^6.4.0
+ freezed: ^2.2.0
+ bloc_test: ^9.1.0
+ mocktail: ^0.3.0
+
+flutter:
+ assets:
+ - assets/images/
+ fonts:
+ - family: Roboto
+ fonts:
+ - asset: assets/fonts/Roboto/RobotoCondensed-Regular.ttf
+ - asset: assets/fonts/Roboto/Roboto-Regular.ttf
+
diff --git a/vehicle-tracker/packages/digit_components/test/bloc/location_test.dart b/vehicle-tracker/packages/digit_components/test/bloc/location_test.dart
new file mode 100644
index 00000000..89290ef4
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/bloc/location_test.dart
@@ -0,0 +1,99 @@
+import 'package:bloc_test/bloc_test.dart';
+import 'package:digit_components/blocs/location/location.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:location/location.dart';
+import 'package:location_platform_interface/location_platform_interface.dart';
+import 'package:mocktail/mocktail.dart';
+import 'package:plugin_platform_interface/plugin_platform_interface.dart';
+
+class MockLocationPlatform extends Mock
+ with MockPlatformInterfaceMixin
+ implements LocationPlatform {}
+
+void main() {
+ const mockLat = -25.953724;
+ const mockLng = 32.588711;
+
+ group('Location bloc', () {
+ blocTest(
+ 'throws if location permission is not granted',
+ build: () => LocationBloc(location: Location()),
+ setUp: () {
+ final mock = MockLocationPlatform();
+ const denied = PermissionStatus.denied;
+
+ when(() => mock.hasPermission()).thenAnswer((_) async => denied);
+ when(() => mock.requestPermission()).thenAnswer((_) async => denied);
+
+ LocationPlatform.instance = mock;
+ },
+ act: (bloc) => bloc.add(const RequestLocationPermissionEvent()),
+ errors: () => [isA()],
+ );
+ });
+
+ blocTest(
+ 'throws if location services are not enabled',
+ build: () => LocationBloc(location: Location()),
+ setUp: () {
+ final mock = MockLocationPlatform();
+
+ const granted = PermissionStatus.granted;
+ when(() => mock.hasPermission()).thenAnswer((_) async => granted);
+ when(() => mock.serviceEnabled()).thenAnswer((_) async => false);
+ when(() => mock.requestService()).thenAnswer((_) async => false);
+
+ LocationPlatform.instance = mock;
+ },
+ act: (bloc) => bloc.add(const RequestLocationServiceEvent()),
+ errors: () => [isA()],
+ );
+
+ blocTest(
+ 'loads with mock latLng',
+ build: () => LocationBloc(location: Location()),
+ setUp: () {
+ final mock = MockLocationPlatform();
+ when(() => mock.serviceEnabled()).thenAnswer((_) async => true);
+ when(() => mock.hasPermission()).thenAnswer(
+ (_) async => PermissionStatus.granted,
+ );
+
+ when(() => mock.getLocation()).thenAnswer(
+ (invocation) async => LocationData.fromMap({
+ 'latitude': mockLat,
+ 'longitude': mockLng,
+ 'accuracy': 1.0,
+ }),
+ );
+
+ LocationPlatform.instance = mock;
+ },
+ act: (bloc) => bloc.add(const LoadLocationEvent()),
+ expect: () => [
+ isA().having(
+ (state) => state.loading,
+ 'will enter loading state',
+ true,
+ ),
+ isA().having(
+ (state) => state.hasPermissions,
+ 'has permission granted',
+ true,
+ ),
+ isA().having(
+ (state) => state.serviceEnabled,
+ 'has services enabled',
+ true,
+ ),
+ isA()
+ .having((state) => state.latitude, 'will fetch mocked lat', mockLat)
+ .having((state) => state.longitude, 'will fetch mocked lng', mockLng),
+ isA().having(
+ (state) => state.loading,
+ 'will enter loading state',
+ false,
+ ),
+ ],
+ );
+}
diff --git a/vehicle-tracker/packages/digit_components/test/digit_ui_components_test.dart b/vehicle-tracker/packages/digit_components/test/digit_ui_components_test.dart
new file mode 100644
index 00000000..ab73b3a2
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/digit_ui_components_test.dart
@@ -0,0 +1 @@
+void main() {}
diff --git a/vehicle-tracker/packages/digit_components/test/theme/theme_test.dart b/vehicle-tracker/packages/digit_components/test/theme/theme_test.dart
new file mode 100644
index 00000000..7b0b08f3
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/theme/theme_test.dart
@@ -0,0 +1,150 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+ DigitUi.instance.initThemeComponents();
+
+ group('Digit theme', () {
+ group('Typography', () {
+ final digitTheme = DigitTheme.instance;
+
+ final widget = MaterialApp(
+ theme: digitTheme.mobileTheme,
+ home: Scaffold(
+ body: Builder(builder: (context) {
+ final theme = Theme.of(context).textTheme;
+ return Column(
+ children: [
+ Text('Headline XL', style: theme.displayMedium),
+ Text('Headline L', style: theme.headlineLarge),
+ Text('Headline M', style: theme.headlineMedium),
+ Text('Headline S', style: theme.headlineSmall),
+ Text('Caption XL', style: theme.labelLarge),
+ Text('Caption L', style: theme.labelMedium),
+ Text('Caption M', style: theme.labelSmall),
+ Text('Body L', style: theme.bodyLarge),
+ Text('Body S', style: theme.bodyMedium),
+ Text('Body XS', style: theme.bodySmall),
+ Text('Label', style: digitTheme.mobileTypography.label),
+ Text('Link', style: digitTheme.mobileTypography.link),
+ ],
+ );
+ }),
+ ),
+ );
+
+ const normal = Color(0xFF0B0C0C);
+ const light = Color(0xFF505A5F);
+
+ const designSpecifications = [
+ _DesignSpecification(
+ title: 'Headline XL',
+ fontSize: 32,
+ color: normal,
+ fontWeight: FontWeight.w700,
+ ),
+ _DesignSpecification(
+ title: 'Headline L',
+ fontSize: 24,
+ color: normal,
+ fontWeight: FontWeight.w700,
+ ),
+ _DesignSpecification(
+ title: 'Headline M',
+ fontSize: 18,
+ color: normal,
+ fontWeight: FontWeight.w700,
+ ),
+ _DesignSpecification(
+ title: 'Body L',
+ fontSize: 16,
+ color: normal,
+ fontWeight: FontWeight.w400,
+ ),
+ _DesignSpecification(
+ title: 'Body S',
+ fontSize: 14,
+ color: normal,
+ fontWeight: FontWeight.w400,
+ ),
+ _DesignSpecification(
+ title: 'Caption XL',
+ fontSize: 19,
+ color: normal,
+ fontWeight: FontWeight.w500,
+ ),
+ _DesignSpecification(
+ title: 'Caption L',
+ fontSize: 18,
+ color: light,
+ fontWeight: FontWeight.w400,
+ ),
+ _DesignSpecification(
+ title: 'Caption M',
+ fontSize: 16,
+ color: light,
+ fontWeight: FontWeight.w400,
+ ),
+ _DesignSpecification(
+ title: 'Link',
+ fontSize: 16,
+ color: normal,
+ fontWeight: FontWeight.w400,
+ ),
+ _DesignSpecification(
+ title: 'Label',
+ fontSize: 16,
+ color: normal,
+ fontWeight: FontWeight.w400,
+ ),
+ ];
+
+ for (final element in designSpecifications) {
+ group(element.title, () {
+ testWidgets('matches title', (tester) async {
+ await tester.pumpWidget(widget);
+ final child = find.text(element.title);
+ expect(child, findsOneWidget);
+ });
+
+ testWidgets('matches font size', (tester) async {
+ await tester.pumpWidget(widget);
+ final child = find.text(element.title);
+ final textWidget = tester.firstWidget(child) as Text;
+ expect(textWidget.style?.fontSize, element.fontSize);
+ });
+
+ testWidgets('matches font weight', (tester) async {
+ await tester.pumpWidget(widget);
+ final child = find.text(element.title);
+ final textWidget = tester.firstWidget(child) as Text;
+ expect(textWidget.style?.fontWeight, element.fontWeight);
+ });
+
+ testWidgets('matches font color', (tester) async {
+ await tester.pumpWidget(widget);
+ final child = find.text(element.title);
+ final textWidget = tester.firstWidget(child) as Text;
+ expect(textWidget.style?.color, element.color);
+ });
+ });
+ }
+ });
+ });
+}
+
+class _DesignSpecification {
+ final String title;
+ final double fontSize;
+ final FontWeight fontWeight;
+ final Color color;
+
+ const _DesignSpecification({
+ required this.title,
+ required this.fontSize,
+ required this.color,
+ required this.fontWeight,
+ });
+}
diff --git a/vehicle-tracker/packages/digit_components/test/widgets/atoms/digit_table_item_test.dart b/vehicle-tracker/packages/digit_components/test/widgets/atoms/digit_table_item_test.dart
new file mode 100644
index 00000000..ef0ac543
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/widgets/atoms/digit_table_item_test.dart
@@ -0,0 +1,30 @@
+import 'package:digit_components/widgets/atoms/digit_table_item.dart';
+import 'package:flutter_test/flutter_test.dart';
+import '../widget_app.dart';
+
+void main() {
+ group(
+ 'DigitTableItem Test',
+ () {
+ testWidgets(
+ 'Widget is created without errors',
+ (widgetTester) async {
+ await widgetTester.pumpWidget(
+ const WidgetApp(
+ child: DigitTableItem(
+ content: 'Male',
+ index: 0,
+ isColored: true,
+ width: 140,
+ ),
+ ),
+ );
+ expect(
+ find.widgetWithText(DigitTableItem, 'Male'),
+ findsOneWidget,
+ );
+ },
+ );
+ },
+ );
+}
diff --git a/vehicle-tracker/packages/digit_components/test/widgets/atoms/digit_table_item_titile.dart b/vehicle-tracker/packages/digit_components/test/widgets/atoms/digit_table_item_titile.dart
new file mode 100644
index 00000000..a4a0e286
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/widgets/atoms/digit_table_item_titile.dart
@@ -0,0 +1,27 @@
+import 'package:digit_components/widgets/atoms/digit_table_item_title.dart';
+import 'package:flutter_test/flutter_test.dart';
+import '../widget_app.dart';
+
+void main() {
+ group(
+ 'DigitTableItemTitle Test',
+ () {
+ testWidgets(
+ 'Widget is created without errors',
+ (widgetTester) async {
+ await widgetTester.pumpWidget(
+ const WidgetApp(
+ child: DigitTableItemTitle(
+ label: 'Title',
+ ),
+ ),
+ );
+ expect(
+ find.widgetWithText(DigitTableItemTitle, 'Title'),
+ findsOneWidget,
+ );
+ },
+ );
+ },
+ );
+}
diff --git a/vehicle-tracker/packages/digit_components/test/widgets/digit_dialog_test.dart b/vehicle-tracker/packages/digit_components/test/widgets/digit_dialog_test.dart
new file mode 100644
index 00000000..19e0964c
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/widgets/digit_dialog_test.dart
@@ -0,0 +1,164 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mocktail/mocktail.dart';
+
+import 'widget_app.dart';
+
+class MockNavigatorObserver extends Mock implements NavigatorObserver {}
+
+class FakeRoute extends Fake implements Route {}
+
+class FakeDialogRoute extends Fake implements DialogRoute {}
+
+void main() {
+ group('Digit Dialog', () {
+ const testButton = 'Test Button';
+ const alertTitle = 'Test title';
+ const content = 'Dialog content';
+ const primaryActionLabel = 'Primary';
+ const secondaryActionLabel = 'Secondary';
+
+ Future buildTester(
+ WidgetTester widgetTester, {
+ MockNavigatorObserver? mockObserver,
+ void Function(BuildContext context)? primaryAction,
+ void Function(BuildContext context)? secondaryAction,
+ }) async {
+ await widgetTester.pumpWidget(
+ WidgetApp(
+ navigatorObserver: mockObserver,
+ child: Builder(
+ builder: (context) => ElevatedButton(
+ child: const Text(testButton),
+ onPressed: () => showDialog(
+ context: context,
+ builder: (_) => DigitDialog(
+ options: DigitDialogOptions(
+ title: const Text(alertTitle),
+ content: const Text(content),
+ primaryAction: DigitDialogActions(
+ label: primaryActionLabel,
+ action: primaryAction,
+ ),
+ secondaryAction: DigitDialogActions(
+ label: secondaryActionLabel,
+ action: secondaryAction,
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+
+ await widgetTester.tap(find.widgetWithText(ElevatedButton, testButton));
+ await widgetTester.pumpAndSettle();
+ }
+
+ setUpAll(() {
+ registerFallbackValue(FakeRoute());
+ registerFallbackValue(FakeDialogRoute());
+ registerFallbackValue(FakeDialogRoute());
+ });
+
+ testWidgets('is pushed on router', (widgetTester) async {
+ final mockObserver = MockNavigatorObserver();
+
+ await buildTester(widgetTester, mockObserver: mockObserver);
+
+ verify(() => mockObserver.didPush(any(), any()));
+ });
+
+ testWidgets('executes primary action', (widgetTester) async {
+ bool primary = false;
+
+ await buildTester(
+ widgetTester,
+ primaryAction: (context) => primary = !primary,
+ );
+
+ await widgetTester.tap(
+ find.widgetWithText(DigitElevatedButton, primaryActionLabel),
+ );
+
+ expect(primary, true);
+ });
+
+ testWidgets('executes secondary action', (widgetTester) async {
+ bool secondary = false;
+
+ await buildTester(
+ widgetTester,
+ secondaryAction: (context) => secondary = !secondary,
+ );
+
+ await widgetTester.tap(
+ find.widgetWithText(TextButton, secondaryActionLabel),
+ );
+
+ expect(secondary, true);
+ });
+
+ testWidgets('has UI components', (widgetTester) async {
+ await buildTester(
+ widgetTester,
+ primaryAction: (context) {},
+ secondaryAction: (context) {},
+ );
+
+ expect(find.text(alertTitle), findsOneWidget);
+ expect(find.text(content), findsOneWidget);
+
+ expect(
+ find.widgetWithText(DigitElevatedButton, primaryActionLabel),
+ findsOneWidget,
+ );
+ expect(
+ find.widgetWithText(TextButton, secondaryActionLabel),
+ findsOneWidget,
+ );
+ });
+
+ testWidgets('context builder renders dialog', (widgetTester) async {
+ final mockObserver = MockNavigatorObserver();
+ bool primary = false;
+
+ await widgetTester.pumpWidget(
+ WidgetApp(
+ navigatorObserver: mockObserver,
+ child: Builder(
+ builder: (context) => ElevatedButton(
+ onPressed: () => DigitDialog.show(
+ context,
+ options: DigitDialogOptions(
+ titleText: alertTitle,
+ contentText: content,
+ primaryAction: DigitDialogActions(
+ label: primaryActionLabel,
+ action: (context) => Navigator.of(context).pop(!primary),
+ ),
+ ),
+ ).then((value) {
+ if (value == null) return;
+ primary = value;
+ }),
+ child: const Text(testButton),
+ ),
+ ),
+ ),
+ );
+
+ await widgetTester.tap(find.widgetWithText(ElevatedButton, testButton));
+ await widgetTester.pumpAndSettle();
+
+ verify(() => mockObserver.didPush(any>(), any()));
+
+ await widgetTester.tap(
+ find.widgetWithText(DigitElevatedButton, primaryActionLabel),
+ );
+ expect(primary, true);
+ });
+ });
+}
diff --git a/vehicle-tracker/packages/digit_components/test/widgets/digit_info_card_test.dart b/vehicle-tracker/packages/digit_components/test/widgets/digit_info_card_test.dart
new file mode 100644
index 00000000..188c66c4
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/widgets/digit_info_card_test.dart
@@ -0,0 +1,34 @@
+import 'package:digit_components/widgets/digit_info_card.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'widget_app.dart';
+
+void main() {
+ group(
+ 'DigitInfoCard Test',
+ () {
+ testWidgets(
+ 'Widget is created without errors',
+ (widgetTester) async {
+ await widgetTester.pumpWidget(
+ const WidgetApp(
+ child: DigitInfoCard(
+ icon: Icons.info,
+ backgroundColor: Colors.white,
+ iconColor: Colors.black,
+ description:
+ 'Click on Register New Household button to add details.',
+ title: 'Match not found!',
+ ),
+ ),
+ );
+ expect(
+ find.widgetWithText(DigitInfoCard, 'Match not found!'),
+ findsOneWidget,
+ );
+ },
+ );
+ },
+ );
+}
diff --git a/vehicle-tracker/packages/digit_components/test/widgets/digit_outline_button_test.dart b/vehicle-tracker/packages/digit_components/test/widgets/digit_outline_button_test.dart
new file mode 100644
index 00000000..4396c3d0
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/widgets/digit_outline_button_test.dart
@@ -0,0 +1,35 @@
+import 'package:digit_components/widgets/digit_outline_button.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'widget_app.dart';
+
+void main() {
+ group(
+ 'DigitOutLineButton Test',
+ () {
+ testWidgets(
+ 'Widget is created without errors',
+ (widgetTester) async {
+ bool primary = false;
+ await widgetTester.pumpWidget(
+ WidgetApp(
+ child: DigitOutLineButton(
+ label: 'Button',
+ onPressed: () {
+ primary = !primary;
+ }),
+ ),
+ );
+ expect(
+ find.widgetWithText(DigitOutLineButton, 'Button'),
+ findsOneWidget,
+ );
+ await widgetTester
+ .tap(find.widgetWithText(DigitOutLineButton, 'Button'));
+ await widgetTester.pumpAndSettle();
+ expect(primary, true);
+ },
+ );
+ },
+ );
+}
diff --git a/vehicle-tracker/packages/digit_components/test/widgets/digit_search_bar_test.dart b/vehicle-tracker/packages/digit_components/test/widgets/digit_search_bar_test.dart
new file mode 100644
index 00000000..650a5e01
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/widgets/digit_search_bar_test.dart
@@ -0,0 +1,29 @@
+import 'package:digit_components/widgets/digit_search_bar.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'widget_app.dart';
+
+void main() {
+ group(
+ 'DigitSearchBar Test',
+ () {
+ testWidgets(
+ 'Widget is created without errors',
+ (widgetTester) async {
+ await widgetTester.pumpWidget(
+ const WidgetApp(
+ child: DigitSearchBar(
+ hintText: 'Enter the name of household head',
+ ),
+ ),
+ );
+ expect(
+ find.widgetWithText(
+ DigitSearchBar, 'Enter the name of household head'),
+ findsOneWidget,
+ );
+ },
+ );
+ },
+ );
+}
diff --git a/vehicle-tracker/packages/digit_components/test/widgets/digit_sync_dialog.dart b/vehicle-tracker/packages/digit_components/test/widgets/digit_sync_dialog.dart
new file mode 100644
index 00000000..1a09e80b
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/widgets/digit_sync_dialog.dart
@@ -0,0 +1,191 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:digit_components/widgets/digit_sync_dialog.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mocktail/mocktail.dart';
+
+import 'widget_app.dart';
+
+class MockNavigatorObserver extends Mock implements NavigatorObserver {}
+
+class FakeRoute extends Fake implements Route {}
+
+class FakeDialogRoute extends Fake implements DialogRoute {}
+
+void main() {
+ group('Digit Sync Dialog - InProgress', () {
+ const syncInProgressTitleText = 'Sync In Progress';
+ const testButton = 'Test Button';
+
+ Future buildTester(
+ WidgetTester widgetTester, {
+ MockNavigatorObserver? mockObserver,
+ }) async {
+ await widgetTester.pumpWidget(
+ WidgetApp(
+ navigatorObserver: mockObserver,
+ child: Builder(
+ builder: (context) => ElevatedButton(
+ child: const Text(testButton),
+ onPressed: () {
+ DigitSyncDialog.show(
+ context,
+ type: DigitSyncDialogType.inProgress,
+ label: syncInProgressTitleText,
+ );
+ },
+ ),
+ ),
+ ),
+ );
+
+ await widgetTester.tap(find.widgetWithText(ElevatedButton, testButton));
+ await widgetTester.pumpAndSettle();
+ }
+
+ setUpAll(() {
+ registerFallbackValue(FakeRoute());
+ registerFallbackValue(FakeDialogRoute());
+ registerFallbackValue(FakeDialogRoute());
+ });
+
+ testWidgets('is pushed on router', (widgetTester) async {
+ final mockObserver = MockNavigatorObserver();
+
+ await buildTester(widgetTester, mockObserver: mockObserver);
+
+ verify(() => mockObserver.didPush(any(), any()));
+ });
+
+ testWidgets('has UI components', (widgetTester) async {
+ await buildTester(
+ widgetTester,
+ );
+ expect(find.text(syncInProgressTitleText), findsOneWidget);
+ expect(find.byIcon(Icons.autorenew), findsOneWidget);
+ });
+ });
+
+ group('Digit Sync Dialog - SyncComplete', () {
+ const syncCompleteTitleText = 'Sync Completed';
+ const testButton = 'Test Button';
+ const syncCompleteButtonText = 'Close';
+ Future buildTester(
+ WidgetTester widgetTester, {
+ MockNavigatorObserver? mockObserver,
+ }) async {
+ await widgetTester.pumpWidget(WidgetApp(
+ navigatorObserver: mockObserver,
+ child: Builder(
+ builder: (context) => ElevatedButton(
+ child: const Text(testButton),
+ onPressed: () {
+ DigitSyncDialog.show(
+ context,
+ type: DigitSyncDialogType.complete,
+ label: syncCompleteTitleText,
+ primaryAction: DigitDialogActions(
+ label: syncCompleteButtonText,
+ action: (_) {},
+ ),
+ );
+ },
+ )),
+ ));
+
+ await widgetTester.tap(find.widgetWithText(ElevatedButton, testButton));
+ await widgetTester.pumpAndSettle();
+ }
+
+ setUpAll(() {
+ registerFallbackValue(FakeRoute());
+ registerFallbackValue(FakeDialogRoute());
+ registerFallbackValue(FakeDialogRoute());
+ });
+
+ testWidgets('is pushed on router', (widgetTester) async {
+ final mockObserver = MockNavigatorObserver();
+
+ await buildTester(widgetTester, mockObserver: mockObserver);
+
+ verify(() => mockObserver.didPush(any(), any()));
+ });
+
+ testWidgets('has required UI components', (widgetTester) async {
+ await buildTester(
+ widgetTester,
+ );
+ expect(find.text(syncCompleteTitleText), findsOneWidget);
+ expect(find.byIcon(Icons.check_circle_outline), findsOneWidget);
+ expect(find.byType(DigitElevatedButton), findsOneWidget);
+ expect(find.text(syncCompleteButtonText), findsOneWidget);
+ });
+ });
+
+ group('Digit Sync Dialog - SyncFailed', () {
+ const syncFailedTitleText = 'Sync Failed';
+ const retryButtonText = 'Retry';
+ const dismissButtonText = 'Dismiss';
+ const testButton = 'Test Button';
+
+ Future buildTester(
+ WidgetTester widgetTester, {
+ MockNavigatorObserver? mockObserver,
+ }) async {
+ await widgetTester.pumpWidget(
+ WidgetApp(
+ navigatorObserver: mockObserver,
+ child: Builder(
+ builder: (context) => ElevatedButton(
+ child: const Text(testButton),
+ onPressed: () {
+ DigitSyncDialog.show(
+ context,
+ type: DigitSyncDialogType.failed,
+ label: syncFailedTitleText,
+ primaryAction: DigitDialogActions(
+ label: retryButtonText,
+ action: (_) {},
+ ),
+ secondaryAction: DigitDialogActions(
+ label: dismissButtonText,
+ action: (_) {},
+ ),
+ );
+ },
+ ),
+ ),
+ ),
+ );
+
+ await widgetTester.tap(find.widgetWithText(ElevatedButton, testButton));
+ await widgetTester.pumpAndSettle();
+ }
+
+ setUpAll(() {
+ registerFallbackValue(FakeRoute());
+ registerFallbackValue(FakeDialogRoute());
+ registerFallbackValue(FakeDialogRoute());
+ });
+
+ testWidgets('is pushed on router', (widgetTester) async {
+ final mockObserver = MockNavigatorObserver();
+
+ await buildTester(widgetTester, mockObserver: mockObserver);
+
+ verify(() => mockObserver.didPush(any(), any()));
+ });
+
+ testWidgets('has required UI components', (widgetTester) async {
+ await buildTester(
+ widgetTester,
+ );
+ expect(find.text(syncFailedTitleText), findsOneWidget);
+ expect(find.byIcon(Icons.error_outline), findsOneWidget);
+ expect(find.byType(DigitElevatedButton), findsOneWidget);
+ expect(find.text(retryButtonText), findsOneWidget);
+ expect(find.byType(TextButton), findsOneWidget);
+ expect(find.text(dismissButtonText), findsOneWidget);
+ });
+ });
+}
diff --git a/vehicle-tracker/packages/digit_components/test/widgets/molecules/digit_acknowledgement_test.dart b/vehicle-tracker/packages/digit_components/test/widgets/molecules/digit_acknowledgement_test.dart
new file mode 100644
index 00000000..f33c9f8c
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/widgets/molecules/digit_acknowledgement_test.dart
@@ -0,0 +1,101 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import '../widget_app.dart';
+
+void main() {
+ group(
+ 'DigitAcknowledgement Test',
+ () {
+ late bool actionTriggered;
+
+ setUp(() {
+ actionTriggered = false;
+ });
+
+ testWidgets(
+ 'Success Acknowledgement is created without errors',
+ (widgetTester) async {
+ await widgetTester.pumpWidget(
+ WidgetApp(
+ child: DigitAcknowledgement.success(
+ action: () => actionTriggered = !actionTriggered,
+ actionLabel: 'DIGIT_ACKNOWLEDGEMENT_ACTION_LABEL',
+ description: 'DIGIT_ACKNOWLEDGEMENT_DESCRIPTION',
+ label: 'DIGIT_ACKNOWLEDGEMENT_LABEL',
+ ),
+ ),
+ );
+
+ expect(
+ find.widgetWithText(
+ DigitAcknowledgement,
+ 'DIGIT_ACKNOWLEDGEMENT_LABEL',
+ ),
+ findsOneWidget,
+ );
+
+ expect(
+ find.widgetWithIcon(
+ DigitAcknowledgement,
+ Icons.check_circle,
+ ),
+ findsOneWidget,
+ );
+
+ expect(actionTriggered, false);
+ await widgetTester.tap(
+ find.widgetWithText(
+ DigitElevatedButton,
+ 'DIGIT_ACKNOWLEDGEMENT_ACTION_LABEL',
+ ),
+ );
+
+ expect(actionTriggered, true);
+ },
+ );
+
+ testWidgets(
+ 'Error Acknowledgement is created without errors',
+ (widgetTester) async {
+ await widgetTester.pumpWidget(
+ WidgetApp(
+ child: DigitAcknowledgement.error(
+ action: () => actionTriggered = !actionTriggered,
+ actionLabel: 'DIGIT_ACKNOWLEDGEMENT_ACTION_LABEL',
+ description: 'DIGIT_ACKNOWLEDGEMENT_DESCRIPTION',
+ label: 'DIGIT_ACKNOWLEDGEMENT_LABEL',
+ ),
+ ),
+ );
+
+ expect(
+ find.widgetWithText(
+ DigitAcknowledgement,
+ 'DIGIT_ACKNOWLEDGEMENT_LABEL',
+ ),
+ findsOneWidget,
+ );
+
+ expect(
+ find.widgetWithIcon(
+ DigitAcknowledgement,
+ Icons.error,
+ ),
+ findsOneWidget,
+ );
+
+ expect(actionTriggered, false);
+ await widgetTester.tap(
+ find.widgetWithText(
+ DigitElevatedButton,
+ 'DIGIT_ACKNOWLEDGEMENT_ACTION_LABEL',
+ ),
+ );
+
+ expect(actionTriggered, true);
+ },
+ );
+ },
+ );
+}
diff --git a/vehicle-tracker/packages/digit_components/test/widgets/molecules/digit_language_card_test.dart b/vehicle-tracker/packages/digit_components/test/widgets/molecules/digit_language_card_test.dart
new file mode 100644
index 00000000..39c9b722
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/widgets/molecules/digit_language_card_test.dart
@@ -0,0 +1,77 @@
+import 'dart:convert';
+import 'package:digit_components/digit_components.dart';
+import 'package:digit_components/models/digit_row_card/digit_row_card_model.dart';
+import 'package:digit_components/widgets/molecules/digit_language_card.dart';
+import 'package:flutter_test/flutter_test.dart';
+import '../widget_app.dart';
+
+const _target = """
+ [
+ {
+ "label": "ਪੰਜਾਬੀ",
+ "value": "pn_IN",
+ "isSelected": false
+ },
+ {
+ "label": "ENGLISH",
+ "value": "en_IN",
+ "isSelected": true
+ }
+ ]
+ """;
+void main() {
+ bool languageChangeTriggered = false;
+ bool languageChangeSubmitTriggered = false;
+
+ final List list = json.decode(_target);
+ List languageList =
+ list.map((e) => DigitRowCardModel.fromJson(e)).toList();
+ group('Digit Language Card ', () {
+ testWidgets('Widget is created without errors', (widgetTester) async {
+ await widgetTester.pumpWidget(
+ WidgetApp(
+ child: DigitLanguageCard(
+ digitRowCardItems: languageList,
+ languageSubmitLabel: 'Continue',
+ onLanguageSubmit: () => languageChangeSubmitTriggered =
+ !languageChangeSubmitTriggered,
+ onLanguageChange: (data) {
+ languageChangeTriggered = !languageChangeTriggered;
+ }),
+ ),
+ );
+
+ expect(
+ find.widgetWithText(
+ DigitLanguageCard,
+ 'Continue',
+ ),
+ findsOneWidget,
+ );
+
+// Test case for LanguageChange Button
+ await widgetTester.tap(
+ find.text(
+ 'ENGLISH',
+ ),
+ );
+
+ await widgetTester.pumpAndSettle();
+ expect(
+ languageChangeTriggered,
+ true,
+ );
+
+// Test case for Submit Button
+ await widgetTester.tap(
+ find.text('Continue'),
+ );
+
+ await widgetTester.pumpAndSettle();
+ expect(
+ languageChangeSubmitTriggered,
+ true,
+ );
+ });
+ });
+}
diff --git a/vehicle-tracker/packages/digit_components/test/widgets/molecules/digit_row_card_test.dart b/vehicle-tracker/packages/digit_components/test/widgets/molecules/digit_row_card_test.dart
new file mode 100644
index 00000000..1e296f24
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/widgets/molecules/digit_row_card_test.dart
@@ -0,0 +1,69 @@
+import 'dart:convert';
+import 'package:digit_components/models/digit_row_card/digit_row_card_model.dart';
+import 'package:digit_components/widgets/digit_row_card/digit_row_card.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import '../widget_app.dart';
+
+const _target = """
+ [
+ {
+ "label": "ਪੰਜਾਬੀ",
+ "value": "pn_IN",
+ "isSelected": false
+ },
+ {
+ "label": "ENGLISH",
+ "value": "en_IN",
+ "isSelected": true
+ }
+ ]
+ """;
+void main() {
+ group('Digit Row card widget', () {
+ final List list = json.decode(_target);
+ List languageList =
+ list.map((e) => DigitRowCardModel.fromJson(e)).toList();
+ testWidgets('Widget is created without errors', (widgetTester) async {
+ bool primary = false;
+
+ onPressed(data) {
+ primary = !primary;
+ }
+
+ await widgetTester.pumpWidget(WidgetApp(
+ child: DigitRowCard(
+ rowItems: languageList,
+ onChanged: (data) => onPressed(data),
+ width: 90,
+ )));
+
+ expect(
+ find.widgetWithText(DigitRowCard, 'ENGLISH'),
+ findsOneWidget,
+ );
+ });
+
+ testWidgets('DigitRowcard widget OnTap', (widgetTester) async {
+ bool primary = false;
+
+ onPressed(data) {
+ primary = !primary;
+ }
+
+ await widgetTester.pumpWidget(WidgetApp(
+ child: DigitRowCard(
+ rowItems: languageList,
+ onChanged: (data) => onPressed(data),
+ width: 90,
+ )));
+
+ await widgetTester.pumpAndSettle();
+ await widgetTester.tap(find.widgetWithText(Center, 'ENGLISH'));
+ await widgetTester.pumpAndSettle();
+ expect(primary, true);
+ });
+ });
+}
diff --git a/vehicle-tracker/packages/digit_components/test/widgets/powered_by_digit_test.dart b/vehicle-tracker/packages/digit_components/test/widgets/powered_by_digit_test.dart
new file mode 100644
index 00000000..8328addf
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/widgets/powered_by_digit_test.dart
@@ -0,0 +1,24 @@
+import 'package:digit_components/digit_components.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'widget_app.dart';
+
+void main() {
+ group('Powered by Digit widget', () {
+ testWidgets('Widget is created without errors', (widgetTester) async {
+ await widgetTester.pumpWidget(const WidgetApp(child: PoweredByDigit()));
+
+ expect(
+ find.widgetWithImage(
+ PoweredByDigit,
+ const AssetImage(
+ 'assets/images/powered_by_digit.png',
+ package: 'digit_components',
+ ),
+ ),
+ findsOneWidget,
+ );
+ });
+ });
+}
diff --git a/vehicle-tracker/packages/digit_components/test/widgets/widget_app.dart b/vehicle-tracker/packages/digit_components/test/widgets/widget_app.dart
new file mode 100644
index 00000000..58d6107a
--- /dev/null
+++ b/vehicle-tracker/packages/digit_components/test/widgets/widget_app.dart
@@ -0,0 +1,18 @@
+import 'package:flutter/material.dart';
+
+class WidgetApp extends StatelessWidget {
+ final Widget child;
+ final NavigatorObserver? navigatorObserver;
+
+ const WidgetApp({
+ super.key,
+ required this.child,
+ this.navigatorObserver,
+ });
+
+ @override
+ Widget build(BuildContext context) => MaterialApp(
+ navigatorObservers: [if (navigatorObserver != null) navigatorObserver!],
+ home: Scaffold(body: child),
+ );
+}
diff --git a/vehicle-tracker/vehicle_tracker_app/android/app/build.gradle b/vehicle-tracker/vehicle_tracker_app/android/app/build.gradle
index b965d21f..afbe308b 100644
--- a/vehicle-tracker/vehicle_tracker_app/android/app/build.gradle
+++ b/vehicle-tracker/vehicle_tracker_app/android/app/build.gradle
@@ -48,7 +48,7 @@ android {
applicationId "com.example.vehicle_tracker_app"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
- minSdkVersion flutter.minSdkVersion
+ minSdkVersion 18
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
diff --git a/vehicle-tracker/vehicle_tracker_app/assets/images/not_found.svg b/vehicle-tracker/vehicle_tracker_app/assets/images/not_found.svg
new file mode 100644
index 00000000..ba16372c
--- /dev/null
+++ b/vehicle-tracker/vehicle_tracker_app/assets/images/not_found.svg
@@ -0,0 +1,101 @@
+
diff --git a/vehicle-tracker/vehicle_tracker_app/lib/blocs/home/controllers/info_controllers.dart b/vehicle-tracker/vehicle_tracker_app/lib/blocs/home/controllers/info_controllers.dart
index 26281061..1c5f8484 100644
--- a/vehicle-tracker/vehicle_tracker_app/lib/blocs/home/controllers/info_controllers.dart
+++ b/vehicle-tracker/vehicle_tracker_app/lib/blocs/home/controllers/info_controllers.dart
@@ -7,6 +7,7 @@ import 'package:vehicle_tracker_app/blocs/home/repository/home_http_repository.d
import 'package:vehicle_tracker_app/constants.dart';
import 'package:vehicle_tracker_app/data/secure_storage_service.dart';
import 'package:vehicle_tracker_app/models/home_trip/home_trip_model/home_trip_model.dart';
+import 'package:vehicle_tracker_app/router/routes.dart';
import 'package:vehicle_tracker_app/util/logout.dart';
class InfoController extends GetxController {
@@ -37,8 +38,8 @@ class InfoController extends GetxController {
logout();
return;
}
-
- await fillList(tenantId, operatorId);
+ final selectedCity= await SecureStorageService.read(CITYCODE);
+ await fillList(selectedCity!, operatorId);
}
// ? It will get the trip data and filter the data based on the status.
@@ -47,7 +48,7 @@ class InfoController extends GetxController {
isLoading.toggle();
// ! For now we are hardcoding the city id as "pg.citya"
- final totalList = await homeHTTPRepository.getHomeTripData("pg.citya", operatorId);
+ final totalList = await homeHTTPRepository.getHomeTripData(tentantId, operatorId);
normalTripList.value = totalList.where((element) {
return element.value.status != TripStates.COMPLETED;
@@ -114,4 +115,4 @@ class InfoController extends GetxController {
}).toList();
}
}
-}
+}
\ No newline at end of file
diff --git a/vehicle-tracker/vehicle_tracker_app/lib/blocs/home/controllers/trip_tracker_controllers.dart b/vehicle-tracker/vehicle_tracker_app/lib/blocs/home/controllers/trip_tracker_controllers.dart
index 6d63cc4b..b6350e70 100644
--- a/vehicle-tracker/vehicle_tracker_app/lib/blocs/home/controllers/trip_tracker_controllers.dart
+++ b/vehicle-tracker/vehicle_tracker_app/lib/blocs/home/controllers/trip_tracker_controllers.dart
@@ -19,48 +19,94 @@ import 'package:vehicle_tracker_app/util/trip_tracker_utility.dart';
import 'package:wakelock/wakelock.dart';
class TripControllers extends GetxController {
- final BuildContext context;
+ static final TripControllers _singleton = TripControllers._internal();
- RxBool isRunning = false.obs; // This variable is to check if the tracking is running or not
- RxBool isLoading = false.obs; // This variable is to check if startTracking is loading or not
+ factory TripControllers() {
+ return _singleton;
+ }
+
+ TripControllers._internal();
+
+ RxBool isRunning =
+ false.obs; // This variable is to check if the tracking is running or not
+ RxBool isLoading =
+ false.obs; // This variable is to check if startTracking is loading or not
HomeHTTPRepository homeHTTPRepository = HomeHTTPRepository();
HomeHiveRepository homeHiveRepository = HomeHiveRepository();
TripTrackerUtility tripTrackerUtility = TripTrackerUtility();
InfoController infoController = Get.find();
- TripControllers(this.context);
-
// * This function starts the tracking peroiodic event
Future startTracking(Rx data) async {
log('---- Trip and Tracking started ----');
log("Start TRIP API");
- final status = await homeHTTPRepository.updateTrip(data.value, TripStates.ONGOING);
+ final status =
+ await homeHTTPRepository.updateTrip(data.value, TripStates.ONGOING);
if (!status) {
- toaster(null, AppTranslation.TRIP_NOT_STARTED_MESSAGE.tr, isError: true);
+ toaster(AppTranslation.TRIP_NOT_STARTED_MESSAGE.tr, isError: true);
data.value.status = TripStates.NOTSTARTED;
update([data.value.id]);
return;
}
- toaster(null, AppTranslation.TRIP_STARTED_SUCCESFULLY_MESSAGE.tr, isError: false);
+ toaster(AppTranslation.TRIP_STARTED_SUCCESFULLY_MESSAGE.tr, isError: false);
data.value.status = TripStates.ONGOING;
update([data.value.id]);
// Calls the periodic function
startPeriodicFunction(data.value);
+
+ // Start tracking immediately
+ trackerLogic("In Progress", data.value);
+ }
+
+ //send the positions stored in local storage to the server
+ Future sendStoredPositions(HomeTripModel trip) async {
+ log("Sending stored positions to server");
+ final isConnected = await tripTrackerUtility.isConnected();
+ if (!isConnected) {
+ log("No internet connection");
+ return;
+ }
+
+ await trackerLogic('Completed', trip);
+
+ final List positions = homeHiveRepository.getTripData();
+ if (positions.isEmpty) {
+ log("No positions to send");
+ return;
+ } else {
+ log("Sending positions to server");
+ final status =
+ await homeHTTPRepository.updateTripProgress(trip, positions);
+ if (status) {
+ log("Positions sent successfully");
+ await homeHiveRepository.deleteTripData();
+ toaster(AppTranslation.POSITION_SENT_MESSAGE.tr);
+ } else {
+ log("Error sending positions");
+ toaster(AppTranslation.POSITION_HIVE_STORE_MESSAGE.tr, isError: true);
+ }
+ }
}
// ? This functions starts a timer which will call the trackerLogic function every n seconds
// ? The timer will stop if the isRunning variable is false
// ? The main point on this function is to get and send tracker function like a cron schedule
+ Timer? _timer;
+
void startPeriodicFunction(HomeTripModel trip) {
log('Periodic function started');
isRunning.value = true;
- Timer.periodic(Duration(seconds: periodicTrackingFrequency), (_) async {
+ // Cancel the existing timer if it's running
+ _timer?.cancel();
+
+ _timer =
+ Timer.periodic(Duration(seconds: periodicTrackingFrequency), (_) async {
log("Periodic function called");
if (!isRunning.value) {
log("Periodic function stopped");
@@ -75,7 +121,8 @@ class TripControllers extends GetxController {
// ? If the location permissions are not granted, the function will stop and return early
// ? If the location permissions are granted, the function will get the current location and use it accordingly
Future trackerLogic(String alert, HomeTripModel trip) async {
- bool permissions = await tripTrackerUtility.handleLocationPermission(Get.context);
+ bool permissions =
+ await tripTrackerUtility.handleLocationPermission(Get.context);
if (!permissions) {
isRunning.value = false;
log('Location permissions not granted');
@@ -101,7 +148,8 @@ class TripControllers extends GetxController {
// ? It will check if the device is connected to internet or not
// ? If there is internet connection, it will send the data to server
// ? If by any chance the data sending fails, it will store the data to hive
- Future positionSender(Position? position, String alert, HomeTripModel trip) async {
+ Future positionSender(
+ Position? position, String alert, HomeTripModel trip) async {
TripHiveModel tripHiveModel = TripHiveModel(
latitude: position?.latitude ?? 0,
longitude: position?.longitude ?? 0,
@@ -121,11 +169,12 @@ class TripControllers extends GetxController {
// if success delete the hive data
// else save the data to hive
log("Sending position to server");
- final status = await homeHTTPRepository.updateTripProgress(trip, positions);
+ final status =
+ await homeHTTPRepository.updateTripProgress(trip, positions);
if (status) {
log("Position sent successfully");
await homeHiveRepository.deleteTripData();
- toaster(null, AppTranslation.POSITION_SENT_MESSAGE.tr);
+ toaster(AppTranslation.POSITION_SENT_MESSAGE.tr);
return status;
} else {
// If the position sending fails, save the data to hive
@@ -137,7 +186,7 @@ class TripControllers extends GetxController {
// If not connected to internet, save the data to hive
log("No internet connection, saving to hive");
await homeHiveRepository.storeTripData(tripHiveModel);
- toaster(null, AppTranslation.POSITION_HIVE_STORE_MESSAGE.tr);
+ toaster(AppTranslation.POSITION_HIVE_STORE_MESSAGE.tr);
return false;
}
}
@@ -147,7 +196,6 @@ class TripControllers extends GetxController {
bool spamChecker(BuildContext context) {
if (isLoading.isTrue) {
toaster(
- context,
AppTranslation.START_LOADING_MESSAGE.tr,
isError: true,
);
@@ -186,15 +234,16 @@ class TripControllers extends GetxController {
Get.back();
- final status = await homeHTTPRepository.updateTrip(data.value, TripStates.COMPLETED);
+ final status =
+ await homeHTTPRepository.updateTrip(data.value, TripStates.COMPLETED);
if (!status) {
data.value.status = TripStates.ONGOING;
update([data.value.id]);
- toaster(null, AppTranslation.TRIP_NOT_END_MESSAGE.tr, isError: true);
+ toaster(AppTranslation.TRIP_NOT_END_MESSAGE.tr, isError: true);
return;
}
- toaster(null, AppTranslation.TRIP_ENDED_SUCCESFULLY_MESSAGE.tr, isError: false);
+ toaster(AppTranslation.TRIP_ENDED_SUCCESFULLY_MESSAGE.tr, isError: false);
log("Deleting trip data from hive");
await homeHiveRepository.deleteTripData();
diff --git a/vehicle-tracker/vehicle_tracker_app/lib/blocs/home/repository/home_http_repository.dart b/vehicle-tracker/vehicle_tracker_app/lib/blocs/home/repository/home_http_repository.dart
index 589bc2c2..d507c135 100644
--- a/vehicle-tracker/vehicle_tracker_app/lib/blocs/home/repository/home_http_repository.dart
+++ b/vehicle-tracker/vehicle_tracker_app/lib/blocs/home/repository/home_http_repository.dart
@@ -7,22 +7,26 @@ import 'package:vehicle_tracker_app/models/home_trip/home_trip_model/home_trip_m
import 'package:vehicle_tracker_app/models/trip/trip_tracker_info/trip_tracker_hive_model.dart';
import 'package:vehicle_tracker_app/util/i18n_translations.dart';
import 'package:vehicle_tracker_app/util/toaster.dart';
+import 'package:vehicle_tracker_app/blocs/home/controllers/trip_tracker_controllers.dart'
+ as send_local_data;
import '../../../data/secure_storage_service.dart';
class HomeHTTPRepository {
// ? Uses the userId to get the list of trips.
- Future>> getHomeTripData(String tenantId, String operatorId) async {
+ Future>> getHomeTripData(
+ String tenantId, String operatorId) async {
List> homeTripModel = [];
- String reqUrl = "$apiUrl/trip/_search?operatorId=$operatorId&tenantId=$tenantId";
+ String reqUrl =
+ "$apiUrl/trip/_search?operatorId=$operatorId&tenantId=$tenantId";
final response = await HttpService.getRequest(reqUrl);
if (response.statusCode != 200) {
log("Error Code: ${response.statusCode}");
log("Error: ${response.body}");
- toaster(Get.context, AppTranslation.NETWORK_ERROR_MESSAGE.tr, isError: true);
+ toaster(AppTranslation.NETWORK_ERROR_MESSAGE.tr, isError: true);
return homeTripModel;
}
@@ -32,10 +36,12 @@ class HomeHTTPRepository {
homeTripModel.add(Rx(HomeTripModel.fromJson(item)));
}
} on FormatException catch (e) {
- toaster(Get.context, AppTranslation.NETWORK_ERROR_MESSAGE.tr, isError: true, error: e.message);
+ toaster(AppTranslation.NETWORK_ERROR_MESSAGE.tr,
+ isError: true, error: e.message);
homeTripModel.clear();
} on Exception catch (e) {
- toaster(Get.context, AppTranslation.NETWORK_ERROR_MESSAGE.tr, isError: true, error: e.toString());
+ toaster(AppTranslation.NETWORK_ERROR_MESSAGE.tr,
+ isError: true, error: e.toString());
homeTripModel.clear();
}
@@ -45,7 +51,8 @@ class HomeHTTPRepository {
// ? API to start and end the Trip
// ? If start is true, then the trip will start
// ? If start is false, then the trip will end
- Future updateTrip(HomeTripModel data, String status, {String? tenantId}) async {
+ Future updateTrip(HomeTripModel data, String status,
+ {String? tenantId}) async {
String reqUrl = "$apiUrl/trip/_update";
final operatorId = await SecureStorageService.read(OPERATOR_ID);
@@ -59,6 +66,8 @@ class HomeHTTPRepository {
log("URL: $reqUrl");
log("Body: $body");
+ await send_local_data.TripControllers().sendStoredPositions(data);
+
final response = await HttpService.putRequest(reqUrl, body);
// ? If the response is null OR not 200, then return false.
@@ -74,7 +83,8 @@ class HomeHTTPRepository {
}
// ? API to update the trip progress
- Future updateTripProgress(HomeTripModel data, List positions) async {
+ Future updateTripProgress(
+ HomeTripModel data, List positions) async {
String reqUrl = "$apiUrl/trip/_progress";
final operatorId = await SecureStorageService.read(OPERATOR_ID);
@@ -114,14 +124,16 @@ class HomeHTTPRepository {
}
// ? API to create a new POI
- Future callTrackingApi(List positions, String alert, String tripId) async {
+ Future callTrackingApi(
+ List positions, String alert, String tripId) async {
String reqUrl = "$apiUrl/poi/_create";
final operatorId = await SecureStorageService.read(OPERATOR_ID);
List