summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitreview5
-rw-r--r--LICENSE.md201
-rw-r--r--assets/cert/Client.key27
-rw-r--r--assets/cert/Client.pem22
-rw-r--r--images/arrow.pngbin0 -> 536 bytes
-rw-r--r--images/battery.pngbin0 -> 3583 bytes
-rw-r--r--images/car.pngbin0 -> 53003 bytes
-rw-r--r--images/cruise.pngbin0 -> 12448 bytes
-rw-r--r--images/demo.pngbin0 -> 692590 bytes
-rw-r--r--images/eco.pngbin0 -> 18611 bytes
-rw-r--r--images/engine-coolant.pngbin0 -> 8710 bytes
-rw-r--r--images/fuel-icon.pngbin0 -> 409 bytes
-rw-r--r--images/hazard.pngbin0 -> 9374 bytes
-rw-r--r--images/high-beam.pngbin0 -> 7322 bytes
-rw-r--r--images/lane.pngbin0 -> 1336 bytes
-rw-r--r--images/left.pngbin0 -> 1528 bytes
-rw-r--r--images/logo_agl.pngbin0 -> 7355 bytes
-rw-r--r--images/low-beam.pngbin0 -> 8854 bytes
-rw-r--r--images/malfunction.pngbin0 -> 4209 bytes
-rw-r--r--images/map1.jpgbin0 -> 206875 bytes
-rw-r--r--images/map2.jpgbin0 -> 238494 bytes
-rw-r--r--images/oil.pngbin0 -> 1351 bytes
-rw-r--r--images/openDoor.pngbin0 -> 1989 bytes
-rw-r--r--images/parking.pngbin0 -> 7820 bytes
-rw-r--r--images/parkingBrake.pngbin0 -> 2167 bytes
-rw-r--r--images/right.pngbin0 -> 787 bytes
-rw-r--r--images/seatBelt.pngbin0 -> 2103 bytes
-rw-r--r--images/server.pngbin0 -> 10680 bytes
-rw-r--r--images/server_error.pngbin0 -> 3538 bytes
-rw-r--r--images/temperature-icon.pngbin0 -> 3302 bytes
-rw-r--r--images/warning.pngbin0 -> 7990 bytes
-rw-r--r--lib/cluster_config.dart154
-rw-r--r--lib/main.dart19
-rw-r--r--lib/map/navigationHome.dart136
-rw-r--r--lib/map/networkPolyline.dart86
-rw-r--r--lib/provider.dart84
-rw-r--r--lib/screen/home.dart348
-rw-r--r--lib/screen/paints/arc_painter.dart116
-rw-r--r--lib/screen/paints/bottombar_paint.dart48
-rw-r--r--lib/screen/paints/guage_paint.dart144
-rw-r--r--lib/screen/paints/topbar_paint.dart42
-rw-r--r--lib/screen/widgets/guages/guage_props.dart48
-rw-r--r--lib/screen/widgets/guages/guage_widget.dart109
-rw-r--r--lib/screen/widgets/guages/rpm_guage_animation_wrapper.dart51
-rw-r--r--lib/screen/widgets/guages/speed_guage_animation_wrapper.dart66
-rw-r--r--lib/screen/widgets/left_bar.dart40
-rw-r--r--lib/screen/widgets/left_signal.dart31
-rw-r--r--lib/screen/widgets/performance_mode.dart32
-rw-r--r--lib/screen/widgets/right_bar.dart39
-rw-r--r--lib/screen/widgets/right_signal.dart31
-rw-r--r--lib/screen/widgets/signals.dart66
-rw-r--r--lib/screen/widgets/turn_signal.dart64
-rw-r--r--lib/vehicle_signal/initial_socket_connection.dart116
-rw-r--r--lib/vehicle_signal/listen_stream.dart56
-rw-r--r--lib/vehicle_signal/vehicle_signal_config.dart35
-rw-r--r--lib/vehicle_signal/vehicle_signal_methods.dart325
-rw-r--r--lib/vehicle_signal/vehicle_signal_model.dart152
-rw-r--r--lib/vehicle_signal/vehicle_signal_path.dart62
-rw-r--r--lib/vehicle_signal/vehicle_signal_provider.dart120
-rw-r--r--library_Llcense_details.txt52
-rw-r--r--pubspec.yaml97
61 files changed, 3024 insertions, 0 deletions
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 0000000..b0920c9
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,5 @@
+[gerrit]
+host=gerrit.automotivelinux.org
+port=29418
+project=apps/flutter-instrument-cluster
+defaultbranch=master \ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..9399587
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,201 @@
+ 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 2022 Aakash Solanki
+
+ 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/assets/cert/Client.key b/assets/cert/Client.key
new file mode 100644
index 0000000..1fe80a8
--- /dev/null
+++ b/assets/cert/Client.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA2ZvI9ZNBAXxgWqup/N9EjaOGB3XekHm0OASM+mMOaz5MXWdO
+b3y9VpxftdZ1JX+XKpWSGvpUnKz55RXSNDSoyIx1ZcWfSG8nPy1o0lnHh3Nu7/wY
+iSSZo0Toa8IpB5DkVzOqOfZHFAb9i1ZQd4BII7VX+C5zOSJ3CPitSbck4c0o5Hem
+PjDgc0BJ0cJGDgjTW1oYpuv2rUbYc1L3WLJNdFlvqIbJbDfm4g64r35tQ87ysXTG
+Ha7N80c+QFkIq0oEPcMPmgy3Hxu2Z9dBn7PLOWDGGJzQX5GTMoc+BFvrXy/TPCpr
+7TRyQWN44zouED/+aQd9wsaJZeLc0HXp2XsanwIDAQABAoIBAAJuNwRnLKazmO/q
+Uu+8HqvmU6zpqB68/UJS6rAxQwv3mirA6KNeJU8+V+NUDeLpVern2MXsXd2SIQhq
+AfsLS4dR6k3Es06/zxRpkW4oCo58v5R2oF+HxR+JuX7aoFKxT5uZvCEBmzGiElro
+D+YFzMh5Ug43lpngxKuY+3aDUSokdzap6kyIenL/kQxFjqp8ZiFPZQVgf+RLw5ks
+o6oVvi9nWmuNDQW6xNm5RsFLpBexQat1rRI59gZHfXEx5yAU5stf+Gdx/hyGcCzB
+Jjpzp7krvPushU0rhNaBaAYTFo5J20feCdvleO1YJia/iT9fS1pcXWrhKMe9Ar2L
+YOooZKECgYEA7ZhM4kGWdwdaxU/udkuZxfIITddvpKcKa/Mk5TcyytBmy8z8p0Uu
+miRkp6jZwmRvxy5oMDiVe1VOkHCtSZvFH2ln4wint54d5fHrr3vL5i5pLnr0gESu
+6aTPIn44ht1XamWBbNBeG5ck+i0tbSvR/rGLdzOYd94isxinzZXabqsCgYEA6ncj
+bzIwc2WYj8FEGHzQwqvvDbAtibPSJNQIAcpGD4dugUSkgxqE5nLHRq8Sshmnb87R
+pVEnOVpLT9hLHBfnNd6Um0k2SioRJ3mnYVFpO3x3idUWfEddEF1fF4JAjJgVaaJw
+b79YJh36H/ibcYlPqzQV+6HdDQUGo+2fFiGYs90CgYBoLZ+Ilbmj91WVrragqAWF
+BdseRphtWdD4BGTpHIro4vgv5HHj5jHI5Kw914uj8OYSMAb/HdBtg7ovGzjgTazh
+WRZ0NhdUK8BwUuNEzsNY6oC0ILWUiIkpaVpphW7Ds8ngnSFi1PpunpdYGvcQJ2as
+hqzgV39DD+OnI+hv/IUKgQKBgAu3dmrZHDi4C/6rDscWfawswV5yvmPSO58hhWDp
+hcTC5M/K6Vmv2pJVjAXJO+rwg0CAGlPjkU05aOgNi/tUanZzsANujir4nTDW0Hl5
+gwO4/ZRCtMPzIQBvWhAnrygXdYJ43puG+VW2S8iLOOFclkGc8bhIZ25+IM3SUw/d
+D+91AoGBAKBPUCOTDnOUK4p1kFRIXtVelPs+iwZ/dZ/EwIz+V2+enO515KM2deXJ
+cESlB8Ne1YbKeoUSoIAHPLUkbgVYDysJ/dTx8/uc+xrtTN6OhOENYtuc1Cgp2URB
+El/CCOTtEr9WtU8zXdTa9HLCzcdmHSuMWO9pDhKk0WwEBEhLKVtN
+-----END RSA PRIVATE KEY-----
diff --git a/assets/cert/Client.pem b/assets/cert/Client.pem
new file mode 100644
index 0000000..6674045
--- /dev/null
+++ b/assets/cert/Client.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDozCCAosCFFSe+tfplfRAkm683YXCJhF1e35eMA0GCSqGSIb3DQEBCwUAMIGQ
+MQswCQYDVQQGEwJERTELMAkGA1UECAwCQlcxDDAKBgNVBAcMA1JuZzEaMBgGA1UE
+CgwRUm9iZXJ0IEJvc2NoIEdtYkgxCzAJBgNVBAsMAkNSMRUwEwYDVQQDDAxsb2Nh
+bGhvc3QtY2ExJjAkBgkqhkiG9w0BCQEWF0NJLkhvdGxpbmVAZGUuYm9zY2guY29t
+MB4XDTIyMDcxNjA4NTgzNloXDTIzMDcxNjA4NTgzNlowgYoxCzAJBgNVBAYTAkRF
+MQswCQYDVQQIDAJCVzEMMAoGA1UEBwwDUm5nMRowGAYDVQQKDBFSb2JlcnQgQm9z
+Y2ggR21iSDELMAkGA1UECwwCQ1IxDzANBgNVBAMMBkNsaWVudDEmMCQGCSqGSIb3
+DQEJARYXQ0kuSG90bGluZUBkZS5ib3NjaC5jb20wggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQDZm8j1k0EBfGBaq6n830SNo4YHdd6QebQ4BIz6Yw5rPkxd
+Z05vfL1WnF+11nUlf5cqlZIa+lScrPnlFdI0NKjIjHVlxZ9Ibyc/LWjSWceHc27v
+/BiJJJmjROhrwikHkORXM6o59kcUBv2LVlB3gEgjtVf4LnM5IncI+K1JtyThzSjk
+d6Y+MOBzQEnRwkYOCNNbWhim6/atRthzUvdYsk10WW+ohslsN+biDrivfm1DzvKx
+dMYdrs3zRz5AWQirSgQ9ww+aDLcfG7Zn10Gfs8s5YMYYnNBfkZMyhz4EW+tfL9M8
+KmvtNHJBY3jjOi4QP/5pB33Cxoll4tzQdenZexqfAgMBAAEwDQYJKoZIhvcNAQEL
+BQADggEBACpacLomhULyxFgUDb8KSQa0DvNoN7awR2OorMQuIDUzpVmyYtxZDYnu
+bW/nDTofgKUowkzwore9t/ngVGekM0Ptn6p7vZ+AvSejZrjtvZ52FGoMEEnk9wql
+J/vaAS18ejx05SOy6BaLHCtCLjW4KQZDmM6KChS1dmImQnNNadyYyVHF0ckm+KaW
+o9KDIi8M0iyNXJM8v0p0LeFAlN3oaSKHZdB7oPEfTDVW4ABePoSlIUlUZHOUSQtx
+2Pra2xqTzOlGwQqaVKSCyMx02sgsgT/C6Ms+ve/gV5r/J1x8uw/l10rxZ8dEMN0Z
+Xv9t0iTXaEqN0wrqZ8BkI+iNI36nUp0=
+-----END CERTIFICATE-----
diff --git a/images/arrow.png b/images/arrow.png
new file mode 100644
index 0000000..5a7a2db
--- /dev/null
+++ b/images/arrow.png
Binary files differ
diff --git a/images/battery.png b/images/battery.png
new file mode 100644
index 0000000..031d2fe
--- /dev/null
+++ b/images/battery.png
Binary files differ
diff --git a/images/car.png b/images/car.png
new file mode 100644
index 0000000..473b970
--- /dev/null
+++ b/images/car.png
Binary files differ
diff --git a/images/cruise.png b/images/cruise.png
new file mode 100644
index 0000000..2a1c481
--- /dev/null
+++ b/images/cruise.png
Binary files differ
diff --git a/images/demo.png b/images/demo.png
new file mode 100644
index 0000000..e4264c5
--- /dev/null
+++ b/images/demo.png
Binary files differ
diff --git a/images/eco.png b/images/eco.png
new file mode 100644
index 0000000..b4a46a2
--- /dev/null
+++ b/images/eco.png
Binary files differ
diff --git a/images/engine-coolant.png b/images/engine-coolant.png
new file mode 100644
index 0000000..4aeeca0
--- /dev/null
+++ b/images/engine-coolant.png
Binary files differ
diff --git a/images/fuel-icon.png b/images/fuel-icon.png
new file mode 100644
index 0000000..70da758
--- /dev/null
+++ b/images/fuel-icon.png
Binary files differ
diff --git a/images/hazard.png b/images/hazard.png
new file mode 100644
index 0000000..ffa99d5
--- /dev/null
+++ b/images/hazard.png
Binary files differ
diff --git a/images/high-beam.png b/images/high-beam.png
new file mode 100644
index 0000000..8678367
--- /dev/null
+++ b/images/high-beam.png
Binary files differ
diff --git a/images/lane.png b/images/lane.png
new file mode 100644
index 0000000..6fbac27
--- /dev/null
+++ b/images/lane.png
Binary files differ
diff --git a/images/left.png b/images/left.png
new file mode 100644
index 0000000..c0fadcc
--- /dev/null
+++ b/images/left.png
Binary files differ
diff --git a/images/logo_agl.png b/images/logo_agl.png
new file mode 100644
index 0000000..422eca5
--- /dev/null
+++ b/images/logo_agl.png
Binary files differ
diff --git a/images/low-beam.png b/images/low-beam.png
new file mode 100644
index 0000000..db2003e
--- /dev/null
+++ b/images/low-beam.png
Binary files differ
diff --git a/images/malfunction.png b/images/malfunction.png
new file mode 100644
index 0000000..1b87ca0
--- /dev/null
+++ b/images/malfunction.png
Binary files differ
diff --git a/images/map1.jpg b/images/map1.jpg
new file mode 100644
index 0000000..fa52f80
--- /dev/null
+++ b/images/map1.jpg
Binary files differ
diff --git a/images/map2.jpg b/images/map2.jpg
new file mode 100644
index 0000000..7575aa5
--- /dev/null
+++ b/images/map2.jpg
Binary files differ
diff --git a/images/oil.png b/images/oil.png
new file mode 100644
index 0000000..20f6c24
--- /dev/null
+++ b/images/oil.png
Binary files differ
diff --git a/images/openDoor.png b/images/openDoor.png
new file mode 100644
index 0000000..3538c2c
--- /dev/null
+++ b/images/openDoor.png
Binary files differ
diff --git a/images/parking.png b/images/parking.png
new file mode 100644
index 0000000..9927010
--- /dev/null
+++ b/images/parking.png
Binary files differ
diff --git a/images/parkingBrake.png b/images/parkingBrake.png
new file mode 100644
index 0000000..e51222f
--- /dev/null
+++ b/images/parkingBrake.png
Binary files differ
diff --git a/images/right.png b/images/right.png
new file mode 100644
index 0000000..c5749bb
--- /dev/null
+++ b/images/right.png
Binary files differ
diff --git a/images/seatBelt.png b/images/seatBelt.png
new file mode 100644
index 0000000..a4c4740
--- /dev/null
+++ b/images/seatBelt.png
Binary files differ
diff --git a/images/server.png b/images/server.png
new file mode 100644
index 0000000..180d082
--- /dev/null
+++ b/images/server.png
Binary files differ
diff --git a/images/server_error.png b/images/server_error.png
new file mode 100644
index 0000000..2d09ec9
--- /dev/null
+++ b/images/server_error.png
Binary files differ
diff --git a/images/temperature-icon.png b/images/temperature-icon.png
new file mode 100644
index 0000000..5a4334e
--- /dev/null
+++ b/images/temperature-icon.png
Binary files differ
diff --git a/images/warning.png b/images/warning.png
new file mode 100644
index 0000000..8af8e16
--- /dev/null
+++ b/images/warning.png
Binary files differ
diff --git a/lib/cluster_config.dart b/lib/cluster_config.dart
new file mode 100644
index 0000000..10e152d
--- /dev/null
+++ b/lib/cluster_config.dart
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:io';
+import 'package:flutter/material.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/initial_socket_connection.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:yaml/yaml.dart';
+
+class GetConfig extends ConsumerStatefulWidget {
+ const GetConfig({Key? key, required this.client}) : super(key: key);
+ final HttpClient client;
+
+ @override
+ ConsumerState<GetConfig> createState() => _GetConfigState();
+}
+
+class _GetConfigState extends ConsumerState<GetConfig> {
+ @override
+ void initState() {
+ super.initState();
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+ final configStateProvider = ref.read(clusterConfigStateprovider.notifier);
+
+ String configFilePath = '/etc/xdg/AGL/flutter-cluster-dashboard.yaml';
+ String orsKeyFilePath = '/etc/default/openroutekey';
+
+ String keyContent = "";
+
+ final configFile = File(configFilePath);
+ final orsKeyFile = File(orsKeyFilePath);
+
+ configFile.readAsString().then((content) {
+ final dynamic yamlMap = loadYaml(content);
+ configStateProvider.update(
+ hostname: yamlMap['hostname'],
+ port: yamlMap['port'],
+ homeLat: yamlMap['homeLat'],
+ homeLng: yamlMap['homeLng'],
+ orsPathParam: yamlMap['orsPathParam'],
+ kuksaAuthToken: yamlMap['kuskaAuthToken'],
+ );
+ });
+
+ orsKeyFile.readAsString().then((content) {
+ keyContent = content.split(':')[1].trim();
+ if (keyContent.isNotEmpty && keyContent != 'YOU_NEED_TO_SET_IT_IN_LOCAL_CONF') {
+ configStateProvider.update(orsApiKey: keyContent);
+ } else {
+ print("WARNING: openrouteservice API Key not found !");
+ }
+ });
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final config = ref.watch(clusterConfigStateprovider);
+ if (config.hostname == "" ||
+ config.port == 0 ||
+ config.kuksaAuthToken == "" ||
+ config.homeLat == 0 ||
+ config.homeLng == 0 ||
+ config.orsPathParam == "") {
+ return Scaffold(
+ body: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: const [
+ Text("ERROR",
+ style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
+ Text(
+ "Something Wrong with config file! Check config.yaml file and restart"),
+ ],
+ )),
+ );
+ }
+ return InitialScreen(client: widget.client);
+ }
+}
+
+class ClusterConfig {
+ ClusterConfig({
+ required this.hostname,
+ required this.port,
+ required this.kuksaAuthToken,
+ required this.orsApiKey,
+ required this.orsPathParam,
+ required this.homeLat,
+ required this.homeLng,
+ });
+ final String hostname;
+ final int port;
+ final String kuksaAuthToken;
+ final double homeLat;
+ final double homeLng;
+ final String orsApiKey;
+ final String orsPathParam;
+
+ ClusterConfig copywith({
+ String? hostname,
+ int? port,
+ String? kuksaAuthToken,
+ double? homeLat,
+ double? homeLng,
+ String? orsApiKey,
+ String? orsPathParam,
+ }) =>
+ ClusterConfig(
+ hostname: hostname ?? this.hostname,
+ port: port ?? this.port,
+ kuksaAuthToken: kuksaAuthToken ?? this.kuksaAuthToken,
+ orsApiKey: orsApiKey ?? this.orsApiKey,
+ orsPathParam: orsPathParam ?? this.orsPathParam,
+ homeLat: homeLat ?? this.homeLat,
+ homeLng: homeLng ?? this.homeLng,
+ );
+}
+
+class ClusterConfigStateNotifier extends StateNotifier<ClusterConfig> {
+ ClusterConfigStateNotifier() : super(_initialValue);
+ static final ClusterConfig _initialValue = ClusterConfig(
+ hostname: "",
+ port: 0,
+ kuksaAuthToken: "",
+ orsApiKey: "",
+ orsPathParam: "",
+ homeLat: 0,
+ homeLng: 0,
+ );
+ void update({
+ String? hostname,
+ int? port,
+ String? kuksaAuthToken,
+ double? homeLat,
+ double? homeLng,
+ String? orsApiKey,
+ String? orsPathParam,
+ }) {
+ state = state.copywith(
+ hostname: hostname,
+ port: port,
+ kuksaAuthToken: kuksaAuthToken,
+ homeLat: homeLat,
+ homeLng: homeLng,
+ orsApiKey: orsApiKey,
+ orsPathParam: orsPathParam,
+ );
+ }
+}
+
+final clusterConfigStateprovider =
+ StateNotifierProvider<ClusterConfigStateNotifier, ClusterConfig>(
+ (ref) => ClusterConfigStateNotifier());
diff --git a/lib/main.dart b/lib/main.dart
new file mode 100644
index 0000000..c1198ce
--- /dev/null
+++ b/lib/main.dart
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:io';
+import 'package:flutter/material.dart';
+import 'package:flutter_cluster_dashboard/cluster_config.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_config.dart';
+
+Future<void> main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+ HttpClient client = await initializeClient();
+ runApp(
+ ProviderScope(
+ child: MaterialApp(
+ home: GetConfig(client: client),
+ ),
+ ),
+ );
+}
diff --git a/lib/map/navigationHome.dart b/lib/map/navigationHome.dart
new file mode 100644
index 0000000..5218de7
--- /dev/null
+++ b/lib/map/navigationHome.dart
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:async';
+import 'package:flutter/material.dart';
+import 'package:flutter_cluster_dashboard/cluster_config.dart';
+import 'package:flutter_map/flutter_map.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_cluster_dashboard/map/networkPolyline.dart';
+import 'package:flutter_cluster_dashboard/provider.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_provider.dart';
+import 'package:latlong2/latlong.dart';
+
+class NavigationHome extends ConsumerStatefulWidget {
+ const NavigationHome({Key? key}) : super(key: key);
+
+ @override
+ ConsumerState<NavigationHome> createState() => _NavigationHomeState();
+}
+
+class _NavigationHomeState extends ConsumerState<NavigationHome> {
+ late Timer timerCurrLocation;
+ Timer timerPolyline = Timer.periodic(const Duration(hours: 10), ((timer) {}));
+ double pathStroke = 5;
+ late MapController mapController;
+ // randomly initialization of the variable
+ LatLng src = LatLng(31.71, 76.95);
+ LatLng markerLocation = LatLng(31.71, 76.95);
+
+ @override
+ void initState() {
+ super.initState();
+ mapController = MapController();
+
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+ var vehicle = ref.read(vehicleSignalProvider);
+ var polylineDB = ref.read(polyLineStateProvider);
+ final polylineDBNotifier = ref.read(polyLineStateProvider.notifier);
+ final clusterConfig = ref.read(clusterConfigStateprovider);
+ // timer for updating map center and zoom
+ timerCurrLocation = Timer.periodic(const Duration(seconds: 2), (timer) {
+ polylineDB = ref.read(polyLineStateProvider);
+
+ vehicle = ref.read(vehicleSignalProvider);
+ markerLocation = LatLng(vehicle.currLat, vehicle.currLng);
+ // move and center
+ mapController.move(LatLng(vehicle.currLat, vehicle.currLng), 15);
+
+ // rotate
+ double rotationDegree = 0;
+ int n = polylineDB.currPolyLineList.length;
+ if (polylineDB.currPolyLineList.isNotEmpty && n > 1) {
+ rotationDegree = calcAngle(
+ polylineDB.currPolyLineList[0], polylineDB.currPolyLineList[1]);
+
+ rotationDegree = (rotationDegree.isNaN) ? 0 : rotationDegree;
+ }
+ // print("Rotation:$rotationDegree");
+ mapController.rotate(-1 * rotationDegree);
+ });
+
+ // update polyline in polyline db
+ if (polylineDB.currPolyLineList.isEmpty && clusterConfig.orsApiKey.isNotEmpty) {
+ timerPolyline.cancel();
+ timerPolyline =
+ Timer.periodic(const Duration(seconds: 10), (timer) async {
+ List data = await getJsonData(ref, vehicle.currLat, vehicle.currLng,
+ vehicle.desLat, vehicle.desLng);
+ List<LatLng> currList =
+ data.map((element) => LatLng(element[1], element[0])).toList();
+ polylineDBNotifier.update(currPolyLineList: currList);
+ });
+ }
+ });
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ timerCurrLocation.cancel();
+ timerPolyline.cancel();
+ }
+
+ double tempangle = 0;
+ @override
+ Widget build(BuildContext context) {
+ final currListProvider = ref.watch(polyLineStateProvider);
+ List<LatLng> currPolyLineList = currListProvider.currPolyLineList;
+
+ return FlutterMap(
+ mapController: mapController,
+ options: MapOptions(
+ rotation: 0,
+ center: src,
+ minZoom: 12,
+ zoom: 12,
+ maxZoom: 22.0,
+ keepAlive: true,
+ ),
+ layers: [
+ TileLayerOptions(
+ maxZoom: 22,
+ maxNativeZoom: 18,
+ subdomains: ["a", "b", "c"],
+ urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
+ userAgentPackageName: 'dev.fleaflet.flutter_map.example',
+ ),
+ if (currPolyLineList.isNotEmpty)
+ PolylineLayerOptions(
+ polylineCulling: false,
+ polylines: [
+ if (currPolyLineList.isNotEmpty)
+ Polyline(
+ strokeWidth: pathStroke,
+ points: currPolyLineList,
+ color: Colors.blue,
+ ),
+ ],
+ ),
+ if (currPolyLineList.isNotEmpty)
+ MarkerLayerOptions(
+ rotate: true,
+ markers: [
+ Marker(
+ point: markerLocation,
+ width: 70,
+ height: 70,
+ builder: (context) => Image.asset(
+ "images/car.png",
+ ),
+ ),
+ ],
+ ),
+ ],
+ );
+ }
+}
diff --git a/lib/map/networkPolyline.dart b/lib/map/networkPolyline.dart
new file mode 100644
index 0000000..0241bec
--- /dev/null
+++ b/lib/map/networkPolyline.dart
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:math';
+import 'package:flutter_cluster_dashboard/cluster_config.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:http/http.dart' as http;
+import 'dart:convert';
+import 'package:latlong2/latlong.dart';
+
+class NetworkHelper {
+ NetworkHelper(
+ {required this.startLng,
+ required this.startLat,
+ required this.endLng,
+ required this.endLat});
+
+ final String url = 'https://api.openrouteservice.org/v2/directions/';
+
+ final double startLng;
+ final double startLat;
+ final double endLng;
+ final double endLat;
+
+ Future getData(WidgetRef ref) async {
+ final config = ref.read(clusterConfigStateprovider);
+ String uriStr =
+ '$url${config.orsPathParam}?api_key=${config.orsApiKey}&start=$startLng,$startLat&end=$endLng,$endLat';
+ http.Response response = await http.get(Uri.parse(uriStr));
+
+ if (response.statusCode == 200) {
+ String data = response.body;
+ return jsonDecode(data);
+ } else {
+ print("Warning: API Response Code: ${response.statusCode}");
+ }
+ }
+}
+
+Future getJsonData(
+ WidgetRef ref,
+ double startLat,
+ double startLng,
+ double endLat,
+ double endLng,
+) async {
+ if (startLat == endLat && startLng == endLng) {
+ return [];
+ } else {
+ NetworkHelper network = NetworkHelper(
+ startLat: startLat,
+ startLng: startLng,
+ endLat: endLat,
+ endLng: endLng,
+ );
+ try {
+ final apikey = ref.read(clusterConfigStateprovider).orsApiKey;
+ if (apikey.isNotEmpty) {
+ var data = await network.getData(ref);
+ return data['features'][0]['geometry']['coordinates'];
+ }
+ else {
+ return [];
+ }
+ } catch (error) {
+ print('Warning: Something Wrong with openstreet API Key !');
+ return [];
+ }
+ }
+}
+
+double calcAngle(LatLng a, LatLng b) {
+ List<double> newA = convertCoord(a);
+ List<double> newB = convertCoord(b);
+ double slope = (newB[1] - newA[1]) / (newB[0] - newA[0]);
+ // -1 * deg + 180
+ return ((atan(slope) * 180) / pi);
+}
+
+List<double> convertCoord(LatLng coord) {
+ double oldLat = coord.latitude;
+ double oldLong = coord.longitude;
+ double newLong = (oldLong * 20037508.34 / 180);
+ double newlat =
+ (log(tan((90 + oldLat) * pi / 360)) / (pi / 180)) * (20037508.34 / 180);
+ return [newlat, newLong];
+}
diff --git a/lib/provider.dart b/lib/provider.dart
new file mode 100644
index 0000000..c96c8e4
--- /dev/null
+++ b/lib/provider.dart
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:async';
+import 'dart:math';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:latlong2/latlong.dart';
+
+// -------------------------------------------------------------
+final clockProvider = StateNotifierProvider<Clock, DateTime>((ref) {
+ return Clock();
+});
+
+class Clock extends StateNotifier<DateTime> {
+ late final Timer _timer;
+ Clock() : super(DateTime.now()) {
+ _timer = Timer.periodic(const Duration(seconds: 5), (_) {
+ state = DateTime.now();
+ });
+ }
+
+ @override
+ void dispose() {
+ _timer.cancel();
+ super.dispose();
+ }
+}
+
+// -------------------------------------------------------------
+class PolyLinesDB {
+ final List<LatLng> currPolyLineList;
+ final List<LatLng> polyLineList;
+ PolyLinesDB({required this.currPolyLineList, required this.polyLineList});
+ PolyLinesDB copyWith({
+ List<LatLng>? currPolyLineList,
+ List<LatLng>? polyLineList,
+ }) {
+ return PolyLinesDB(
+ currPolyLineList: currPolyLineList ?? this.currPolyLineList,
+ polyLineList: polyLineList ?? this.polyLineList,
+ );
+ }
+}
+
+class PolyLineNotifier extends StateNotifier<PolyLinesDB> {
+ static final PolyLinesDB initialvalue = PolyLinesDB(
+ currPolyLineList: [],
+ polyLineList: [],
+ );
+ PolyLineNotifier() : super(initialvalue);
+ void update({
+ List<LatLng>? currPolyLineList,
+ List<LatLng>? polyLineList,
+ }) {
+ state = state.copyWith(
+ currPolyLineList: currPolyLineList,
+ polyLineList: polyLineList,
+ );
+ }
+}
+
+final polyLineStateProvider =
+ StateNotifierProvider<PolyLineNotifier, PolyLinesDB>(
+ (ref) => PolyLineNotifier(),
+);
+
+// -------------------------------------------------------------
+
+class Gear {
+ static String parking = "P";
+ static String drive = "D";
+ static String neutral = "N";
+ static String reverse = "R";
+}
+
+double calculateDistance(point1, point2) {
+ double p = 0.017453292519943295;
+
+ double halfCosLatDiff = cos((point2.latitude - point1.latitude) * p) / 2;
+ double halfCosLngDiff = cos((point2.longitude - point1.longitude) * p) / 2;
+
+ double dist = 0.5 - halfCosLatDiff + cos(point1.latitude * p) * cos(point2.latitude * p) * (0.5 - halfCosLngDiff);
+
+ return 12742 * asin(sqrt(dist));
+}
diff --git a/lib/screen/home.dart b/lib/screen/home.dart
new file mode 100644
index 0000000..70bc5c6
--- /dev/null
+++ b/lib/screen/home.dart
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_cluster_dashboard/map/navigationHome.dart';
+import 'package:flutter_cluster_dashboard/provider.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/guage_props.dart';
+import 'package:flutter_cluster_dashboard/screen/paints/bottombar_paint.dart';
+import 'package:flutter_cluster_dashboard/screen/paints/topbar_paint.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/rpm_guage_animation_wrapper.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/left_bar.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/performance_mode.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/right_bar.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/speed_guage_animation_wrapper.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/signals.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/turn_signal.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_provider.dart';
+import 'package:intl/intl.dart';
+
+class Home extends ConsumerWidget {
+ const Home({Key? key}) : super(key: key);
+ GuageColors? getGuageColor(String mode) {
+ return (mode == "economy")
+ ? GuageProps.ecoModeColor
+ : (mode == "sport")
+ ? GuageProps.sportModeColor
+ : null;
+ }
+
+ String addZero(int val) {
+ String res = val.toString();
+ if (res.length < 2) {
+ if (res.isEmpty) {
+ return "00";
+ } else if (res.length == 1) {
+ return "0$res";
+ }
+ }
+ return res;
+ }
+
+ double calcPadding(double value, double height) {
+ // values wrt to values at 720 height
+ return (value * height) / 720;
+ }
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final vehicle = ref.watch(vehicleSignalProvider);
+ final clock = ref.watch(clockProvider);
+ final windowHeight = MediaQuery.of(context).size.height;
+ final windowWidth = MediaQuery.of(context).size.width;
+
+ double screenHeight = windowHeight;
+ double screenWidth = windowWidth;
+
+ double ratHeight = (windowWidth * 9) / 16;
+ double ratWidth = (windowHeight * 16) / 9;
+
+ if (ratWidth <= windowWidth) {
+ screenHeight = windowHeight;
+ screenWidth = ratWidth;
+ } else {
+ screenHeight = ratHeight;
+ screenWidth = windowWidth;
+ }
+
+ return Scaffold(
+ backgroundColor: GuageProps.bgColor,
+ body: SafeArea(
+ child: Center(
+ child: Center(
+ child: SizedBox(
+ width: screenWidth,
+ height: screenHeight,
+ child: Flex(
+ direction: Axis.vertical,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ //TopBarPainter
+ Flexible(
+ flex: 1,
+ child: Stack(
+ children: [
+ TurnSignal(
+ screenHeight: screenHeight,
+ isLefton: vehicle.isLeftIndicator,
+ isRighton: vehicle.isRightIndicator,
+ ),
+ Flex(
+ direction: Axis.horizontal,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ // mid section of top bar
+ Flexible(
+ flex: 1,
+ child: SizedBox(
+ width: (400 * screenHeight) / 480,
+ height: (65 * screenHeight) / 480,
+ child: CustomPaint(
+ painter: TopBarPainter(),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(
+ DateFormat('EEEE')
+ .format(clock)
+ .substring(0, 3),
+ style: TextStyle(
+ color: const Color.fromARGB(
+ 255, 184, 183, 183),
+ fontSize: (20 * screenHeight) / 480,
+ fontWeight: FontWeight.bold),
+ ),
+ SizedBox(
+ width: (40 * screenHeight) / 480),
+ Text(
+ "${clock.hour}:${addZero(clock.minute)}",
+ style: TextStyle(
+ color: const Color.fromARGB(
+ 255, 255, 255, 255),
+ fontSize: (30 * screenHeight) / 480,
+ fontWeight: FontWeight.bold),
+ ),
+ SizedBox(
+ width: (30 * screenHeight) / 480),
+ Text(
+ "${vehicle.ambientAirTemp} ${"\u00B0"}C",
+ style: TextStyle(
+ color: const Color.fromARGB(
+ 255, 184, 183, 183),
+ fontSize: (20 * screenHeight) / 480,
+ fontWeight: FontWeight.bold),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ // mid section
+ Flexible(
+ flex: 4,
+ fit: FlexFit.tight,
+ child: Stack(
+ children: [
+ //left and right arc
+ Padding(
+ padding: EdgeInsets.fromLTRB(
+ calcPadding(60, screenHeight),
+ calcPadding(60, screenHeight),
+ calcPadding(60, screenHeight),
+ 0),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Stack(
+ alignment: Alignment.bottomRight,
+ children: [
+ LeftArc(screenHeight: screenHeight),
+ Positioned(
+ child: Image.asset(
+ "images/temperature-icon.png",
+ color: Colors.white,
+ width: (13 * screenHeight) / 480,
+ ),
+ ),
+ ],
+ ),
+ Stack(
+ alignment: Alignment.bottomLeft,
+ children: [
+ RightArc(screenHeight: screenHeight),
+ Positioned(
+ child: Image.asset(
+ "images/fuel-icon.png",
+ color: Colors.white,
+ width: (18 * screenHeight) / 480,
+ ))
+ ],
+ ),
+ ],
+ ),
+ ),
+ //logo area
+ Padding(
+ padding: EdgeInsets.fromLTRB(
+ calcPadding(60, screenHeight),
+ calcPadding(10, screenHeight),
+ calcPadding(60, screenHeight),
+ 0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ SizedBox(
+ width: (550 * screenHeight) / 720,
+ height: (450 * screenHeight) / 720,
+ child: Flex(
+ direction: Axis.vertical,
+ mainAxisAlignment:
+ MainAxisAlignment.spaceEvenly,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ // performance mode
+ Flexible(
+ flex: 1,
+ child: PerformanceMode(
+ size: Size((90 * screenHeight) / 480,
+ (20 * screenHeight) / 480),
+ mode: vehicle.performanceMode),
+ ),
+ // logo
+ Flexible(
+ flex: 6,
+ fit: FlexFit.tight,
+ child: SizedBox(
+ width: (330 * screenHeight) / 720,
+ child: (vehicle.isSteeringInfo)
+ ? const NavigationHome()
+ : Padding(
+ padding: EdgeInsets.all(
+ (48.0 * screenHeight) /
+ 720),
+ child: Image.asset(
+ "images/logo_agl.png",
+ width:
+ (90 * screenHeight) / 480,
+ color: Colors.grey.shade600,
+ ),
+ ),
+ ),
+ ),
+ const Flexible(
+ flex: 1,
+ child: SizedBox(),
+ )
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ //warning signals
+ Padding(
+ padding: EdgeInsets.fromLTRB(
+ calcPadding(0, screenHeight),
+ calcPadding(430, screenHeight),
+ calcPadding(0, screenHeight),
+ 0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Signals(
+ screenHeight: screenHeight,
+ vehicle: vehicle,
+ ),
+ ],
+ ),
+ ),
+ // guages
+ Padding(
+ padding: EdgeInsets.fromLTRB(
+ calcPadding(70, screenHeight),
+ calcPadding(30, screenHeight),
+ calcPadding(70, screenHeight),
+ 0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ // Speed Guage
+ SpeedGauge(
+ screenHeight: screenHeight,
+ guageColor:
+ getGuageColor(vehicle.performanceMode),
+ ),
+ //RPM Guage
+ RPMGauge(
+ screenHeight: screenHeight,
+ guageColor:
+ getGuageColor(vehicle.performanceMode),
+ ),
+ ],
+ ),
+ )
+ ],
+ ),
+ ),
+ // bottomBarPainter
+ Flexible(
+ flex: 1,
+ child: SizedBox(
+ width: (400 * screenHeight) / 480,
+ height: (50 * screenHeight) / 480,
+ child: CustomPaint(
+ size: Size((400 * screenHeight) / 480,
+ (50 * screenHeight) / 480),
+ painter: BottomBarPainter(),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: [
+ (vehicle.selectedGear == Gear.parking)
+ ? Text("P",
+ style: GuageProps.activeGearIconStyle(
+ screenHeight))
+ : Text("P",
+ style: GuageProps.gearIconStyle(
+ screenHeight)),
+ (vehicle.selectedGear == Gear.reverse)
+ ? Text("R",
+ style: GuageProps.activeGearIconStyle(
+ screenHeight))
+ : Text("R",
+ style: GuageProps.gearIconStyle(
+ screenHeight)),
+ (vehicle.selectedGear == Gear.neutral)
+ ? Text("N",
+ style: GuageProps.activeGearIconStyle(
+ screenHeight))
+ : Text("N",
+ style: GuageProps.gearIconStyle(
+ screenHeight)),
+ (vehicle.selectedGear == Gear.drive)
+ ? Text("D",
+ style: GuageProps.activeGearIconStyle(
+ screenHeight))
+ : Text("D",
+ style: GuageProps.gearIconStyle(
+ screenHeight)),
+ ]),
+ ),
+ )),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/screen/paints/arc_painter.dart b/lib/screen/paints/arc_painter.dart
new file mode 100644
index 0000000..66e38d4
--- /dev/null
+++ b/lib/screen/paints/arc_painter.dart
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:math';
+import 'package:flutter/material.dart';
+
+class LeftPainter extends CustomPainter {
+ LeftPainter(
+ {required this.radi,
+ required this.currentValue,
+ required this.bottomPadding,
+ required this.color});
+
+ late final double radi;
+ late final double currentValue;
+ late final double bottomPadding;
+ late final Color color;
+
+ Offset getLeftPoints(Size size, double radius, double value) {
+ final double diam = 2 * radius;
+ final double arcHalfAngle = asin(size.height / diam); //thetha
+ final double currentAngle = (arcHalfAngle * value) / 50; //alpha
+ return Offset(
+ (radi * cos(arcHalfAngle)) +
+ (size.width) -
+ (radi * cos(arcHalfAngle - currentAngle)),
+ (radi * sin(arcHalfAngle)) + (radi * sin(arcHalfAngle - currentAngle)));
+ }
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ Offset startPoint = getLeftPoints(size, radi, bottomPadding);
+
+ final paint = Paint()
+ ..color = const Color.fromARGB(255, 49, 48, 48)
+ ..style = PaintingStyle.stroke
+ ..strokeCap = StrokeCap.round
+ ..strokeWidth = radi / 15;
+ final paint2 = Paint()
+ ..color = color
+ ..strokeCap = StrokeCap.round
+ ..style = PaintingStyle.stroke
+ ..strokeWidth = radi / 15;
+
+ final path = Path()
+ ..moveTo(startPoint.dx, startPoint.dy)
+ ..arcToPoint(Offset(size.width, 0), radius: Radius.circular(radi));
+ canvas.drawPath(path, paint);
+ final path2 = Path()
+ ..moveTo(startPoint.dx, startPoint.dy)
+ ..arcToPoint(
+ getLeftPoints(size, radi,
+ bottomPadding + ((1 - (bottomPadding / 100)) * currentValue)),
+ radius: Radius.circular(radi));
+ canvas.drawPath(path, paint);
+ canvas.drawPath(path2, paint2);
+ }
+
+ @override
+ bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
+}
+
+class RightPainter extends CustomPainter {
+ RightPainter(
+ {required this.radi,
+ required this.currentValue,
+ required this.bottomPadding,
+ required this.color});
+ final double radi;
+ final double currentValue;
+ late final double bottomPadding;
+ late final Color color;
+
+ Offset getRightPoints(Size size, double radius, double value) {
+ final double diam = 2 * radius;
+ final double arcHalfAngle = asin(size.height / diam); //thetha
+ final double currentAngle = (arcHalfAngle * value) / 50; //alpha
+ return Offset(
+ (radi * cos(arcHalfAngle - currentAngle)) - (radi * cos(arcHalfAngle)),
+ (radi * sin(arcHalfAngle)) + (radi * sin(arcHalfAngle - currentAngle)));
+ }
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ Offset startPoint = getRightPoints(size, radi, bottomPadding);
+ final paint = Paint()
+ ..color = const Color.fromARGB(255, 49, 48, 48)
+ ..style = PaintingStyle.stroke
+ ..strokeCap = StrokeCap.round
+ ..strokeWidth = radi / 15;
+
+ final paint2 = Paint()
+ ..color = color
+ ..style = PaintingStyle.stroke
+ ..strokeCap = StrokeCap.round
+ ..strokeWidth = radi / 15;
+ final path = Path()
+ ..moveTo(startPoint.dx, startPoint.dy)
+ ..arcToPoint(
+ const Offset(0, 0),
+ radius: Radius.circular(radi),
+ clockwise: false,
+ );
+ final path2 = Path()
+ ..moveTo(startPoint.dx, startPoint.dy)
+ ..arcToPoint(
+ getRightPoints(size, radi,
+ bottomPadding + ((1 - (bottomPadding / 100)) * currentValue)),
+ radius: Radius.circular(radi),
+ clockwise: false);
+ canvas.drawPath(path, paint);
+ canvas.drawPath(path2, paint2);
+ }
+
+ @override
+ bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
+}
diff --git a/lib/screen/paints/bottombar_paint.dart b/lib/screen/paints/bottombar_paint.dart
new file mode 100644
index 0000000..85c129e
--- /dev/null
+++ b/lib/screen/paints/bottombar_paint.dart
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:math';
+import 'dart:ui' as ui;
+import 'package:flutter/material.dart';
+
+class BottomBarPainter extends CustomPainter {
+ @override
+ void paint(Canvas canvas, Size size) {
+ final double height = size.height;
+ const double angle = 40;
+
+ double x = height * tan((angle / 180) * pi);
+ double y = height;
+
+ final double width = size.width - 2 * x;
+ final paint = Paint()
+ ..color = const ui.Color.fromARGB(255, 36, 36, 36)
+ ..strokeWidth = 5
+ ..strokeCap = StrokeCap.round
+ ..style = PaintingStyle.stroke;
+ final paint2 = Paint()
+ ..style = PaintingStyle.fill
+ ..shader = ui.Gradient.linear(
+ Offset((x + width / 2), 0), Offset((x + width / 2), height), [
+ const ui.Color.fromARGB(255, 25, 24, 24),
+ Colors.black,
+ ]);
+
+ final path = Path()
+ ..moveTo(0, size.height)
+ ..lineTo(x, size.height - y)
+ ..lineTo(x + width, size.height - y)
+ ..lineTo(size.width, size.height);
+
+ final path2 = Path()
+ ..moveTo(0, size.height)
+ ..lineTo(x, size.height - y)
+ ..lineTo(x + width, size.height - y)
+ ..lineTo(size.width, size.height);
+
+ canvas.drawPath(path, paint);
+ canvas.drawPath(path2, paint2);
+ }
+
+ @override
+ bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
+}
diff --git a/lib/screen/paints/guage_paint.dart b/lib/screen/paints/guage_paint.dart
new file mode 100644
index 0000000..22f7544
--- /dev/null
+++ b/lib/screen/paints/guage_paint.dart
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'dart:math';
+import 'dart:ui' as ui;
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/guage_props.dart';
+
+class GuagePainter extends CustomPainter {
+ final double low, high;
+ double currentSpeed;
+ final Color? outPrimaryColor;
+ final Color? inPrimaryColor;
+ final Color? secondaryColor;
+ GuagePainter({
+ required this.low,
+ required this.high,
+ required this.currentSpeed,
+ this.outPrimaryColor,
+ this.inPrimaryColor,
+ this.secondaryColor,
+ });
+ @override
+ void paint(Canvas canvas, Size size) {
+ Offset center = Offset(size.width / 2, size.height / 2);
+ double radius = min(size.width / 2, size.height / 2);
+ double radius1 = radius - ((20 / 200) * (radius));
+ double speedAngle =
+ GuageProps.degToRad((GuageProps.majorAngle / high) * currentSpeed);
+
+ final zeroTickPaint = Paint()
+ ..strokeWidth = ((7 / 200) * (radius))
+ ..shader = ui.Gradient.radial(center, radius1,
+ [Colors.black, const Color.fromARGB(255, 244, 242, 231)], [1, 0.5]);
+ final maxTickPaint = Paint()
+ ..strokeWidth = ((7 / 200) * (radius))
+ ..shader = ui.Gradient.radial(center, radius1,
+ [Colors.black, const Color.fromARGB(255, 244, 242, 231)], [1, 0.5]);
+
+ final speedPathStrokePaint = Paint()
+ ..style = PaintingStyle.stroke
+ ..strokeWidth = ((7 / 200) * (radius))
+ ..shader = ui.Gradient.radial(
+ center, radius1, [Colors.black, Colors.white], [0.6, 1]);
+
+ final speedPathFillPaint = Paint()
+ ..style = PaintingStyle.fill
+ ..shader = ui.Gradient.radial(center, radius1, [
+ const ui.Color.fromARGB(0, 0, 0, 0),
+ secondaryColor ?? const Color.fromARGB(156, 226, 226, 200)
+ ], [
+ 0.8,
+ 1
+ ]);
+
+ final outerPathPaint = ui.Paint()
+ ..style = PaintingStyle.fill
+ ..shader = ui.Gradient.radial(
+ center,
+ radius,
+ [
+ Colors.black,
+ outPrimaryColor ?? const Color.fromARGB(255, 120, 120, 120)
+ ],
+ [0.8, 0.9],
+ );
+
+ final innerPathPaint = Paint()
+ ..style = PaintingStyle.fill
+ ..shader = ui.Gradient.radial(center, radius1, [
+ Colors.black,
+ inPrimaryColor ?? const Color.fromARGB(255, 67, 67, 67)
+ ], [
+ 0.65,
+ 0.9
+ ]);
+
+ final outerPathPaintRed = ui.Paint()
+ ..style = PaintingStyle.fill
+ ..shader = ui.Gradient.radial(
+ center,
+ radius,
+ [Colors.black, const Color.fromARGB(255, 187, 59, 57)],
+ [0.8, 0.9],
+ );
+
+ final innerPathPaintRed = Paint()
+ ..style = PaintingStyle.fill
+ ..shader = ui.Gradient.radial(center, radius1,
+ [Colors.black, const Color.fromARGB(255, 142, 35, 39)], [0.65, 0.9]);
+
+ for (double i = 0; i < 13; i++) {
+ double startAngle = GuageProps.degToRad(i * 20);
+ double gapAngle = GuageProps.degToRad(19);
+
+ var outerPath = Path();
+ outerPath.addArc(Rect.fromCircle(center: center, radius: radius),
+ startAngle, gapAngle);
+ outerPath.lineTo(center.dx, center.dy);
+ outerPath.close();
+
+ var innerPath = Path();
+ innerPath.addArc(Rect.fromCircle(center: center, radius: radius1),
+ startAngle, gapAngle);
+ innerPath.lineTo(center.dx, center.dy);
+ innerPath.close();
+ if (i >= 11) {
+ canvas.drawPath(outerPath, outerPathPaintRed);
+ canvas.drawPath(innerPath, innerPathPaintRed);
+ } else {
+ canvas.drawPath(outerPath, outerPathPaint);
+ canvas.drawPath(innerPath, innerPathPaint);
+ }
+ }
+
+ var speedStrokePath = Path();
+ speedStrokePath.moveTo(center.dx, center.dy);
+ speedStrokePath.addArc(
+ Rect.fromCircle(center: center, radius: radius), 0, speedAngle);
+ speedStrokePath.lineTo(center.dx, center.dy);
+
+ var speedFillPath = Path();
+ speedFillPath.addArc(
+ Rect.fromCircle(center: center, radius: radius), 0, speedAngle);
+ speedFillPath.lineTo(center.dx, center.dy);
+ speedFillPath.close();
+
+ canvas.drawPath(speedFillPath, speedPathFillPaint);
+ canvas.drawPath(speedStrokePath, speedPathStrokePaint);
+ canvas.drawLine(
+ center,
+ Offset(center.dx + (radius + ((3 / 200) * (radius))), center.dy),
+ zeroTickPaint);
+ canvas.drawLine(
+ center,
+ Offset(center.dx + (radius) * cos(GuageProps.majorAngleRad),
+ center.dy + (radius) * sin(GuageProps.majorAngleRad)),
+ maxTickPaint);
+ }
+
+ @override
+ bool shouldRepaint(covariant CustomPainter oldDelegate) {
+ return true;
+ }
+}
diff --git a/lib/screen/paints/topbar_paint.dart b/lib/screen/paints/topbar_paint.dart
new file mode 100644
index 0000000..f7c79e8
--- /dev/null
+++ b/lib/screen/paints/topbar_paint.dart
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:math';
+import 'dart:ui' as ui;
+import 'package:flutter/material.dart';
+
+class TopBarPainter extends CustomPainter {
+ @override
+ void paint(Canvas canvas, Size size) {
+ final double height = size.height;
+ const double angle = 40;
+
+ double x = height * tan((angle / 180) * pi);
+ double y = height;
+
+ final double width = size.width - 2 * x;
+ final paint = Paint()
+ ..color = const ui.Color.fromARGB(255, 49, 47, 47)
+ ..strokeWidth = 5
+ ..strokeCap = StrokeCap.round
+ ..style = PaintingStyle.stroke;
+ final paint2 = Paint()
+ ..style = PaintingStyle.fill
+ ..shader = ui.Gradient.linear(
+ Offset((x + width / 2), 0),
+ Offset((x + width / 2), height),
+ [Colors.black, const ui.Color.fromARGB(255, 32, 31, 31)]);
+ final path = Path()
+ ..lineTo(x, y)
+ ..lineTo(x + width, y)
+ ..lineTo(size.width, 0);
+ final path2 = Path()
+ ..lineTo(x, y)
+ ..lineTo(x + width, y)
+ ..lineTo(size.width, 0);
+ canvas.drawPath(path, paint);
+ canvas.drawPath(path2, paint2);
+ }
+
+ @override
+ bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
+}
diff --git a/lib/screen/widgets/guages/guage_props.dart b/lib/screen/widgets/guages/guage_props.dart
new file mode 100644
index 0000000..bb56a31
--- /dev/null
+++ b/lib/screen/widgets/guages/guage_props.dart
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:math';
+import 'package:flutter/material.dart';
+
+class GuageProps {
+ static GuageColors normalModeColor = GuageColors(
+ inPrimary: const Color.fromARGB(255, 67, 67, 67),
+ outPrimary: const Color.fromARGB(255, 120, 120, 120),
+ secondary: const Color.fromARGB(156, 226, 226, 200),
+ );
+ static GuageColors sportModeColor = GuageColors(
+ inPrimary: Colors.deepPurple,
+ outPrimary: Colors.blue,
+ secondary: const Color.fromARGB(214, 202, 202, 202));
+ static GuageColors ecoModeColor = GuageColors(
+ inPrimary: const Color.fromARGB(255, 85, 165, 87),
+ outPrimary: const Color.fromARGB(255, 40, 92, 42),
+ secondary: const Color.fromARGB(202, 194, 238, 195));
+ static double majorAngle = 260;
+ static double majorAngleRad = 260 * (pi / 180);
+ static double minorAngle = 360 - majorAngle;
+ static Color bgColor = const Color.fromARGB(255, 0, 0, 0);
+ static const leftLowColor = Color(0x000000ff);
+ static const leftHighColor = Color(0x00ff0000);
+
+ static double degToRad(double deg) => deg * (pi / 180.0);
+ static TextStyle gearIconStyle(screenHeight) {
+ return TextStyle(
+ color: const Color.fromARGB(255, 84, 83, 83),
+ fontSize: (20 * screenHeight) / 480,
+ fontWeight: FontWeight.bold);
+ }
+
+ static TextStyle activeGearIconStyle(screenHeight) {
+ return TextStyle(
+ color: Colors.white,
+ fontSize: (20 * screenHeight) / 480,
+ fontWeight: FontWeight.bold);
+ }
+}
+
+class GuageColors {
+ Color? inPrimary;
+ Color? outPrimary;
+ Color? secondary;
+ GuageColors({this.inPrimary, this.outPrimary, this.secondary});
+}
diff --git a/lib/screen/widgets/guages/guage_widget.dart b/lib/screen/widgets/guages/guage_widget.dart
new file mode 100644
index 0000000..fa43958
--- /dev/null
+++ b/lib/screen/widgets/guages/guage_widget.dart
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:math';
+import 'package:flutter/material.dart';
+import 'package:flutter_cluster_dashboard/screen/paints/guage_paint.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/guage_props.dart';
+
+class CustomGuage extends StatelessWidget {
+ const CustomGuage({
+ Key? key,
+ required this.mainValue,
+ required this.low,
+ required this.high,
+ required this.label,
+ this.zeroTickLabel,
+ this.maxTickLabel,
+ this.distanceBWTicks,
+ this.size,
+ this.distLabelTop,
+ this.distMainTop,
+ this.distTicksBottom,
+ this.inPrimaryColor,
+ this.outPrimaryColor,
+ this.secondaryColor,
+ }) : super(key: key);
+
+ final double mainValue;
+ final double low;
+ final double high;
+ final String label;
+ final String? zeroTickLabel;
+ final String? maxTickLabel;
+ final double? distanceBWTicks;
+ final double? distTicksBottom;
+ final double? distMainTop;
+ final double? distLabelTop;
+ final double? size;
+ final Color? outPrimaryColor;
+ final Color? inPrimaryColor;
+ final Color? secondaryColor;
+
+ @override
+ Widget build(BuildContext context) {
+ TextStyle tickStyle = TextStyle(
+ color: Colors.white,
+ fontSize: ((26 / 400) * (size ?? 400)),
+ fontWeight: FontWeight.bold); //20
+ TextStyle mainValueTextStyle = TextStyle(
+ color: Colors.white,
+ fontSize: ((85 / 400) * (size ?? 400)),
+ fontWeight: FontWeight.bold); //65
+ TextStyle labelTextStyle = TextStyle(
+ color: Colors.white,
+ fontSize: ((26 / 400) * (size ?? 400)),
+ fontWeight: FontWeight.normal); //20
+ return SizedBox(
+ width: size ?? 400,
+ height: size ?? 400,
+ child: Stack(
+ alignment: Alignment.topCenter,
+ children: [
+ // Guage painter
+ Positioned(
+ top: 0,
+ child: Transform.rotate(
+ angle: (pi / 2) + (GuageProps.minorAngle * (pi / 360.0)),
+ child: CustomPaint(
+ size: Size(size ?? 400, size ?? 400),
+ painter: GuagePainter(
+ low: low,
+ high: high,
+ currentSpeed: mainValue,
+ inPrimaryColor: inPrimaryColor,
+ outPrimaryColor: outPrimaryColor,
+ secondaryColor: secondaryColor,
+ ),
+ ),
+ ),
+ ),
+ // Guage Label
+ Positioned(
+ top: distLabelTop ?? ((100 / 400) * (size ?? 400)),
+ child: Text(label, style: labelTextStyle),
+ ),
+ // Guage Main Value
+ Positioned(
+ top: distMainTop ?? ((150 / 400) * (size ?? 400)),
+ child: Text("${mainValue.toInt()}", style: mainValueTextStyle),
+ ),
+ // Guage Ticks value
+ Positioned(
+ bottom: distTicksBottom ?? ((80 / 400) * (size ?? 400)),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(zeroTickLabel ?? "", style: tickStyle),
+ SizedBox(
+ width: (size != null)
+ ? ((180 * size!) / 400)
+ : (distanceBWTicks ?? 180)),
+ Text(maxTickLabel ?? "", style: tickStyle)
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/screen/widgets/guages/rpm_guage_animation_wrapper.dart b/lib/screen/widgets/guages/rpm_guage_animation_wrapper.dart
new file mode 100644
index 0000000..95403dd
--- /dev/null
+++ b/lib/screen/widgets/guages/rpm_guage_animation_wrapper.dart
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/guage_props.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/guage_widget.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_model.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_provider.dart';
+
+class RPMGauge extends HookConsumerWidget {
+ final double screenHeight;
+ final GuageColors? guageColor;
+ const RPMGauge({Key? key, required this.screenHeight, this.guageColor})
+ : super(key: key);
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final VehicleSignal vehicle = ref.watch(vehicleSignalProvider);
+
+ const double minRPM = 0;
+ const double maxRPM = 8000;
+ const Duration sweepDuration = Duration(milliseconds: 200);
+
+ final animationController = useAnimationController(
+ lowerBound: minRPM,
+ upperBound: maxRPM,
+ )..animateTo(vehicle.rpm,
+ duration: sweepDuration, curve: Curves.linearToEaseOut);
+ return AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: CustomGuage(
+ size: (248 * screenHeight) / 480,
+ low: minRPM,
+ high: maxRPM,
+ mainValue: animationController.value,
+ label: "rpm",
+ zeroTickLabel: minRPM.toInt().toString(),
+ maxTickLabel: maxRPM.toInt().toString(),
+ inPrimaryColor: guageColor?.inPrimary,
+ outPrimaryColor: guageColor?.outPrimary,
+ secondaryColor: guageColor?.secondary,
+ ),
+ );
+ });
+ }
+}
diff --git a/lib/screen/widgets/guages/speed_guage_animation_wrapper.dart b/lib/screen/widgets/guages/speed_guage_animation_wrapper.dart
new file mode 100644
index 0000000..8704fcd
--- /dev/null
+++ b/lib/screen/widgets/guages/speed_guage_animation_wrapper.dart
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/guage_props.dart';
+import 'package:flutter_cluster_dashboard/screen/widgets/guages/guage_widget.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_model.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_provider.dart';
+
+class SpeedGauge extends HookConsumerWidget {
+ final double screenHeight;
+ final GuageColors? guageColor;
+ const SpeedGauge({Key? key, required this.screenHeight, this.guageColor})
+ : super(key: key);
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final VehicleSignal vehicle = ref.watch(vehicleSignalProvider);
+
+ const double minSpeed = 0;
+ const double maxSpeed = 240;
+ const Duration sweepDuration = Duration(milliseconds: 200);
+ double speedScaling =
+ (vehicle.vehicleDistanceUnit == "mi") ? 0.621504 : 1.0;
+
+ final animationController = useAnimationController(
+ lowerBound: minSpeed,
+ upperBound: maxSpeed,
+ )..animateTo(speedScaling * (vehicle.speed),
+ duration: sweepDuration, curve: Curves.linearToEaseOut);
+
+ return AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: CustomGuage(
+ size: (248 * screenHeight) / 480,
+ low: minSpeed,
+ high: maxSpeed,
+ mainValue: animationController.value,
+ label: (vehicle.vehicleDistanceUnit == "mi") ? "mph" : "Km/h",
+ zeroTickLabel: minSpeed.toInt().toString(),
+ maxTickLabel: maxSpeed.toInt().toString(),
+ inPrimaryColor: guageColor?.inPrimary,
+ outPrimaryColor: guageColor?.outPrimary,
+ secondaryColor: guageColor?.secondary,
+ ),
+ );
+ });
+ }
+}
+
+final guageColorProvider = Provider.family<GuageColors, String>((ref, mode) {
+ switch (mode) {
+ case "normal":
+ return GuageColors(inPrimary: Colors.red);
+ case "sports":
+ return GuageColors(inPrimary: Colors.blue);
+ case "eco":
+ return GuageColors(inPrimary: Colors.green);
+ default:
+ return GuageColors();
+ }
+});
diff --git a/lib/screen/widgets/left_bar.dart b/lib/screen/widgets/left_bar.dart
new file mode 100644
index 0000000..3192c28
--- /dev/null
+++ b/lib/screen/widgets/left_bar.dart
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:flutter_cluster_dashboard/screen/paints/arc_painter.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_model.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_provider.dart';
+
+class LeftArc extends HookConsumerWidget {
+ final double screenHeight;
+ const LeftArc({Key? key, required this.screenHeight}) : super(key: key);
+ // final Color color;
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final VehicleSignal vehicle = ref.watch(vehicleSignalProvider);
+ final animationController = useAnimationController(
+ lowerBound: 0,
+ upperBound: 100,
+ )..animateTo(vehicle.coolantTemp,
+ duration: const Duration(milliseconds: 1000));
+
+ return AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return CustomPaint(
+ size: Size(0, (220 * screenHeight) / 480),
+ painter: LeftPainter(
+ radi: (170 * screenHeight) / 480,
+ currentValue: animationController.value,
+ bottomPadding: 15,
+ color: Color.lerp(Colors.yellow, Colors.red,
+ (animationController.value / 100)) ??
+ Colors.orange,
+ ),
+ );
+ });
+ }
+}
diff --git a/lib/screen/widgets/left_signal.dart b/lib/screen/widgets/left_signal.dart
new file mode 100644
index 0000000..693c762
--- /dev/null
+++ b/lib/screen/widgets/left_signal.dart
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+
+class LeftSignal extends HookConsumerWidget {
+ final double screenHeight;
+ const LeftSignal({Key? key, required this.screenHeight}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final animationController = useAnimationController(
+ lowerBound: 0.9,
+ upperBound: 1.1,
+ duration: const Duration(milliseconds: 1000),
+ )..repeat();
+ return AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return Image.asset(
+ "images/left.png",
+ color: Color.lerp(
+ Colors.black,
+ const Color.fromARGB(255, 99, 251, 104),
+ animationController.value.floorToDouble()),
+ width: 0.125 * screenHeight,
+ );
+ });
+ }
+}
diff --git a/lib/screen/widgets/performance_mode.dart b/lib/screen/widgets/performance_mode.dart
new file mode 100644
index 0000000..256365b
--- /dev/null
+++ b/lib/screen/widgets/performance_mode.dart
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+
+class PerformanceMode extends StatelessWidget {
+ const PerformanceMode({Key? key, this.size, required this.mode})
+ : super(key: key);
+ final Size? size;
+ final String mode;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ width: size?.width ?? 20,
+ height: size?.height ?? 40,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(10),
+ color: (mode == "sport")
+ ? Colors.deepPurple
+ : (mode == "economy")
+ ? Colors.green
+ : Colors.transparent),
+ child: Center(
+ child: Text(
+ mode.toUpperCase(),
+ style: const TextStyle(
+ color: Colors.black, fontWeight: FontWeight.bold, fontSize: 12),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/screen/widgets/right_bar.dart b/lib/screen/widgets/right_bar.dart
new file mode 100644
index 0000000..e5ed44d
--- /dev/null
+++ b/lib/screen/widgets/right_bar.dart
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:flutter_cluster_dashboard/screen/paints/arc_painter.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_model.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_provider.dart';
+
+class RightArc extends HookConsumerWidget {
+ final double screenHeight;
+ const RightArc({Key? key, required this.screenHeight}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final VehicleSignal vehicle = ref.watch(vehicleSignalProvider);
+ final animationController = useAnimationController(
+ lowerBound: 0,
+ upperBound: 100,
+ )..animateTo(vehicle.fuelLevel,
+ duration: const Duration(milliseconds: 500));
+
+ return AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return CustomPaint(
+ size: Size(0, (220 * screenHeight) / 480),
+ painter: RightPainter(
+ radi: (170 * screenHeight) / 480,
+ currentValue: animationController.value,
+ bottomPadding: 17,
+ color: Color.lerp(Colors.red, Colors.green,
+ (animationController.value / 100)) ??
+ Colors.blue),
+ );
+ },
+ );
+ }
+}
diff --git a/lib/screen/widgets/right_signal.dart b/lib/screen/widgets/right_signal.dart
new file mode 100644
index 0000000..5c3aa5b
--- /dev/null
+++ b/lib/screen/widgets/right_signal.dart
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+
+class RightSignal extends HookConsumerWidget {
+ final double screenHeight;
+ const RightSignal({Key? key, required this.screenHeight}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final animationController = useAnimationController(
+ lowerBound: 0.9,
+ upperBound: 1.1,
+ duration: const Duration(milliseconds: 1000),
+ )..repeat();
+ return AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return Image.asset(
+ "images/right.png",
+ color: Color.lerp(
+ Colors.black,
+ const Color.fromARGB(255, 99, 251, 104),
+ animationController.value.floorToDouble()),
+ width: 0.125 * screenHeight,
+ );
+ });
+ }
+}
diff --git a/lib/screen/widgets/signals.dart b/lib/screen/widgets/signals.dart
new file mode 100644
index 0000000..788d379
--- /dev/null
+++ b/lib/screen/widgets/signals.dart
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_model.dart';
+
+class Signals extends StatelessWidget {
+ final VehicleSignal vehicle;
+ final double screenHeight;
+ static Color idleColor = const Color.fromARGB(194, 55, 53, 53);
+ const Signals({
+ Key? key,
+ required this.screenHeight,
+ required this.vehicle,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Wrap(
+ spacing: 14,
+ runAlignment: WrapAlignment.spaceBetween,
+ alignment: WrapAlignment.spaceEvenly,
+ children: [
+ (vehicle.isLowBeam)
+ ? Image.asset("images/low-beam.png",
+ color: Colors.green, width: (20 * screenHeight) / 480)
+ : (vehicle.isHighBeam)
+ ? Image.asset("images/high-beam.png",
+ color: Colors.green, width: (20 * screenHeight) / 480)
+ : Image.asset("images/high-beam.png",
+ color: idleColor, width: (20 * screenHeight) / 480),
+ Image.asset("images/hazard.png",
+ color: (vehicle.isHazardLightOn) ? Colors.red : idleColor,
+ width: (20 * screenHeight) / 480),
+ Image.asset("images/parking.png",
+ color: (vehicle.isParkingOn) ? Colors.green : idleColor,
+ width: (20 * screenHeight) / 480),
+ Image.asset("images/battery.png",
+ color: (vehicle.isBatteryCharging) ? Colors.green : Colors.red,
+ width: (20 * screenHeight) / 480),
+ Image.asset("images/malfunction.png",
+ color: (vehicle.isMILon) ? Colors.red : idleColor,
+ width: (20 * screenHeight) / 480),
+ //
+ Image.asset("images/openDoor.png",
+ color: (vehicle.isMILon) ? Colors.white : idleColor,
+ width: (20 * screenHeight) / 480),
+
+ Image.asset("images/seatBelt.png",
+ color: (vehicle.isMILon) ? Colors.white : idleColor,
+ width: (20 * screenHeight) / 480),
+
+ //
+ Image.asset("images/lane.png",
+ color: (vehicle.isSteeringLaneWarning) ? Colors.white : idleColor,
+ width: (25 * screenHeight) / 480),
+ Image.asset("images/cruise.png",
+ color: (vehicle.isSteeringCruiseEnable)
+ ? (vehicle.isSteeringCruiseSet)
+ ? Colors.green
+ : Colors.orange
+ : idleColor,
+ width: (20 * screenHeight) / 480),
+ ],
+ );
+ }
+}
diff --git a/lib/screen/widgets/turn_signal.dart b/lib/screen/widgets/turn_signal.dart
new file mode 100644
index 0000000..a447cbe
--- /dev/null
+++ b/lib/screen/widgets/turn_signal.dart
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+
+class TurnSignal extends HookConsumerWidget {
+ final double screenHeight;
+ final bool isLefton;
+ final bool isRighton;
+ const TurnSignal({
+ Key? key,
+ required this.screenHeight,
+ required this.isLefton,
+ required this.isRighton,
+ }) : super(key: key);
+ double calcPadding(double value, double height) {
+ // values wrt to values at 720 height
+ return (value * height) / 720;
+ }
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final animationController = useAnimationController(
+ lowerBound: 0.9,
+ upperBound: 1.1,
+ duration: const Duration(milliseconds: 1000),
+ )..repeat();
+ return AnimatedBuilder(
+ animation: animationController,
+ builder: (context, child) {
+ return Padding(
+ padding: EdgeInsets.fromLTRB(calcPadding(150, screenHeight), 0,
+ calcPadding(150, screenHeight), 0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Image.asset(
+ "images/left.png",
+ color: (isLefton)
+ ? Color.lerp(
+ Colors.black,
+ const Color.fromARGB(255, 99, 251, 104),
+ animationController.value.floorToDouble())
+ : const Color.fromARGB(255, 49, 48, 48),
+ width: 0.125 * screenHeight,
+ ),
+ Image.asset(
+ "images/right.png",
+ color: (isRighton)
+ ? Color.lerp(
+ Colors.black,
+ const Color.fromARGB(255, 99, 251, 104),
+ animationController.value.floorToDouble())
+ : const Color.fromARGB(255, 49, 48, 48),
+ width: 0.125 * screenHeight,
+ ),
+ ],
+ ),
+ );
+ });
+ }
+}
diff --git a/lib/vehicle_signal/initial_socket_connection.dart b/lib/vehicle_signal/initial_socket_connection.dart
new file mode 100644
index 0000000..6b9305a
--- /dev/null
+++ b/lib/vehicle_signal/initial_socket_connection.dart
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:io';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/listen_stream.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_config.dart';
+
+class InitialScreen extends ConsumerWidget {
+ InitialScreen({Key? key, required this.client}) : super(key: key);
+ final HttpClient client;
+ late WebSocket socket;
+
+ @override
+ Widget build(BuildContext context, ref) {
+ final sockConnect = ref.watch(sockConnectprovider(client));
+
+ return sockConnect.when(
+ data: (socket) {
+ this.socket = socket;
+ this.socket.pingInterval = const Duration(seconds: 2);
+ return OnBoardingPage(client: client, socket: this.socket);
+ },
+ error: (e, stk) {
+ print(e);
+ Future.delayed(const Duration(milliseconds: 700), (() {
+ ref.refresh(sockConnectprovider(client));
+ }));
+ return const Scaffold(
+ backgroundColor: Colors.black,
+ body: NoticeWidget(
+ assetImageName: "images/server_error.png",
+ text1: "Server Unavailable",
+ text2: "Retrying to conncect!",
+ ),
+ );
+ },
+ loading: () => const Scaffold(
+ backgroundColor: Colors.black,
+ body: NoticeWidget(
+ assetImageName: "images/server.png",
+ text1: "Hi!",
+ text2: "Connecting...!",
+ ),
+ ),
+ );
+ }
+}
+
+class NoticeWidget extends StatelessWidget {
+ const NoticeWidget({
+ Key? key,
+ required this.assetImageName,
+ required this.text1,
+ required this.text2,
+ this.loadingColor,
+ }) : super(key: key);
+
+ final String assetImageName;
+ final String text1;
+ final String text2;
+ final Color? loadingColor;
+
+ @override
+ Widget build(BuildContext context) {
+ return LoadingContainer(
+ child: Flex(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ direction: Axis.vertical,
+ children: [
+ Flexible(
+ child: SizedBox(
+ height: 100,
+ child:
+ Image(image: AssetImage(assetImageName), fit: BoxFit.fitWidth),
+ ),
+ ),
+ Flexible(
+ child: Text(text1,
+ style: const TextStyle(fontWeight: FontWeight.bold))),
+ Flexible(
+ child: Text(text2,
+ style: const TextStyle(fontWeight: FontWeight.bold))),
+ Flexible(
+ child: Padding(
+ padding: const EdgeInsets.fromLTRB(25, 0, 25, 20),
+ child: LinearProgressIndicator(color: loadingColor ?? Colors.red),
+ ),
+ )
+ ],
+ ));
+ }
+}
+
+class LoadingContainer extends StatelessWidget {
+ const LoadingContainer({Key? key, required this.child}) : super(key: key);
+ final Widget child;
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Center(
+ child: Container(
+ width: MediaQuery.of(context).size.width / 2,
+ height: MediaQuery.of(context).size.height * 3 / 4,
+ decoration: BoxDecoration(
+ color: Colors.white, borderRadius: BorderRadius.circular(40)),
+ child: child,
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/vehicle_signal/listen_stream.dart b/lib/vehicle_signal/listen_stream.dart
new file mode 100644
index 0000000..0e2772d
--- /dev/null
+++ b/lib/vehicle_signal/listen_stream.dart
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:async';
+import 'dart:io';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_cluster_dashboard/screen/home.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_config.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_methods.dart';
+
+class OnBoardingPage extends ConsumerStatefulWidget {
+ const OnBoardingPage({Key? key, required this.client, required this.socket})
+ : super(key: key);
+ final WebSocket socket;
+ final HttpClient client;
+
+ @override
+ ConsumerState<OnBoardingPage> createState() => _OnBoardingPageState();
+}
+
+class _OnBoardingPageState extends ConsumerState<OnBoardingPage> {
+ late Timer _timer;
+
+ @override
+ void initState() {
+ super.initState();
+ VISS.init(widget.socket, ref);
+ _timer = Timer.periodic(const Duration(seconds: 2), (timer) {
+ if (widget.socket.readyState == 3) {
+ ref.refresh(sockConnectprovider(widget.client));
+ }
+ });
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+ widget.socket.listen(
+ (data) {
+ // print(data);
+ VISS.parseData(ref, data);
+ },
+ onError: (e, stk) {
+ print(e.toString());
+ ref.refresh(sockConnectprovider(widget.client));
+ },
+ );
+ });
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ _timer.cancel();
+ widget.socket.close(786887, "Connection lost with server!");
+ }
+
+ @override
+ Widget build(BuildContext context) => const Home();
+}
diff --git a/lib/vehicle_signal/vehicle_signal_config.dart b/lib/vehicle_signal/vehicle_signal_config.dart
new file mode 100644
index 0000000..3f0ff1d
--- /dev/null
+++ b/lib/vehicle_signal/vehicle_signal_config.dart
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:io';
+import 'package:flutter/services.dart';
+import 'package:flutter_cluster_dashboard/cluster_config.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+final sockConnectprovider = FutureProvider.family<WebSocket, HttpClient>(
+ (ref, client) => connect(client, ref));
+
+// load certificates and set context and returns http client
+Future<HttpClient> initializeClient() async {
+ ByteData dataCA = await rootBundle.load('assets/cert/CA.pem');
+ ByteData dataCert = await rootBundle.load('assets/cert/Client.pem');
+ ByteData dataKey = await rootBundle.load('assets/cert/Client.key');
+
+ SecurityContext ctx = SecurityContext.defaultContext;
+ ctx.useCertificateChainBytes(dataCert.buffer.asUint8List());
+ ctx.usePrivateKeyBytes(dataKey.buffer.asUint8List());
+ ctx.setTrustedCertificatesBytes(dataCA.buffer.asUint8List());
+ HttpClient client = HttpClient(context: ctx)
+ ..findProxy = null
+ ..badCertificateCallback = (cert, host, port) {
+ return true;
+ };
+ return client;
+}
+
+Future<WebSocket> connect(HttpClient client, ref) async {
+ final config = ref.read(clusterConfigStateprovider);
+ WebSocket socket = await WebSocket.connect(
+ "wss://${config.hostname}:${config.port}",
+ customClient: client);
+ return socket;
+}
diff --git a/lib/vehicle_signal/vehicle_signal_methods.dart b/lib/vehicle_signal/vehicle_signal_methods.dart
new file mode 100644
index 0000000..21341c2
--- /dev/null
+++ b/lib/vehicle_signal/vehicle_signal_methods.dart
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'dart:convert';
+import 'dart:io';
+import 'package:flutter_cluster_dashboard/cluster_config.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_cluster_dashboard/map/networkPolyline.dart';
+import 'package:flutter_cluster_dashboard/provider.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_path.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_provider.dart';
+import 'package:latlong2/latlong.dart';
+
+class VISS {
+ static const requestId = "flutter-cluster-app";
+ static void init(WebSocket socket, WidgetRef ref) {
+ authorize(socket, ref);
+ subscribe(socket, ref, VSPath.vehicleSpeed);
+ subscribe(socket, ref, VSPath.vehicleEngineRPM);
+ subscribe(socket, ref, VSPath.vehicleLeftIndicator);
+ subscribe(socket, ref, VSPath.vehicleRightIndicator);
+ subscribe(socket, ref, VSPath.vehicleFuelLevel);
+ subscribe(socket, ref, VSPath.vehicleCoolantTemp);
+ subscribe(socket, ref, VSPath.vehicleHazardLightOn);
+ subscribe(socket, ref, VSPath.vehicleHighBeamOn);
+ subscribe(socket, ref, VSPath.vehicleLowBeamOn);
+ subscribe(socket, ref, VSPath.vehicleSelectedGear);
+ subscribe(socket, ref, VSPath.vehiclePerformanceMode);
+ subscribe(socket, ref, VSPath.vehicleAmbientAirTemperature);
+ subscribe(socket, ref, VSPath.vehicleParkingLightOn);
+ subscribe(socket, ref, VSPath.vehicleTrunkLocked);
+ subscribe(socket, ref, VSPath.vehicleTrunkOpen);
+ subscribe(socket, ref, VSPath.vehicleAmbientAirTemperature);
+ subscribe(socket, ref, VSPath.vehicleMIL);
+ subscribe(socket, ref, VSPath.vehicleCruiseControlError);
+ subscribe(socket, ref, VSPath.vehicleCruiseControlSpeedSet);
+ subscribe(socket, ref, VSPath.vehicleCruiseControlSpeedisActive);
+ subscribe(socket, ref, VSPath.vehicleBatteryChargingStatus);
+
+ //
+ subscribe(socket, ref, VSPath.steeringCruiseEnable);
+ subscribe(socket, ref, VSPath.steeringCruiseSet);
+ subscribe(socket, ref, VSPath.steeringCruiseResume);
+ subscribe(socket, ref, VSPath.steeringCruiseCancel);
+ subscribe(socket, ref, VSPath.steeringInfo);
+ subscribe(socket, ref, VSPath.steeringLaneDepWarn);
+ subscribe(socket, ref, VSPath.vehicleDistanceUnit);
+ //
+ subscribe(socket, ref, VSPath.vehicleCurrLat);
+ subscribe(socket, ref, VSPath.vehicleCurrLng);
+ subscribe(socket, ref, VSPath.vehicleDesLat);
+ subscribe(socket, ref, VSPath.vehicleDesLng);
+
+ update(socket, ref);
+ }
+
+ static void update(WebSocket socket, WidgetRef ref) {
+ get(socket, ref, VSPath.vehicleSpeed);
+ get(socket, ref, VSPath.vehicleEngineRPM);
+ get(socket, ref, VSPath.vehicleLeftIndicator);
+ get(socket, ref, VSPath.vehicleRightIndicator);
+ get(socket, ref, VSPath.vehicleFuelLevel);
+ get(socket, ref, VSPath.vehicleCoolantTemp);
+ get(socket, ref, VSPath.vehicleHazardLightOn);
+ get(socket, ref, VSPath.vehicleHighBeamOn);
+ get(socket, ref, VSPath.vehicleLowBeamOn);
+ get(socket, ref, VSPath.vehicleSelectedGear);
+ get(socket, ref, VSPath.vehiclePerformanceMode);
+ get(socket, ref, VSPath.vehicleAmbientAirTemperature);
+ get(socket, ref, VSPath.vehicleParkingLightOn);
+ get(socket, ref, VSPath.vehicleTrunkLocked);
+ get(socket, ref, VSPath.vehicleTrunkOpen);
+ get(socket, ref, VSPath.vehicleAmbientAirTemperature);
+ get(socket, ref, VSPath.vehicleMIL);
+ get(socket, ref, VSPath.vehicleCruiseControlError);
+ get(socket, ref, VSPath.vehicleCruiseControlSpeedSet);
+ get(socket, ref, VSPath.vehicleCruiseControlSpeedisActive);
+ get(socket, ref, VSPath.vehicleBatteryChargingStatus);
+ get(socket, ref, VSPath.vehicleDistanceUnit);
+ //
+ get(socket, ref, VSPath.vehicleCurrLat);
+ get(socket, ref, VSPath.vehicleCurrLng);
+ get(socket, ref, VSPath.vehicleDesLat);
+ get(socket, ref, VSPath.vehicleDesLng);
+ }
+
+ static void authorize(WebSocket socket, WidgetRef ref) {
+ final config = ref.read(clusterConfigStateprovider);
+ Map<String, dynamic> map = {
+ "action": "authorize",
+ "tokens": config.kuksaAuthToken,
+ "requestId": requestId
+ };
+ socket.add(jsonEncode(map));
+ }
+
+ static void get(WebSocket socket, WidgetRef ref, String path) {
+ final config = ref.read(clusterConfigStateprovider);
+ Map<String, dynamic> map = {
+ "action": "get",
+ "tokens": config.kuksaAuthToken,
+ "path": path,
+ "requestId": requestId
+ };
+ socket.add(jsonEncode(map));
+ }
+
+ static void set(WebSocket socket, WidgetRef ref, String path, String value) {
+ final config = ref.read(clusterConfigStateprovider);
+ Map<String, dynamic> map = {
+ "action": "set",
+ "tokens": config.kuksaAuthToken,
+ "path": path,
+ "requestId": requestId,
+ "value": value
+ };
+ socket.add(jsonEncode(map));
+ }
+
+ static void subscribe(WebSocket socket, WidgetRef ref, String path) {
+ final config = ref.read(clusterConfigStateprovider);
+ Map<String, dynamic> map = {
+ "action": "subscribe",
+ "tokens": config.kuksaAuthToken,
+ "path": path,
+ "requestId": requestId
+ };
+ socket.add(jsonEncode(map));
+ }
+
+ static String? numToGear(int? number) {
+ switch (number) {
+ case -1:
+ return 'R';
+ case 0:
+ return 'N';
+ case 126:
+ return 'P';
+ case 127:
+ return 'D';
+ default:
+ return null;
+ }
+ }
+
+ static void parseData(WidgetRef ref, String data) {
+ final vehicleSignal = ref.read(vehicleSignalProvider.notifier);
+ final polylineDBNotifier = ref.read(polyLineStateProvider.notifier);
+ Map<String, dynamic> dataMap = jsonDecode(data);
+ if (dataMap["action"] == "subscription" || dataMap["action"] == "get") {
+ if (dataMap.containsKey("data")) {
+ if ((dataMap["data"] as Map<String, dynamic>).containsKey("dp") &&
+ (dataMap["data"] as Map<String, dynamic>).containsKey("path")) {
+ String path = dataMap["data"]["path"];
+ Map<String, dynamic> dp = dataMap["data"]["dp"];
+ if (dp.containsKey("value")) {
+ if (dp["value"] != "---") {
+ switch (path) {
+ case VSPath.vehicleSpeed:
+ vehicleSignal.update(speed: double.parse(dp["value"]));
+ break;
+ case VSPath.vehicleEngineRPM:
+ vehicleSignal.update(rpm: double.parse(dp["value"]));
+ break;
+ case VSPath.vehicleFuelLevel:
+ vehicleSignal.update(fuelLevel: double.parse(dp["value"]));
+ break;
+ case VSPath.vehicleCoolantTemp:
+ vehicleSignal.update(coolantTemp: double.parse(dp["value"]));
+ break;
+ case VSPath.vehicleLeftIndicator:
+ vehicleSignal.update(isLeftIndicator: dp["value"]);
+ break;
+ case VSPath.vehicleRightIndicator:
+ vehicleSignal.update(isRightIndicator: dp["value"]);
+ break;
+ case VSPath.vehicleHighBeamOn:
+ if (dp["value"]) {
+ vehicleSignal.update(isHighBeam: true);
+ vehicleSignal.update(isLowBeam: false);
+ } else {
+ vehicleSignal.update(isHighBeam: dp["value"]);
+ }
+ break;
+ case VSPath.vehicleParkingLightOn:
+ vehicleSignal.update(isParkingOn: dp["value"]);
+ break;
+ case VSPath.vehicleLowBeamOn:
+ if (dp["value"]) {
+ vehicleSignal.update(isHighBeam: false);
+ vehicleSignal.update(isLowBeam: true);
+ } else {
+ vehicleSignal.update(isLowBeam: dp["value"]);
+ }
+ break;
+ case VSPath.vehicleHazardLightOn:
+ vehicleSignal.update(isHazardLightOn: dp["value"]);
+ break;
+ case VSPath.vehicleSelectedGear:
+ vehicleSignal.update(
+ selectedGear: numToGear(int.parse(dp["value"])));
+ break;
+ case VSPath.vehiclePerformanceMode:
+ vehicleSignal.update(performanceMode: dp['value']);
+ break;
+ case VSPath.vehicleTravelledDistance:
+ vehicleSignal.update(travelledDistance: dp['value']);
+ break;
+ case VSPath.vehicleTrunkLocked:
+ vehicleSignal.update(isTrunkLocked: dp['value']);
+ break;
+ case VSPath.vehicleTrunkOpen:
+ vehicleSignal.update(isTrunkOpen: dp['value']);
+ break;
+ case VSPath.vehicleAmbientAirTemperature:
+ vehicleSignal.update(ambientAirTemp: dp['value']);
+ break;
+ case VSPath.vehicleMIL:
+ vehicleSignal.update(isMILon: dp['value']);
+ break;
+ case VSPath.vehicleCruiseControlError:
+ vehicleSignal.update(isCruiseControlError: dp['value']);
+ break;
+ case VSPath.vehicleCruiseControlSpeedSet:
+ vehicleSignal.update(
+ cruiseControlSpeed: double.parse(dp['value']));
+ break;
+ case VSPath.vehicleCruiseControlSpeedisActive:
+ vehicleSignal.update(isCruiseControlActive: dp['value']);
+ break;
+ case VSPath.vehicleBatteryChargingStatus:
+ vehicleSignal.update(isBatteryCharging: dp['value']);
+ break;
+ //
+ case VSPath.steeringCruiseEnable:
+ if (dp['value']) {
+ if (vehicleSignal.state.isSteeringCruiseEnable) {
+ vehicleSignal.update(isSteeringCruiseEnable: false);
+ vehicleSignal.update(isSteeringCruiseSet: false);
+ } else {
+ vehicleSignal.update(isSteeringCruiseEnable: dp['value']);
+ }
+ }
+ break;
+ case VSPath.steeringCruiseSet:
+ if (dp['value'] &&
+ vehicleSignal.state.isSteeringCruiseEnable) {
+ vehicleSignal.update(isSteeringCruiseSet: dp['value']);
+ }
+ break;
+ case VSPath.steeringCruiseResume:
+ if (dp['value'] &&
+ vehicleSignal.state.isSteeringCruiseEnable) {
+ vehicleSignal.update(isSteeringCruiseSet: dp['value']);
+ }
+ break;
+ case VSPath.steeringCruiseCancel:
+ if (dp['value']) {
+ vehicleSignal.update(isSteeringCruiseSet: false);
+ }
+ break;
+ case VSPath.steeringInfo:
+ if (dp['value']) {
+ vehicleSignal.update(
+ isSteeringInfo: !vehicleSignal.state.isSteeringInfo);
+ }
+ break;
+ case VSPath.steeringLaneDepWarn:
+ if (dp['value']) {
+ vehicleSignal.update(
+ isSteeringLaneWarning:
+ !(vehicleSignal.state.isSteeringLaneWarning));
+ }
+ break;
+ case VSPath.vehicleDistanceUnit:
+ vehicleSignal.update(vehicleDistanceUnit: dp['value']);
+ break;
+ //
+ case VSPath.vehicleCurrLat:
+ vehicleSignal.update(currLat: double.parse(dp['value']));
+ break;
+ case VSPath.vehicleCurrLng:
+ vehicleSignal.update(currLng: double.parse(dp['value']));
+ break;
+ case VSPath.vehicleDesLat:
+ vehicleSignal.update(desLat: double.parse(dp['value']));
+ polylineDBNotifier.update(currPolyLineList: []);
+
+ break;
+ case VSPath.vehicleDesLng:
+ vehicleSignal.update(desLng: double.parse(dp['value']));
+ polylineDBNotifier.update(currPolyLineList: []);
+ break;
+ default:
+ print("$path Not Available yet!");
+ }
+ } else {
+ print("ERROR:Value not available yet! Set Value of $path");
+ }
+ } else {
+ print("ERROR:'value': Key not found!");
+ }
+ } else if ((!dataMap["data"] as Map<String, dynamic>)
+ .containsKey("path")) {
+ print("ERROR:'path':key not found !");
+ } else if ((dataMap["data"] as Map<String, dynamic>)
+ .containsKey("dp")) {
+ print("ERROR:'dp':key not found !");
+ }
+ } else {
+ print("ERROR:'data':key not found!");
+ }
+ }
+ }
+}
+
+void updatePolyline(WidgetRef ref, vehicleSignal, polyLineState) {
+ getJsonData(ref, vehicleSignal.state.currLat, vehicleSignal.state.currLng,
+ vehicleSignal.state.desLat, vehicleSignal.state.desLng)
+ .then((polylineList) {
+ polyLineState.update(
+ currPolyLineList: polylineList
+ .map((element) => LatLng(element[1], element[0]))
+ .toList());
+ });
+}
diff --git a/lib/vehicle_signal/vehicle_signal_model.dart b/lib/vehicle_signal/vehicle_signal_model.dart
new file mode 100644
index 0000000..90f76e7
--- /dev/null
+++ b/lib/vehicle_signal/vehicle_signal_model.dart
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: Apache-2.0
+
+class VehicleSignal {
+ VehicleSignal({
+ required this.speed,
+ required this.rpm,
+ required this.fuelLevel,
+ required this.coolantTemp,
+ required this.isLeftIndicator,
+ required this.isRightIndicator,
+ required this.selectedGear,
+ required this.isLowBeam,
+ required this.isHighBeam,
+ required this.isHazardLightOn,
+ required this.travelledDistance,
+ required this.isParkingOn,
+ required this.performanceMode,
+ required this.ambientAirTemp,
+ required this.cruiseControlSpeed,
+ required this.isCruiseControlActive,
+ required this.isCruiseControlError,
+ required this.isMILon,
+ required this.isTrunkLocked,
+ required this.isTrunkOpen,
+ required this.isBatteryCharging,
+ //steering switches
+ required this.vehicleDistanceUnit,
+ required this.isSteeringCruiseEnable,
+ required this.isSteeringCruiseSet,
+ required this.isSteeringCruiseResume,
+ required this.isSteeringCruiseCancel,
+ required this.isSteeringLaneWarning,
+ required this.isSteeringInfo,
+ // map coordinates
+ required this.currLat,
+ required this.currLng,
+ required this.desLat,
+ required this.desLng,
+ });
+ final double speed;
+ final double rpm;
+ final double fuelLevel;
+ final double coolantTemp;
+ final double cruiseControlSpeed;
+ final bool isLeftIndicator;
+ final bool isRightIndicator;
+ final String selectedGear;
+ final String performanceMode;
+ final String ambientAirTemp;
+ final bool isLowBeam;
+ final bool isHighBeam;
+ final bool isParkingOn;
+ final bool isHazardLightOn;
+ final bool isTrunkOpen;
+ final bool isTrunkLocked;
+ final bool isMILon;
+ final bool isCruiseControlActive;
+ final bool isCruiseControlError;
+ final bool isBatteryCharging;
+ final double travelledDistance;
+
+ final String vehicleDistanceUnit;
+ final bool isSteeringCruiseEnable;
+ final bool isSteeringCruiseSet;
+ final bool isSteeringCruiseResume;
+ final bool isSteeringCruiseCancel;
+ final bool isSteeringLaneWarning;
+ final bool isSteeringInfo;
+
+ final double currLat;
+ final double currLng;
+ final double desLat;
+ final double desLng;
+
+ VehicleSignal copyWith({
+ double? speed,
+ double? rpm,
+ double? fuelLevel,
+ double? coolantTemp,
+ bool? isLeftIndicator,
+ bool? isRightIndicator,
+ String? selectedGear,
+ String? performanceMode,
+ String? ambientAirTemp,
+ bool? isLowBeam,
+ bool? isHighBeam,
+ bool? isHazardLightOn,
+ bool? isParkingOn,
+ bool? isTrunkLocked,
+ bool? isTrunkOpen,
+ bool? isMILon,
+ bool? isCruiseControlError,
+ bool? isCruiseControlActive,
+ bool? isBatteryCharging,
+ double? travelledDistance,
+ double? cruiseControlSpeed,
+ // Steering
+ String? vehicleDistanceUnit,
+ bool? isSteeringCruiseEnable,
+ bool? isSteeringCruiseSet,
+ bool? isSteeringCruiseResume,
+ bool? isSteeringCruiseCancel,
+ bool? isSteeringLaneWarning,
+ bool? isSteeringInfo,
+ // map coordinates
+ double? currLat,
+ double? currLng,
+ double? desLat,
+ double? desLng,
+ }) {
+ return VehicleSignal(
+ speed: speed ?? (this.speed),
+ rpm: rpm ?? this.rpm,
+ fuelLevel: fuelLevel ?? this.fuelLevel,
+ coolantTemp: coolantTemp ?? this.coolantTemp,
+ isLeftIndicator: isLeftIndicator ?? this.isLeftIndicator,
+ isRightIndicator: isRightIndicator ?? this.isRightIndicator,
+ selectedGear: selectedGear ?? this.selectedGear,
+ isLowBeam: isLowBeam ?? this.isLowBeam,
+ isHighBeam: isHighBeam ?? this.isHighBeam,
+ isHazardLightOn: isHazardLightOn ?? this.isHazardLightOn,
+ travelledDistance: travelledDistance ?? this.travelledDistance,
+ isParkingOn: isParkingOn ?? this.isParkingOn,
+ performanceMode: performanceMode ?? this.performanceMode,
+ isTrunkLocked: isTrunkLocked ?? this.isTrunkLocked,
+ isTrunkOpen: isTrunkOpen ?? this.isTrunkOpen,
+ ambientAirTemp: ambientAirTemp ?? this.ambientAirTemp,
+ isMILon: isMILon ?? this.isMILon,
+ isCruiseControlActive:
+ isCruiseControlActive ?? this.isCruiseControlActive,
+ cruiseControlSpeed: cruiseControlSpeed ?? this.cruiseControlSpeed,
+ isCruiseControlError: isCruiseControlError ?? this.isCruiseControlError,
+ isBatteryCharging: isBatteryCharging ?? this.isBatteryCharging,
+ isSteeringCruiseEnable:
+ isSteeringCruiseEnable ?? this.isSteeringCruiseEnable,
+ isSteeringCruiseSet: isSteeringCruiseSet ?? this.isSteeringCruiseSet,
+ isSteeringCruiseResume:
+ isSteeringCruiseResume ?? this.isSteeringCruiseResume,
+ isSteeringCruiseCancel:
+ isSteeringCruiseCancel ?? this.isSteeringCruiseCancel,
+ isSteeringInfo: isSteeringInfo ?? this.isSteeringInfo,
+ isSteeringLaneWarning:
+ isSteeringLaneWarning ?? this.isSteeringLaneWarning,
+ vehicleDistanceUnit: vehicleDistanceUnit ?? this.vehicleDistanceUnit,
+ //
+ currLat: currLat ?? this.currLat,
+ currLng: currLng ?? this.currLng,
+ desLat: desLat ?? this.desLat,
+ desLng: desLng ?? this.desLng,
+ );
+ }
+}
diff --git a/lib/vehicle_signal/vehicle_signal_path.dart b/lib/vehicle_signal/vehicle_signal_path.dart
new file mode 100644
index 0000000..72b016d
--- /dev/null
+++ b/lib/vehicle_signal/vehicle_signal_path.dart
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: Apache-2.0
+
+class VSPath {
+ static const String vehicleSpeed = "Vehicle.Speed";
+ static const String vehicleEngineRPM =
+ "Vehicle.Powertrain.CombustionEngine.Engine.Speed";
+ static const String vehicleFuelLevel = "Vehicle.Powertrain.FuelSystem.Level";
+ static const String vehicleCoolantTemp =
+ "Vehicle.Powertrain.CombustionEngine.Engine.ECT";
+ static const String vehicleLeftIndicator =
+ "Vehicle.Body.Lights.IsLeftIndicatorOn";
+ static const String vehicleRightIndicator =
+ "Vehicle.Body.Lights.IsRightIndicatorOn";
+ //Selected Gear output=> 0=Neutral, 1/2/..=Forward, -1/..=Reverse, 126=Park, 127=Drive
+ static const String vehicleSelectedGear =
+ "Vehicle.Powertrain.Transmission.SelectedGear";
+ static const String vehicleLowBeamOn = "Vehicle.Body.Lights.IsLowBeamOn";
+ static const String vehicleHighBeamOn = "Vehicle.Body.Lights.IsHighBeamOn";
+ static const String vehicleParkingLightOn = "Vehicle.Body.Lights.IsParkingOn";
+ static const String vehicleHazardLightOn = "Vehicle.Body.Lights.IsHazardOn";
+ static const String vehicleTravelledDistance = "Vehicle.TravelledDistance";
+ static const String vehicleTrunkLocked = "Vehicle.Body.Trunk.IsLocked";
+ static const String vehicleTrunkOpen = "Vehicle.Body.Trunk.IsOpen";
+ // \"normal\",\"sport\",\"economy\",\"snow\",\"rain\"]
+ static const String vehiclePerformanceMode =
+ "Vehicle.Powertrain.Transmission.PerformanceMode";
+ static const String vehicleAmbientAirTemperature =
+ "Vehicle.AmbientAirTemperature";
+ static const String vehicleMIL = "Vehicle.OBD.Status.MIL";
+ static const String vehicleCruiseControlError =
+ "Vehicle.ADAS.CruiseControl.Error";
+ static const String vehicleCruiseControlSpeedSet =
+ "Vehicle.ADAS.CruiseControl.SpeedSet";
+ static const String vehicleCruiseControlSpeedisActive =
+ "Vehicle.ADAS.CruiseControl.IsActive";
+ static const String vehicleBatteryChargingStatus =
+ "Vehicle.Powertrain.Battery.Charging.Status";
+
+ static const String steeringCruiseEnable =
+ "Vehicle.Cabin.SteeringWheel.Switches.CruiseEnable";
+ static const String steeringCruiseSet =
+ "Vehicle.Cabin.SteeringWheel.Switches.CruiseSet";
+ static const String steeringCruiseResume =
+ "Vehicle.Cabin.SteeringWheel.Switches.CruiseResume";
+ static const String steeringCruiseCancel =
+ "Vehicle.Cabin.SteeringWheel.Switches.CruiseCancel";
+ static const String steeringLaneDepWarn =
+ "Vehicle.Cabin.SteeringWheel.Switches.LaneDepartureWarning";
+ static const String steeringInfo =
+ "Vehicle.Cabin.SteeringWheel.Switches.Info";
+ static const String vehicleDistanceUnit =
+ "Vehicle.Cabin.Infotainment.HMI.DistanceUnit";
+
+ static const String vehicleCurrLat =
+ "Vehicle.Cabin.Infotainment.Navigation.CurrentLocation.Latitude";
+ static const String vehicleCurrLng =
+ "Vehicle.Cabin.Infotainment.Navigation.CurrentLocation.Longitude";
+ static const String vehicleDesLat =
+ "Vehicle.Cabin.Infotainment.Navigation.DestinationSet.Latitude";
+ static const String vehicleDesLng =
+ "Vehicle.Cabin.Infotainment.Navigation.DestinationSet.Longitude";
+}
diff --git a/lib/vehicle_signal/vehicle_signal_provider.dart b/lib/vehicle_signal/vehicle_signal_provider.dart
new file mode 100644
index 0000000..e75afff
--- /dev/null
+++ b/lib/vehicle_signal/vehicle_signal_provider.dart
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: Apache-2.0
+
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_cluster_dashboard/vehicle_signal/vehicle_signal_model.dart';
+
+final vehicleSignalProvider =
+ StateNotifierProvider<VehicleSignalNotifier, VehicleSignal>(
+ (ref) => VehicleSignalNotifier(),
+);
+
+class VehicleSignalNotifier extends StateNotifier<VehicleSignal> {
+ VehicleSignalNotifier() : super(_initialValue);
+ static final VehicleSignal _initialValue = VehicleSignal(
+ speed: 140,
+ rpm: 7000,
+ fuelLevel: 90,
+ coolantTemp: 90,
+ isLeftIndicator: false,
+ isRightIndicator: false,
+ selectedGear: "P",
+ performanceMode: "normal",
+ isHazardLightOn: false,
+ isHighBeam: true,
+ isLowBeam: false,
+ isParkingOn: true,
+ travelledDistance: 888,
+ ambientAirTemp: '25',
+ cruiseControlSpeed: 60,
+ isCruiseControlActive: false,
+ isCruiseControlError: false,
+ isMILon: false,
+ isTrunkLocked: true,
+ isTrunkOpen: false,
+ isBatteryCharging: true,
+ isSteeringCruiseEnable: false,
+ isSteeringCruiseSet: false,
+ isSteeringCruiseResume: false,
+ isSteeringCruiseCancel: false,
+ isSteeringInfo: false,
+ isSteeringLaneWarning: false,
+ vehicleDistanceUnit: 'km',
+ currLat: 31.71,
+ currLng: 76.95,
+ desLat: 31.71,
+ desLng: 76.95,
+ );
+ void update({
+ double? speed,
+ double? rpm,
+ double? fuelLevel,
+ double? coolantTemp,
+ bool? isLeftIndicator,
+ bool? isRightIndicator,
+ String? selectedGear,
+ String? performanceMode,
+ String? ambientAirTemp,
+ bool? isLowBeam,
+ bool? isHighBeam,
+ bool? isHazardLightOn,
+ bool? isMILon,
+ bool? isParkingOn,
+ bool? isTrunkOpen,
+ bool? isTrunkLocked,
+ bool? isCruiseControlActive,
+ bool? isCruiseControlError,
+ bool? isBatteryCharging,
+ double? travelledDistance,
+ double? cruiseControlSpeed,
+ //
+ String? vehicleDistanceUnit,
+ bool? isSteeringCruiseEnable,
+ bool? isSteeringCruiseSet,
+ bool? isSteeringCruiseResume,
+ bool? isSteeringCruiseCancel,
+ bool? isSteeringLaneWarning,
+ bool? isSteeringInfo,
+ //
+ double? currLat,
+ double? currLng,
+ double? desLat,
+ double? desLng,
+ }) {
+ state = state.copyWith(
+ speed: speed,
+ rpm: rpm,
+ fuelLevel: fuelLevel,
+ coolantTemp: coolantTemp,
+ isLeftIndicator: isLeftIndicator,
+ isRightIndicator: isRightIndicator,
+ selectedGear: selectedGear,
+ isLowBeam: isLowBeam,
+ isHighBeam: isHighBeam,
+ isHazardLightOn: isHazardLightOn,
+ travelledDistance: travelledDistance,
+ performanceMode: performanceMode,
+ isParkingOn: isParkingOn,
+ isTrunkOpen: isTrunkOpen,
+ isTrunkLocked: isTrunkLocked,
+ isMILon: isMILon,
+ ambientAirTemp: ambientAirTemp,
+ isCruiseControlActive: isCruiseControlActive,
+ isCruiseControlError: isCruiseControlError,
+ cruiseControlSpeed: cruiseControlSpeed,
+ isBatteryCharging: isBatteryCharging,
+ //
+ isSteeringCruiseEnable: isSteeringCruiseEnable,
+ isSteeringCruiseSet: isSteeringCruiseSet,
+ isSteeringCruiseResume: isSteeringCruiseResume,
+ isSteeringCruiseCancel: isSteeringCruiseCancel,
+ isSteeringInfo: isSteeringInfo,
+ isSteeringLaneWarning: isSteeringLaneWarning,
+ vehicleDistanceUnit: vehicleDistanceUnit,
+ //
+ currLat: currLat,
+ currLng: currLng,
+ desLat: desLat,
+ desLng: desLng,
+ );
+ }
+}
diff --git a/library_Llcense_details.txt b/library_Llcense_details.txt
new file mode 100644
index 0000000..3e806ce
--- /dev/null
+++ b/library_Llcense_details.txt
@@ -0,0 +1,52 @@
+
+Libraries and Dependencies
+================================
+* flutter_riverpod: ^1.0.4 : MIT
+ - collection: BSD-3-Clause
+ - meta :BSD-3-Clause
+ - flutter
+ - state_notifier : MIT
+ - riverpod : MIT
+---------------------------------
+* flutter_hooks: ^0.18.5: MIT
+ - flutter
+---------------------------------
+* hooks_riverpod :MIT
+ - collection
+ - flutter
+ - flutter_hooks
+ - flutter_riverpod
+ - riverpod
+ - state_notifier
+---------------------------------
+* flutter_map :BSD-3-Clause
+ - async : BSD-3-Clause
+ - collection
+ - meta
+ - flutter
+ - http : BSD-3-Clause
+ - http_parser
+ - path : BSD-3-Clause
+ - source_span : BSD-3-Clause
+ - term_glyph : BSD-3-Clause
+ - string_scanner : BSD-3-Clause
+ - typed_data : BSD-3-Clause
+ - latlong2 : Apache-2.0
+ - intl : BSD-3-Clause
+ - clock : Apache-2.0
+ - polylabel : BSD-3-Clause
+ - positioned_tap_detector_2 : MIT
+ - proj4dart : MIT
+ - mgrs_dart : MIT
+ - wkt_parser : MIT
+ - tuple : BSD-3-Clause
+ - quiver : Apache-2.0
+ - matcher : BSD-3-Clause
+ - stack_trace : BSD-3-Clause
+ - vector_math : Zlib, BSD-3-Clause
+---------------------------------
+* yaml: MIT
+ - collection
+ - source_span
+ - string_scanner
+---------------------------------
diff --git a/pubspec.yaml b/pubspec.yaml
new file mode 100644
index 0000000..d6c889e
--- /dev/null
+++ b/pubspec.yaml
@@ -0,0 +1,97 @@
+name: flutter_cluster_dashboard
+
+description: Instrument Cluster demo app using Flutter for AGL
+
+# The following line prevents the package from being accidentally published to
+# pub.dev using `flutter pub publish`. This is preferred for private packages.
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+
+# The following defines the version and build number for your application.
+# A version number is three numbers separated by dots, like 1.2.43
+# followed by an optional build number separated by a +.
+# Both the version and the builder number may be overridden in flutter
+# build by specifying --build-name and --build-number, respectively.
+# In Android, build-name is used as versionName while build-number used as versionCode.
+# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
+# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
+# Read more about iOS versioning at
+# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
+version: 1.0.0+1
+
+# Dart SDK version
+environment:
+ sdk: ">=2.17.0 <3.0.0"
+
+# Dependencies specify other packages that your package needs in order to work.
+# To automatically upgrade your package dependencies to the latest versions
+# consider running `flutter pub upgrade --major-versions`. Alternatively,
+# dependencies can be manually updated by changing the version numbers below to
+# the latest version available on pub.dev. To see which dependencies have newer
+# versions available, run `flutter pub outdated`.
+dependencies:
+ flutter:
+ sdk: flutter
+
+
+ flutter_riverpod: ^1.0.4
+ flutter_hooks: ^0.18.5
+ hooks_riverpod: ^1.0.4
+ flutter_map: ^2.2.0
+ latlong2: ^0.8.1
+ intl: ^0.17.0
+ http: ^0.13.5
+ yaml: ^3.1.1
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+ # The "flutter_lints" package below contains a set of recommended lints to
+ # encourage good coding practices. The lint set provided by the package is
+ # activated in the `analysis_options.yaml` file located at the root of your
+ # package. See that file for information about deactivating specific lint
+ # rules and activating additional ones.
+ flutter_lints: ^2.0.0
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter packages.
+flutter:
+
+ # The following line ensures that the Material Icons font is
+ # included with your application, so that you can use the icons in
+ # the material Icons class.
+ uses-material-design: true
+
+ # To add assets to your application, add an assets section, like this:
+ assets:
+ - assets/cert/
+ - assets/cert/jwt/
+ - images/
+
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/assets-and-images/#resolution-aware
+
+ # For details regarding adding assets from package dependencies, see
+ # https://flutter.dev/assets-and-images/#from-packages
+
+ # To add custom fonts to your application, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts from package dependencies,
+ # see https://flutter.dev/custom-fonts/#from-packages