├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ └── security_report.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .gitmodules
├── .metadata
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ ├── app_openemr
│ │ │ │ └── MainActivity.kt
│ │ │ │ └── openemr_app
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── splash.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ └── values
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
└── settings.gradle
├── img
├── 1.png
├── 2.png
├── 3.png
├── 4.png
├── 5.png
├── 6.png
├── 7.png
├── auth.gif
├── database.gif
├── google_auth.gif
├── ip.gif
└── storage.gif
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
└── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── 1024.png
│ │ ├── 114.png
│ │ ├── 120.png
│ │ ├── 180.png
│ │ ├── 29.png
│ │ ├── 40.png
│ │ ├── 57.png
│ │ ├── 58.png
│ │ ├── 60.png
│ │ ├── 80.png
│ │ ├── 87.png
│ │ └── Contents.json
│ └── LaunchImage.imageset
│ │ ├── Contents.json
│ │ ├── LaunchImage.png
│ │ ├── LaunchImage@2x.png
│ │ ├── LaunchImage@3x.png
│ │ └── README.md
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ └── Runner-Bridging-Header.h
├── lib
├── assets
│ ├── fonts
│ │ ├── gfFontIcon.ttf
│ │ ├── gfFontIcons2.ttf
│ │ ├── gfIconFonts.ttf
│ │ ├── gfSocialFonts.ttf
│ │ └── loader.ttf
│ ├── gif
│ │ ├── loader.gif
│ │ ├── loader1.gif
│ │ └── success1.gif
│ ├── icons
│ │ └── gflogo.png
│ └── images
│ │ ├── avatar.png
│ │ ├── avatar1.png
│ │ ├── avatar10.png
│ │ ├── avatar11.png
│ │ ├── avatar12.png
│ │ ├── avatar2.png
│ │ ├── avatar3.png
│ │ ├── avatar4.png
│ │ ├── avatar5.png
│ │ ├── avatar6.png
│ │ ├── avatar7.png
│ │ ├── avatar8.png
│ │ ├── avatar9.png
│ │ ├── card.png
│ │ ├── card1.png
│ │ ├── card2.png
│ │ ├── card3.png
│ │ ├── card4.png
│ │ ├── card5.png
│ │ ├── firebase.png
│ │ ├── gflogo.png
│ │ ├── image.png
│ │ ├── image1.png
│ │ ├── image2.png
│ │ ├── img.png
│ │ ├── img1.png
│ │ ├── img2.png
│ │ ├── logo.png
│ │ ├── orange.png
│ │ ├── pink.png
│ │ ├── purple.png
│ │ └── red.png
├── const
│ └── strings.dart
├── main.dart
├── models
│ ├── patient.dart
│ └── user.dart
├── screens
│ ├── addpatient
│ │ ├── add_patient.dart
│ │ └── local_widgets
│ │ │ └── custom_dropdown_field.dart
│ ├── codescanner
│ │ └── codescanner.dart
│ ├── drawer
│ │ ├── drawer.dart
│ │ └── webview.dart
│ ├── home.dart
│ ├── login
│ │ ├── create_account.dart
│ │ ├── login.dart
│ │ └── login2.dart
│ ├── medicine
│ │ └── medicine_recognition_ML_Kit.dart
│ ├── patientList
│ │ └── patient_list.dart
│ ├── ppg
│ │ ├── chart.dart
│ │ └── heartRate.dart
│ ├── register
│ │ └── register.dart
│ ├── shimmer
│ │ └── shimmer.dart
│ └── telehealth
│ │ ├── chat.dart
│ │ ├── local_widgets
│ │ └── profileShimmer.dart
│ │ ├── profile.dart
│ │ ├── signaling.dart
│ │ └── telehealth.dart
└── utils
│ ├── common.dart
│ ├── customlistloadingshimmer.dart
│ ├── network.dart
│ ├── rest_ds.dart
│ ├── system_padding.dart
│ └── websocket.dart
├── pubspec.lock
├── pubspec.yaml
└── test
└── widget_test.dart
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Report any bugs present in the code
4 | ---
5 |
6 | **Expected Behavior**
7 |
8 | **Actual Behavior**
9 |
10 | **Steps to Reproduce the Problem**
11 |
12 | **Screenshots/Video showcasing the issue**
13 |
14 | **What might be causing this behavior and your solution to it**
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | about: Suggest a feature for the Project
4 | ---
5 |
6 | **Describe the Feature**
7 |
8 |
9 |
10 | **Need of this Feature**
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/security_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Security Report
3 | about: Privately report a Security Vulnerability
4 | ---
5 |
6 | **Security Report**
7 |
8 |
12 |
13 |
15 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | **IMPORTANT: Please do not create a Pull Request without creating an issue first.**
2 |
3 |
4 |
5 | **Description**
6 |
7 |
8 |
9 | **Testing Methods**
10 |
11 |
12 |
13 | **Screenshots/Videos**
14 |
15 |
16 |
17 | **New Packages Added**
18 |
19 |
20 |
21 | **Closing Issues**
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .flutter-plugins-dependencies
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | /build/
32 |
33 | # Web related
34 | lib/generated_plugin_registrant.dart
35 |
36 | # Exceptions to above rules.
37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
38 |
39 | #Keys
40 | google-services.json
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "app-golang-openemr"]
2 | path = app-golang-openemr
3 | url = https://github.com/openemr/app-golang-openemr
4 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 0b8abb4724aa590dd0f429683339b1e045a1594d
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | ## 1. Purpose
4 |
5 | A primary goal of OpenEMR is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
6 |
7 | This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.
8 |
9 | We invite all those who participate in OpenEMR to help us create safe and positive experiences for everyone.
10 |
11 | ## 2. Open Source Citizenship
12 |
13 | A supplemental goal of this Code of Conduct is to increase open source citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community.
14 |
15 | Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society.
16 |
17 | If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know.
18 |
19 | ## 3. Expected Behavior
20 |
21 | The following behaviors are expected and requested of all community members:
22 |
23 | * Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.
24 | * Exercise consideration and respect in your speech and actions.
25 | * Attempt collaboration before conflict.
26 | * Refrain from demeaning, discriminatory, or harassing behavior and speech.
27 | * Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential.
28 | * Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations.
29 |
30 | ## 4. Unacceptable Behavior
31 |
32 | The following behaviors are considered harassment and are unacceptable within our community:
33 |
34 | * Violence, threats of violence or violent language directed against another person.
35 | * Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language.
36 | * Posting or displaying sexually explicit or violent material.
37 | * Posting or threatening to post other people’s personally identifying information ("doxing").
38 | * Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.
39 | * Inappropriate photography or recording.
40 | * Inappropriate physical contact. You should have someone’s consent before touching them.
41 | * Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances.
42 | * Deliberate intimidation, stalking or following (online or in person).
43 | * Advocating for, or encouraging, any of the above behavior.
44 | * Sustained disruption of community events, including talks and presentations.
45 |
46 | ## 5. Consequences of Unacceptable Behavior
47 |
48 | Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated.
49 |
50 | Anyone asked to stop unacceptable behavior is expected to comply immediately.
51 |
52 | If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event).
53 |
54 | ## 6. Reporting Guidelines
55 |
56 | If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. This can be done by sending a direct message to "admins" on the community forums found at https://community.open-emr.org/.
57 |
58 |
59 |
60 | Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress.
61 |
62 | ## 7. Addressing Grievances
63 |
64 | If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify OpenEMR with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies.
65 |
66 |
67 |
68 | ## 8. Scope
69 |
70 | We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues–online and in-person–as well as in all one-on-one communications pertaining to community business.
71 |
72 | This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members.
73 |
74 | ## 10. License and attribution
75 |
76 | This Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/).
77 |
78 | Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy).
79 |
80 | Retrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/)
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Thank you for your contribution. OpenEMR (and global healthcare) continues to get better because of people like you!
2 |
3 | The maintainers of OpenEMR want to get your pull request in as seamlessly as possible, so please ensure your code is consistent with our [development policies](https://open-emr.org/wiki/index.php/Development_Policies).
4 |
5 |
6 | We look forward to your contribution...
7 |
8 | ## Financial contributions
9 |
10 | We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/openemr).
11 | Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.
12 |
13 | ## Credits
14 |
15 | ### Contributors
16 |
17 | Thank you to all the people who have already contributed to openemr!
18 |
19 |
20 | ### Backers
21 |
22 | Thank you to all our backers! [[Become a backer](https://opencollective.com/openemr#backer)]
23 |
24 |
25 |
26 | ### Sponsors
27 |
28 | Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/openemr#sponsor))
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OpenEMRv2.2
2 |
3 | [OpenEMR](https://open-emr.org) is the most popular open source electronic health records and medical practice management solution.
4 | ## What's New?
5 | - Warnings has been removed
6 | - Deprecated has been fixed
7 | - Api has been improved
8 | - Databse has been added to medical recognition
9 |
10 | ## Future Work
11 | -> Error message based on API response (In Progress)
12 |
13 | ## For Developers
14 |
15 | If using OpenEMR directly from the code repository, then the following commands will build OpenEMR apk :
16 |
17 | ```shell
18 | flutter pub get
19 | flutter build apk|appbundle|ios|ios-framework
20 | ```
21 |
22 | To run openemr in a device
23 |
24 | ```shell
25 | flutter pub get
26 | flutter run
27 | ```
28 | ### How to use calling feature
29 | 1. Run the [app-golang-openemr](https://github.com/openemr/app-golang-openemr/tree/c6930bb8f84e572234daaa071add316334a247f5)
30 | 2. Enter the server ip address in the prompt
31 | 
32 |
33 | ### How to Setup Firebase
34 |
35 | #### Project Creation
36 |
37 | 1. Go to [Firebase console](https://console.firebase.google.com/)
38 | 2. Login and click on `Add Project` card
39 | 
40 | 3. Enter desired project name and click on `Continue` button
41 | 
42 | 4. Disable Google Analytics if you want but we suggest you to keep it as it is and click on `Continue` button
43 | 
44 | 5. Select default or desired account and click on `Continue`. (will not appear if you have disabled Google Analytics in previous step)
45 | 
46 |
47 | #### Android - Connection
48 |
49 | 1. Select `Android` on home-page of your project
50 | 
51 | 2. Enter a `com.example.openemr` as package name. You can checkout this post if you want to [use custom package name](https://medium.com/@skyblazar.cc/how-to-change-the-package-name-of-your-flutter-app-4529e6e6e6fc)
52 | 
53 | 3. Enter the `SHA-1 hash`. [You can get the SHA-1 using this link](https://developers.google.com/android/guides/client-auth)
54 | 4. Click on `register app` button
55 | 5. Click on `Download google-services.json`. A json file will be downloaded to your desktop.
56 | 
57 | 6. Click on `next` button then again click on `next` button followed by `skip this step` button.
58 | 7. Place the `google-services.json` in `android/app` directory.
59 | 8. Go to `android/build.gradle` and uncomment `line 12`
60 | 9. Go to `android/app/build.gradle` and uncomment `line 26 & 65`
61 |
62 | #### IOS - Connection
63 |
64 | Coming soon
65 |
66 | #### Enable Firebase services
67 |
68 | 1. Authentication(Used for login/register)
69 | - Enable Email / Password
70 | 
71 | - Enable Google
72 | 
73 | 2. Database(Used to store messages)
74 | 
75 | 3. Firestore(Used to store images shared in chat)
76 | 
77 |
78 | #### Final step - turn firebase flag on
79 |
80 | Go to `lib/screens/home.dart` and change `firebaseFlag` to `true` from `false`
81 |
82 | ```diff
83 | - final firebaseFlag = false;
84 | + final firebaseFlag = true;
85 | ```
86 |
87 | ## License
88 |
89 | [GNU GPL](LICENSE)
90 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | // Uncomment the line if you want to use firebase
26 | // apply plugin: 'com.google.gms.google-services'
27 | apply plugin: 'kotlin-android'
28 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
29 |
30 | android {
31 | compileSdkVersion 28
32 |
33 | sourceSets {
34 | main.java.srcDirs += 'src/main/kotlin'
35 | }
36 |
37 | lintOptions {
38 | disable 'InvalidPackage'
39 | }
40 |
41 | defaultConfig {
42 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
43 | applicationId "com.example.openemr"
44 | minSdkVersion 21
45 | targetSdkVersion 28
46 | versionCode flutterVersionCode.toInteger()
47 | versionName flutterVersionName
48 | }
49 |
50 | buildTypes {
51 | release {
52 | // TODO: Add your own signing config for the release build.
53 | // Signing with the debug keys for now, so `flutter run --release` works.
54 | signingConfig signingConfigs.debug
55 | }
56 | }
57 | }
58 |
59 | flutter {
60 | source '../..'
61 | }
62 |
63 | dependencies {
64 | // Uncomment the line if you want to use firebase
65 | // implementation 'com.google.firebase:firebase-analytics:17.2.2'
66 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
67 | api 'com.google.firebase:firebase-ml-vision-image-label-model:17.0.2'
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
8 |
12 |
19 |
23 |
27 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
43 |
46 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/app_openemr/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.app_openemr
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/openemr_app/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.openemr
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | -
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/android/app/src/main/res/mipmap-xxhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.5.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | // Uncomment the line if you want to use firebase
12 | // classpath 'com.google.gms:google-services:4.3.3'
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | google()
19 | jcenter()
20 | }
21 | }
22 |
23 | rootProject.buildDir = '../build'
24 | subprojects {
25 | project.buildDir = "${rootProject.buildDir}/${project.name}"
26 | }
27 | subprojects {
28 | project.evaluationDependsOn(':app')
29 | }
30 |
31 | task clean(type: Delete) {
32 | delete rootProject.buildDir
33 | }
34 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
7 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
13 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
14 |
15 | def plugins = new Properties()
16 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
17 | if (pluginsFile.exists()) {
18 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
19 | }
20 |
21 | plugins.each { name, path ->
22 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
23 | include ":$name"
24 | project(":$name").projectDir = pluginDirectory
25 | }
26 |
--------------------------------------------------------------------------------
/img/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/img/1.png
--------------------------------------------------------------------------------
/img/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/img/2.png
--------------------------------------------------------------------------------
/img/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/img/3.png
--------------------------------------------------------------------------------
/img/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/img/4.png
--------------------------------------------------------------------------------
/img/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/img/5.png
--------------------------------------------------------------------------------
/img/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/img/6.png
--------------------------------------------------------------------------------
/img/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/img/7.png
--------------------------------------------------------------------------------
/img/auth.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/img/auth.gif
--------------------------------------------------------------------------------
/img/database.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/img/database.gif
--------------------------------------------------------------------------------
/img/google_auth.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/img/google_auth.gif
--------------------------------------------------------------------------------
/img/ip.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/img/ip.gif
--------------------------------------------------------------------------------
/img/storage.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/img/storage.gif
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"}]}
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | openemr
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/lib/assets/fonts/gfFontIcon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/fonts/gfFontIcon.ttf
--------------------------------------------------------------------------------
/lib/assets/fonts/gfFontIcons2.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/fonts/gfFontIcons2.ttf
--------------------------------------------------------------------------------
/lib/assets/fonts/gfIconFonts.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/fonts/gfIconFonts.ttf
--------------------------------------------------------------------------------
/lib/assets/fonts/gfSocialFonts.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/fonts/gfSocialFonts.ttf
--------------------------------------------------------------------------------
/lib/assets/fonts/loader.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/fonts/loader.ttf
--------------------------------------------------------------------------------
/lib/assets/gif/loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/gif/loader.gif
--------------------------------------------------------------------------------
/lib/assets/gif/loader1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/gif/loader1.gif
--------------------------------------------------------------------------------
/lib/assets/gif/success1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/gif/success1.gif
--------------------------------------------------------------------------------
/lib/assets/icons/gflogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/icons/gflogo.png
--------------------------------------------------------------------------------
/lib/assets/images/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/avatar.png
--------------------------------------------------------------------------------
/lib/assets/images/avatar1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/avatar1.png
--------------------------------------------------------------------------------
/lib/assets/images/avatar10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/avatar10.png
--------------------------------------------------------------------------------
/lib/assets/images/avatar11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/avatar11.png
--------------------------------------------------------------------------------
/lib/assets/images/avatar12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/avatar12.png
--------------------------------------------------------------------------------
/lib/assets/images/avatar2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/avatar2.png
--------------------------------------------------------------------------------
/lib/assets/images/avatar3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/avatar3.png
--------------------------------------------------------------------------------
/lib/assets/images/avatar4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/avatar4.png
--------------------------------------------------------------------------------
/lib/assets/images/avatar5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/avatar5.png
--------------------------------------------------------------------------------
/lib/assets/images/avatar6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/avatar6.png
--------------------------------------------------------------------------------
/lib/assets/images/avatar7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/avatar7.png
--------------------------------------------------------------------------------
/lib/assets/images/avatar8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/avatar8.png
--------------------------------------------------------------------------------
/lib/assets/images/avatar9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/avatar9.png
--------------------------------------------------------------------------------
/lib/assets/images/card.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/card.png
--------------------------------------------------------------------------------
/lib/assets/images/card1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/card1.png
--------------------------------------------------------------------------------
/lib/assets/images/card2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/card2.png
--------------------------------------------------------------------------------
/lib/assets/images/card3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/card3.png
--------------------------------------------------------------------------------
/lib/assets/images/card4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/card4.png
--------------------------------------------------------------------------------
/lib/assets/images/card5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/card5.png
--------------------------------------------------------------------------------
/lib/assets/images/firebase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/firebase.png
--------------------------------------------------------------------------------
/lib/assets/images/gflogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/gflogo.png
--------------------------------------------------------------------------------
/lib/assets/images/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/image.png
--------------------------------------------------------------------------------
/lib/assets/images/image1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/image1.png
--------------------------------------------------------------------------------
/lib/assets/images/image2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/image2.png
--------------------------------------------------------------------------------
/lib/assets/images/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/img.png
--------------------------------------------------------------------------------
/lib/assets/images/img1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/img1.png
--------------------------------------------------------------------------------
/lib/assets/images/img2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/img2.png
--------------------------------------------------------------------------------
/lib/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/logo.png
--------------------------------------------------------------------------------
/lib/assets/images/orange.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/orange.png
--------------------------------------------------------------------------------
/lib/assets/images/pink.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/pink.png
--------------------------------------------------------------------------------
/lib/assets/images/purple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/purple.png
--------------------------------------------------------------------------------
/lib/assets/images/red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openemr/app-flutter-openemr/4d19907d06914da0d163a6bba9cc3645981cd93b/lib/assets/images/red.png
--------------------------------------------------------------------------------
/lib/const/strings.dart:
--------------------------------------------------------------------------------
1 | const loginendpoint = "/apis/api/auth";
2 | const patientendpoint = "/apis/api/patient";
3 | const addpatientendpoint = "/apis/api/patient";
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'package:flutter/material.dart';
3 | import 'package:openemr/screens/home.dart';
4 | // import './screens/home.dart';
5 |
6 | void main(){
7 | HttpOverrides.global = new MyHttpOverrides();
8 | runApp(new MyApp());
9 | }
10 |
11 | class MyApp extends StatelessWidget {
12 | @override
13 | Widget build(BuildContext context) => MaterialApp(
14 | title: 'OpenEMR',
15 | debugShowCheckedModeBanner: false,
16 | theme: ThemeData(
17 | primarySwatch: Colors.blue,
18 | ),
19 | home: HomePage(),
20 | );
21 | }
22 |
23 | class MyHttpOverrides extends HttpOverrides{
24 | @override
25 | HttpClient createHttpClient(SecurityContext context){
26 | return super.createHttpClient(context)
27 | ..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
28 | }
29 | }
--------------------------------------------------------------------------------
/lib/models/patient.dart:
--------------------------------------------------------------------------------
1 | class Patient {
2 | String _id;
3 | String _pid;
4 | String _pubpid;
5 | String _title;
6 | String _fname;
7 | String _mname;
8 | String _lname;
9 | String _street;
10 | String _postalCode;
11 | String _city;
12 | String _state;
13 | String _countryCode;
14 | String _phoneContact;
15 | String _dob;
16 | String _sex;
17 | String _race;
18 | String _ethnicity;
19 | String _username;
20 | String _tokenType;
21 | String _accessToken;
22 | String _userId;
23 |
24 | String get pid => _pid;
25 | String get title => _title;
26 | String get fname => _fname;
27 | String get mname => _mname;
28 | String get lname => _lname;
29 | String get sex => _sex;
30 |
31 | Patient(
32 | this._accessToken,
33 | this._tokenType,
34 | this._userId,
35 | this._username,
36 | this._city,
37 | this._countryCode,
38 | this._dob,
39 | this._ethnicity,
40 | this._fname,
41 | this._id,
42 | this._lname,
43 | this._mname,
44 | this._phoneContact,
45 | this._pid,
46 | this._postalCode,
47 | this._pubpid,
48 | this._race,
49 | this._sex,
50 | this._state,
51 | this._street,
52 | this._title);
53 |
54 | Patient.map(dynamic obj) {
55 | this._accessToken = obj["access_token"];
56 | this._tokenType = obj["token_type"];
57 | this._userId = obj["user_id"];
58 | this._username = obj["username"];
59 | this._city = obj["city"];
60 | this._countryCode = obj["country_code"];
61 | this._dob = obj["DOB"];
62 | this._ethnicity = obj["ethnicity"];
63 | this._fname = obj["fname"];
64 | this._id = obj["id"];
65 | this._lname = obj["lname"];
66 | this._mname = obj["mname"];
67 | this._phoneContact = obj["phone_contact"];
68 | this._pid = obj["pid"];
69 | this._postalCode = obj["postal_code"];
70 | this._pubpid = obj["pubpid"];
71 | this._race = obj["race"];
72 | this._sex = obj["sex"];
73 | this._state = obj["state"];
74 | this._street = obj["street"];
75 | this._title = obj["title"];
76 | }
77 |
78 | Map toMap() {
79 | var map = new Map();
80 | map["access_token"] = _accessToken;
81 | map["token_type"] = _tokenType;
82 | map["user_id"] = _userId;
83 | map["username"] = _username;
84 | map["city"] = _city;
85 | map["country_code"] = _countryCode;
86 | map["DOB"] = _dob;
87 | map["ethnicity"] = _ethnicity;
88 | map["fname"] = _fname;
89 | map["id"] = _id;
90 | map["lname"] = _lname;
91 | map["mname"] = _mname;
92 | map["phone_contact"] = _phoneContact;
93 | map["pid"] = _pid;
94 | map["postal_code"] = _postalCode;
95 | map["pubpid"] = _pubpid;
96 | map["race"] = _race;
97 | map["sex"] = _sex;
98 | map["state"] = _state;
99 | map["street"] = _street;
100 | map["title"] = _title;
101 | return map;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/lib/models/user.dart:
--------------------------------------------------------------------------------
1 | class User {
2 | String _username;
3 | String _tokenType;
4 | String _accessToken;
5 | String _baseUrl;
6 | String _password;
7 | User(this._username, this._tokenType, this._accessToken, this._baseUrl,
8 | this._password);
9 |
10 | User.map(dynamic obj) {
11 | this._username = obj["username"];
12 | this._tokenType = obj["token_type"];
13 | this._accessToken = obj["access_token"];
14 | this._baseUrl = obj["baseUrl"];
15 | this._password = obj["password"];
16 | }
17 |
18 | set username(String username) {
19 | this._username = username;
20 | }
21 |
22 | set password(String password) {
23 | this._password = password;
24 | }
25 |
26 | set url(String url) {
27 | this._baseUrl = url;
28 | }
29 |
30 | String get username => _username;
31 | String get tokenType => _tokenType;
32 | String get accessToken => _accessToken;
33 | String get baseUrl => _baseUrl;
34 | String get password => _password;
35 |
36 | Map toMap() {
37 | var map = new Map();
38 | map["username"] = _username;
39 | map["tokenType"] = _tokenType;
40 | map["accessToken"] = _accessToken;
41 | map["baseUrl"] = _baseUrl;
42 | map["password"] = _password;
43 | return map;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/screens/addpatient/local_widgets/custom_dropdown_field.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class CustomDropdownField extends StatefulWidget {
4 | final String label;
5 | final List options;
6 | final Function updateValue;
7 | final String value;
8 |
9 | CustomDropdownField({
10 | this.label,
11 | this.options,
12 | this.updateValue,
13 | this.value,
14 | });
15 |
16 | @override
17 | _CustomDropdownFieldState createState() => _CustomDropdownFieldState();
18 | }
19 |
20 | class _CustomDropdownFieldState extends State {
21 | @override
22 | Widget build(BuildContext context) {
23 | return Container(
24 | padding: const EdgeInsets.symmetric(horizontal: 10),
25 | child: Row(
26 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
27 | children: [
28 | Text(
29 | widget.label,
30 | style: TextStyle(
31 | color: Colors.black,
32 | fontSize: 16,
33 | ),
34 | ),
35 | DropdownButton(
36 | value: widget.value,
37 | icon: Icon(Icons.arrow_drop_down_rounded),
38 | iconSize: 20,
39 | elevation: 16,
40 | underline: Container(
41 | height: 2,
42 | color: Colors.black,
43 | ),
44 | onChanged: (String newValue) {
45 | setState(() {
46 | widget.updateValue(newValue);
47 | });
48 | },
49 | items: widget.options.map>((String value) {
50 | return DropdownMenuItem(
51 | value: value,
52 | child: Text(value),
53 | );
54 | }).toList(),
55 | ),
56 | ],
57 | ),
58 | );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/lib/screens/codescanner/codescanner.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io' show Platform;
3 |
4 | import 'package:barcode_scan/barcode_scan.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter/services.dart';
7 | import 'package:getwidget/getwidget.dart';
8 | import 'package:flutter/cupertino.dart';
9 | import '../../screens/drawer/webview.dart';
10 |
11 | class CodeScanner extends StatefulWidget {
12 | @override
13 | _CodeScannerState createState() => _CodeScannerState();
14 | }
15 |
16 | class _CodeScannerState extends State {
17 | ScanResult scanResult;
18 |
19 | final _flashOnController = TextEditingController(text: "Flash on");
20 | final _flashOffController = TextEditingController(text: "Flash off");
21 | final _cancelController = TextEditingController(text: "Cancel");
22 |
23 | var _aspectTolerance = 0.00;
24 | var _numberOfCameras = 0;
25 | var _selectedCamera = -1;
26 | var _useAutoFocus = true;
27 | var _autoEnableFlash = false;
28 |
29 | static final _possibleFormats = BarcodeFormat.values.toList()
30 | ..removeWhere((e) => e == BarcodeFormat.unknown);
31 |
32 | List selectedFormats = [..._possibleFormats];
33 |
34 | @override
35 | // ignore: type_annotate_public_apis
36 | initState() {
37 | super.initState();
38 |
39 | Future.delayed(Duration.zero, () async {
40 | _numberOfCameras = await BarcodeScanner.numberOfCameras;
41 | setState(() {});
42 | });
43 | }
44 |
45 | @override
46 | Widget build(BuildContext context) {
47 | var contentList = [
48 | if (scanResult != null)
49 | Card(
50 | child: Column(
51 | children: [
52 | ListTile(
53 | title: Text("Result Type"),
54 | subtitle: Text(scanResult.type?.toString() ?? ""),
55 | ),
56 | ListTile(
57 | title: Text("Raw Content"),
58 | subtitle: Text(scanResult.rawContent ?? ""),
59 | trailing: IconButton(
60 | icon: Icon(Icons.content_copy),
61 | onPressed: () {
62 | Clipboard.setData(
63 | ClipboardData(text: scanResult.rawContent));
64 | })),
65 | ListTile(
66 | title: Text("Format"),
67 | subtitle: Text(scanResult.format?.toString() ?? ""),
68 | ),
69 | ListTile(
70 | title: Text("Format note"),
71 | subtitle: Text(scanResult.formatNote ?? ""),
72 | ),
73 | Uri.parse(scanResult.rawContent).isAbsolute
74 | ? GFButton(
75 | onPressed: () {
76 | var uri = scanResult.rawContent;
77 | Navigator.push(
78 | context,
79 | MaterialPageRoute(
80 | builder: (BuildContext context) => WebViews(
81 | url: uri,
82 | ),
83 | ));
84 | },
85 | shape: GFButtonShape.pills,
86 | child: const Text(
87 | 'Open',
88 | ),
89 | color: GFColors.DARK,
90 | )
91 | : GFButton(
92 | onPressed: () {
93 | var uri = "https://www.google.com/search?q=" +
94 | Uri.encodeFull(scanResult.rawContent);
95 | Navigator.push(
96 | context,
97 | MaterialPageRoute(
98 | builder: (BuildContext context) => WebViews(
99 | url: uri,
100 | ),
101 | ));
102 | },
103 | shape: GFButtonShape.pills,
104 | child: const Text(
105 | 'Search',
106 | ),
107 | color: GFColors.DARK,
108 | )
109 | ],
110 | ),
111 | ),
112 | ListTile(
113 | title: Text("Camera selection"),
114 | dense: true,
115 | enabled: false,
116 | ),
117 | RadioListTile(
118 | activeColor: GFColors.DARK,
119 | onChanged: (v) => setState(() => _selectedCamera = -1),
120 | value: -1,
121 | title: Text("Default camera"),
122 | groupValue: _selectedCamera,
123 | ),
124 | ];
125 |
126 | for (var i = 0; i < _numberOfCameras; i++) {
127 | contentList.add(RadioListTile(
128 | activeColor: GFColors.DARK,
129 | onChanged: (v) => setState(() => _selectedCamera = i),
130 | value: i,
131 | title: Text("Camera ${i + 1}"),
132 | groupValue: _selectedCamera,
133 | ));
134 | }
135 |
136 | if (Platform.isAndroid) {
137 | contentList.addAll([
138 | ListTile(
139 | title: Text("Android specific options"),
140 | dense: true,
141 | enabled: false,
142 | ),
143 | ListTile(
144 | title:
145 | Text("Aspect tolerance (${_aspectTolerance.toStringAsFixed(2)})"),
146 | subtitle: Slider(
147 | activeColor: GFColors.DARK,
148 | min: -1.0,
149 | max: 1.0,
150 | value: _aspectTolerance,
151 | onChanged: (value) {
152 | setState(() {
153 | _aspectTolerance = value;
154 | });
155 | },
156 | ),
157 | ),
158 | CheckboxListTile(
159 | activeColor: GFColors.DARK,
160 | title: Text("Use autofocus"),
161 | value: _useAutoFocus,
162 | onChanged: (checked) {
163 | setState(() {
164 | _useAutoFocus = checked;
165 | });
166 | },
167 | )
168 | ]);
169 | }
170 |
171 | contentList.addAll([
172 | ListTile(
173 | title: Text("Other options"),
174 | dense: true,
175 | enabled: false,
176 | ),
177 | CheckboxListTile(
178 | activeColor: GFColors.DARK,
179 | title: Text("Start with flash"),
180 | value: _autoEnableFlash,
181 | onChanged: (checked) {
182 | setState(() {
183 | _autoEnableFlash = checked;
184 | });
185 | },
186 | )
187 | ]);
188 |
189 | contentList.addAll([
190 | ListTile(
191 | title: Text("Barcode formats"),
192 | dense: true,
193 | enabled: false,
194 | ),
195 | ListTile(
196 | trailing: Checkbox(
197 | activeColor: GFColors.DARK,
198 | tristate: true,
199 | materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
200 | value: selectedFormats.length == _possibleFormats.length
201 | ? true
202 | : selectedFormats.length == 0 ? false : null,
203 | onChanged: (checked) {
204 | setState(() {
205 | selectedFormats = [
206 | if (checked ?? false) ..._possibleFormats,
207 | ];
208 | });
209 | },
210 | ),
211 | dense: true,
212 | enabled: false,
213 | title: Text("Detect barcode formats"),
214 | subtitle: Text(
215 | 'If all are unselected, all possible platform formats will be used',
216 | ),
217 | ),
218 | ]);
219 |
220 | contentList.addAll(_possibleFormats.map(
221 | (format) => CheckboxListTile(
222 | activeColor: GFColors.DARK,
223 | value: selectedFormats.contains(format),
224 | onChanged: (i) {
225 | setState(() => selectedFormats.contains(format)
226 | ? selectedFormats.remove(format)
227 | : selectedFormats.add(format));
228 | },
229 | title: Text(format.toString()),
230 | ),
231 | ));
232 |
233 | return MaterialApp(
234 | debugShowCheckedModeBanner: false,
235 | home: Scaffold(
236 | appBar: AppBar(
237 | backgroundColor: GFColors.DARK,
238 | leading: InkWell(
239 | onTap: () {
240 | Navigator.pop(context);
241 | },
242 | child: Icon(
243 | CupertinoIcons.back,
244 | color: GFColors.SUCCESS,
245 | ),
246 | ),
247 | title: const Text(
248 | 'Code Scanner',
249 | style: TextStyle(fontSize: 17),
250 | ),
251 | centerTitle: true,
252 | actions: [
253 | IconButton(
254 | icon: Icon(Icons.camera),
255 | tooltip: "Scan",
256 | onPressed: scan,
257 | )
258 | ],
259 | ),
260 | body: ListView(
261 | scrollDirection: Axis.vertical,
262 | shrinkWrap: true,
263 | children: contentList,
264 | ),
265 | ),
266 | );
267 | }
268 |
269 | Future scan() async {
270 | try {
271 | var options = ScanOptions(
272 | strings: {
273 | "cancel": _cancelController.text,
274 | "flash_on": _flashOnController.text,
275 | "flash_off": _flashOffController.text,
276 | },
277 | restrictFormat: selectedFormats,
278 | useCamera: _selectedCamera,
279 | autoEnableFlash: _autoEnableFlash,
280 | android: AndroidOptions(
281 | aspectTolerance: _aspectTolerance,
282 | useAutoFocus: _useAutoFocus,
283 | ),
284 | );
285 |
286 | var result = await BarcodeScanner.scan(options: options);
287 |
288 | setState(() => scanResult = result);
289 | } on PlatformException catch (e) {
290 | var result = ScanResult(
291 | type: ResultType.Error,
292 | format: BarcodeFormat.unknown,
293 | );
294 |
295 | if (e.code == BarcodeScanner.cameraAccessDenied) {
296 | setState(() {
297 | result.rawContent = 'The user did not grant the camera permission!';
298 | });
299 | } else {
300 | result.rawContent = 'Unknown error: $e';
301 | }
302 | setState(() {
303 | scanResult = result;
304 | });
305 | }
306 | }
307 | }
308 |
--------------------------------------------------------------------------------
/lib/screens/drawer/drawer.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:getwidget/getwidget.dart';
4 | import '../../screens/drawer/webview.dart';
5 |
6 | class DrawerPage extends StatefulWidget {
7 | @override
8 | _DrawerPageState createState() => _DrawerPageState();
9 | }
10 |
11 | class _DrawerPageState extends State {
12 | @override
13 | Widget build(BuildContext context) => GFDrawer(
14 | color: Colors.white,
15 | child: ListView(
16 | padding: EdgeInsets.zero,
17 | children: [
18 | Container(
19 | decoration: BoxDecoration(
20 | gradient: LinearGradient(
21 | begin: Alignment.topCenter,
22 | end: Alignment.bottomCenter,
23 | colors: const [Color(0xFFD685FF), Color(0xFF7466CC)])),
24 | height: 250,
25 | child: GFDrawerHeader(
26 | closeButton: InkWell(
27 | onTap: () {
28 | Navigator.pop(context);
29 | },
30 | child: Icon(
31 | CupertinoIcons.back,
32 | color: GFColors.SUCCESS,
33 | ),
34 | ),
35 | decoration: BoxDecoration(
36 | gradient: LinearGradient(
37 | begin: Alignment.topCenter,
38 | end: Alignment.bottomCenter,
39 | colors: const [Color(0xFFD685FF), Color(0xFF7466CC)],
40 | ),
41 | ),
42 | child: Column(
43 | mainAxisAlignment: MainAxisAlignment.start,
44 | crossAxisAlignment: CrossAxisAlignment.center,
45 | children: [
46 | GFAvatar(
47 | backgroundColor: GFColors.DARK,
48 | shape: GFAvatarShape.square,
49 | borderRadius: BorderRadius.all(Radius.circular(20)),
50 | backgroundImage: AssetImage(
51 | 'lib/assets/images/gflogo.png',
52 | ),
53 | ),
54 | const SizedBox(
55 | height: 5,
56 | ),
57 | Text(
58 | 'OpenEMR',
59 | style: TextStyle(
60 | fontSize: 20,
61 | fontWeight: FontWeight.w500,
62 | color: Colors.white),
63 | ),
64 | const SizedBox(
65 | height: 5,
66 | ),
67 | const Text(
68 | 'open-emr.org',
69 | style: TextStyle(color: Colors.white),
70 | ),
71 | ],
72 | ),
73 | ),
74 | ),
75 | Container(
76 | padding: const EdgeInsets.only(
77 | left: 10,
78 | ),
79 | color: Colors.white,
80 | child: Column(
81 | children: [
82 | InkWell(
83 | onTap: () {
84 | Navigator.push(
85 | context,
86 | MaterialPageRoute(
87 | builder: (BuildContext context) => const WebViews(
88 | url: 'https://github.com/openemr/openemr'),
89 | ),
90 | );
91 | },
92 | child: const Padding(
93 | padding: EdgeInsets.only(left: 2),
94 | child: GFListTile(
95 | avatar: Icon(Icons.store),
96 | title: Text('Main Repository',
97 | style:
98 | TextStyle(fontSize: 16, color: Colors.black87)),
99 | ),
100 | ),
101 | ),
102 | InkWell(
103 | onTap: () {
104 | Navigator.push(
105 | context,
106 | MaterialPageRoute(
107 | builder: (BuildContext context) => const WebViews(
108 | url:
109 | 'https://github.com/openemr/app-flutter-openemr/blob/master/README.md'),
110 | ),
111 | );
112 | },
113 | child: const Padding(
114 | padding: EdgeInsets.only(left: 2),
115 | child: GFListTile(
116 | avatar: Icon(CupertinoIcons.eye_solid),
117 | title: Text('Documentation',
118 | style:
119 | TextStyle(fontSize: 16, color: Colors.black87)),
120 | ),
121 | ),
122 | ),
123 | Divider(color: GFColors.FOCUS, indent: 20, endIndent: 30),
124 | const Padding(
125 | padding: EdgeInsets.only(left: 2),
126 | child: Text("Last update: 04/08/21"),
127 | ),
128 | const Padding(
129 | padding: EdgeInsets.only(left: 2),
130 | child: Text("v2.2"),
131 | ),
132 | ],
133 | ),
134 | ),
135 | ],
136 | ),
137 | );
138 | }
139 |
--------------------------------------------------------------------------------
/lib/screens/drawer/webview.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:getwidget/getwidget.dart';
6 | import 'package:webview_flutter/webview_flutter.dart';
7 |
8 | class WebViews extends StatefulWidget {
9 | const WebViews({Key key, this.url}) : super(key: key);
10 | final String url;
11 | @override
12 | _WebViewsState createState() => _WebViewsState();
13 | }
14 |
15 | class _WebViewsState extends State {
16 | final Completer _controller =
17 | Completer();
18 | @override
19 | Widget build(BuildContext context) => Scaffold(
20 | appBar: AppBar(
21 | backgroundColor: GFColors.DARK,
22 | title: Image.asset(
23 | 'lib/assets/icons/gflogo.png',
24 | width: 150,
25 | ),
26 | centerTitle: true,
27 | leading: InkWell(
28 | onTap: () {
29 | Navigator.pop(context);
30 | },
31 | child: Icon(
32 | CupertinoIcons.back,
33 | color: GFColors.SUCCESS,
34 | ),
35 | ),
36 | ),
37 | body: Builder(
38 | builder: (BuildContext context) => WebView(
39 | initialUrl: widget.url,
40 | javascriptMode: JavascriptMode.unrestricted,
41 | onWebViewCreated: _controller.complete,
42 | )));
43 | }
44 |
--------------------------------------------------------------------------------
/lib/screens/home.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:getwidget/getwidget.dart';
5 | import 'package:openemr/models/user.dart';
6 | import 'package:openemr/screens/codescanner/codescanner.dart';
7 | import 'package:openemr/screens/login/login2.dart';
8 | import 'package:openemr/screens/medicine/medicine_recognition_ML_Kit.dart';
9 | import 'package:openemr/screens/patientList/patient_list.dart';
10 | import 'package:openemr/screens/ppg/heartRate.dart';
11 | import 'package:openemr/screens/telehealth/telehealth.dart';
12 | import 'package:openemr/utils/rest_ds.dart';
13 | import 'package:shared_preferences/shared_preferences.dart';
14 | import '../screens/drawer/drawer.dart';
15 | import 'login/login.dart';
16 | import 'package:firebase_auth/firebase_auth.dart';
17 |
18 | class HomePage extends StatefulWidget {
19 | @override
20 | _HomePageState createState() => _HomePageState();
21 | }
22 |
23 | class _HomePageState extends State {
24 | final userRef = Firestore.instance.collection('username');
25 | final FirebaseAuth _auth = FirebaseAuth.instance;
26 | final firebaseFlag = false;
27 | List gfComponents = [
28 | {
29 | 'icon': CupertinoIcons.heart_solid,
30 | 'title': 'PPG',
31 | 'route': PPG(),
32 | },
33 | {
34 | 'icon': Icons.video_call,
35 | 'title': 'Telehealth',
36 | 'authentication': "firebase",
37 | 'failRoute': LoginFirebaseScreen(),
38 | 'route': Telehealth(),
39 | },
40 | {
41 | 'icon': Icons.people,
42 | 'title': 'Patient List',
43 | 'route': PatientListPage(),
44 | 'authentication': "webapp",
45 | 'failRoute': LoginScreen(),
46 | },
47 | {
48 | 'icon': Icons.translate,
49 | 'title': 'Medicine Recognition',
50 | 'route': MedicineRecognitionMLKit(),
51 | },
52 | {
53 | 'icon': Icons.scanner,
54 | 'title': 'Code scanner',
55 | 'route': CodeScanner(),
56 | },
57 | ];
58 |
59 | void _showSnackBar(String text) {
60 | ScaffoldMessenger.of(context)
61 | .showSnackBar(new SnackBar(content: new Text(text)));
62 | }
63 |
64 | @override
65 | Widget build(BuildContext context) => Scaffold(
66 | drawer: DrawerPage(),
67 | appBar: AppBar(
68 | backgroundColor: GFColors.DARK,
69 | title: Image.asset(
70 | 'lib/assets/icons/gflogo.png',
71 | width: 150,
72 | ),
73 | centerTitle: true,
74 | ),
75 | body: ListView(
76 | physics: const ScrollPhysics(),
77 | scrollDirection: Axis.vertical,
78 | shrinkWrap: true,
79 | children: [
80 | Container(
81 | margin: const EdgeInsets.only(
82 | left: 15,
83 | bottom: 20,
84 | top: 20,
85 | right: 15,
86 | ),
87 | child: GridView.builder(
88 | scrollDirection: Axis.vertical,
89 | shrinkWrap: true,
90 | physics: const ScrollPhysics(),
91 | itemCount: gfComponents.length,
92 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
93 | crossAxisCount: 2,
94 | crossAxisSpacing: 20,
95 | mainAxisSpacing: 20),
96 | itemBuilder: (BuildContext context, int index) =>
97 | buildSquareTile(
98 | gfComponents[index]['title'],
99 | gfComponents[index]['icon'],
100 | gfComponents[index]['route'],
101 | gfComponents[index]['authentication'],
102 | gfComponents[index]['failRoute'],
103 | gfComponents[index]['disabled'] ?? false,
104 | ),
105 | ),
106 | ),
107 | ],
108 | ),
109 | );
110 |
111 | Widget buildSquareTile(
112 | String title,
113 | IconData icon,
114 | Widget route,
115 | String auth,
116 | Widget failRoute,
117 | bool disabled,
118 | ) =>
119 | InkWell(
120 | onTap: !disabled
121 | ? () async {
122 | if (auth == "webapp") {
123 | final prefs = await SharedPreferences.getInstance();
124 | var username = prefs.getString('username');
125 | var password = prefs.getString('password');
126 | var url = prefs.getString('baseUrl');
127 | RestDatasource api = new RestDatasource();
128 | api.login(username, password, url).then((User user) {
129 | prefs.setString(
130 | 'token', user.tokenType + " " + user.accessToken);
131 | Navigator.push(
132 | context,
133 | MaterialPageRoute(
134 | builder: (BuildContext context) => route),
135 | );
136 | }).catchError((Object error) {
137 | Navigator.push(
138 | context,
139 | MaterialPageRoute(
140 | builder: (BuildContext context) => failRoute),
141 | );
142 | });
143 | } else if (auth == "firebase") {
144 | if (firebaseFlag) {
145 | var user = await _auth.currentUser();
146 | SharedPreferences prefs =
147 | await SharedPreferences.getInstance();
148 | var loggedUserId = prefs.getString('loggedUserId');
149 |
150 | if (user != null && user.isEmailVerified) {
151 | Navigator.push(
152 | context,
153 | MaterialPageRoute(
154 | builder: (BuildContext context) => route),
155 | );
156 | } else if (user != null && loggedUserId != null) {
157 | DocumentSnapshot documentSnapshot =
158 | await userRef.document(loggedUserId).get();
159 | if (documentSnapshot.exists) {
160 | Navigator.push(
161 | context,
162 | MaterialPageRoute(
163 | builder: (BuildContext context) => route),
164 | );
165 | } else {
166 | Navigator.push(
167 | context,
168 | MaterialPageRoute(
169 | builder: (BuildContext context) => failRoute),
170 | );
171 | }
172 | } else {
173 | Navigator.push(
174 | context,
175 | MaterialPageRoute(
176 | builder: (BuildContext context) => failRoute),
177 | );
178 | }
179 | } else {
180 | _showSnackBar("Check readme to enable firebase");
181 | }
182 | } else {
183 | Navigator.push(
184 | context,
185 | MaterialPageRoute(builder: (BuildContext context) => route),
186 | );
187 | }
188 | }
189 | : null,
190 | child: Container(
191 | decoration: BoxDecoration(
192 | color: !disabled ? Color(0xFF333333) : Colors.grey[500],
193 | borderRadius: const BorderRadius.all(Radius.circular(7)),
194 | boxShadow: [
195 | BoxShadow(
196 | color: Colors.black.withOpacity(0.61),
197 | blurRadius: 6,
198 | spreadRadius: 0,
199 | ),
200 | ],
201 | ),
202 | child: Column(
203 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
204 | children: [
205 | Icon(
206 | icon,
207 | color: !disabled
208 | ? GFColors.SUCCESS
209 | : Colors.white.withOpacity(0.7),
210 | size: 30,
211 | ),
212 | // Icon((icon),),
213 | Text(title,
214 | style: const TextStyle(color: GFColors.WHITE, fontSize: 20),
215 | textAlign: TextAlign.center)
216 | ],
217 | ),
218 | ),
219 | );
220 | }
221 |
--------------------------------------------------------------------------------
/lib/screens/login/create_account.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 | import 'package:firebase_auth/firebase_auth.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:getwidget/colors/gf_color.dart';
5 | import 'package:getwidget/components/button/gf_button.dart';
6 | import 'package:google_sign_in/google_sign_in.dart';
7 | import 'package:openemr/screens/telehealth/telehealth.dart';
8 | import 'package:openemr/utils/customlistloadingshimmer.dart';
9 |
10 | class CreateAccount extends StatefulWidget {
11 | final FirebaseUser dispUser;
12 |
13 | CreateAccount({Key key, @required this.dispUser}) : super(key: key);
14 | @override
15 | _CreateAccountBuyerState createState() => _CreateAccountBuyerState();
16 | }
17 |
18 | class _CreateAccountBuyerState extends State {
19 | final userRef = Firestore.instance.collection('username');
20 | final GoogleSignIn googleSignIn = GoogleSignIn();
21 |
22 | bool _isLoading = false;
23 | final _formKey = GlobalKey();
24 | String _userid;
25 |
26 | void _toggleLoadingStatus(bool newLoadingState) {
27 | setState(() {
28 | _isLoading = newLoadingState;
29 | });
30 | }
31 |
32 | handleRegister(context) async {
33 | final form = _formKey.currentState;
34 |
35 | if (form.validate()) {
36 | form.save();
37 | _toggleLoadingStatus(true);
38 | await userRef.document(widget.dispUser.uid).setData({
39 | "id": _userid,
40 | "name": widget.dispUser.displayName,
41 | });
42 |
43 | _toggleLoadingStatus(false);
44 | Navigator.of(context).pushReplacement(
45 | MaterialPageRoute(builder: (context) => Telehealth()));
46 | }
47 | }
48 |
49 | @override
50 | Widget build(BuildContext parentContext) {
51 | double width = MediaQuery.of(context).size.width;
52 |
53 | return Scaffold(
54 | backgroundColor: GFColors.LIGHT,
55 | body: Padding(
56 | padding: EdgeInsets.only(left: width * 0.1, right: width * 0.1),
57 | child: Center(
58 | child: SingleChildScrollView(
59 | child: Form(
60 | key: _formKey,
61 | child: Column(
62 | mainAxisAlignment: MainAxisAlignment.center,
63 | crossAxisAlignment: CrossAxisAlignment.center,
64 | children: [
65 | SizedBox(
66 | height: 25,
67 | ),
68 | Image.asset(
69 | 'lib/assets/images/firebase.png',
70 | width: width * 0.25,
71 | ),
72 | SizedBox(
73 | height: 20,
74 | ),
75 | _isLoading
76 | ? customListLoadingShimmer(context,
77 | loadingMessage: 'Creating Account', listLength: 4)
78 | : Column(
79 | children: [
80 | SizedBox(
81 | child: TextFormField(
82 | validator: (value) {
83 | if (value.isEmpty) {
84 | return 'Username can\'t be blank';
85 | }
86 | return null;
87 | },
88 | onSaved: (val) => _userid = val,
89 | decoration: InputDecoration(
90 | border: OutlineInputBorder(),
91 | labelText: 'Username'),
92 | ),
93 | ),
94 | SizedBox(
95 | height: 20,
96 | ),
97 | GFButton(
98 | onPressed: () => handleRegister(context),
99 | text: 'Register',
100 | color: GFColors.DARK,
101 | ),
102 | ],
103 | ),
104 | ],
105 | ),
106 | ),
107 | ),
108 | ),
109 | ));
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/lib/screens/login/login.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:getwidget/getwidget.dart';
4 | import 'package:openemr/utils/customlistloadingshimmer.dart';
5 | import 'package:openemr/utils/rest_ds.dart';
6 | import 'package:shared_preferences/shared_preferences.dart';
7 | import '../../models/user.dart';
8 |
9 | class LoginScreen extends StatefulWidget {
10 | @override
11 | _LoginScreenState createState() => _LoginScreenState();
12 | }
13 |
14 | class _LoginScreenState extends State {
15 | User user;
16 | bool _isLoading = false;
17 |
18 | final formKey = new GlobalKey();
19 | String _username, _password, _url;
20 |
21 | void _showSnackBar(String text) {
22 | ScaffoldMessenger.of(context)
23 | .showSnackBar(new SnackBar(content: new Text(text)));
24 | }
25 |
26 | void _toggleLoadingStatus(bool newLoadingState) {
27 | setState(() => _isLoading = newLoadingState);
28 | }
29 |
30 | @override
31 | Widget build(BuildContext context) {
32 | double width = MediaQuery.of(context).size.width;
33 | return GestureDetector(
34 | onTap: () {
35 | FocusScopeNode currentFocus = FocusScope.of(context);
36 | if (!currentFocus.hasPrimaryFocus) {
37 | currentFocus.unfocus();
38 | }
39 | },
40 | child: Scaffold(
41 | backgroundColor: GFColors.LIGHT,
42 | body: Padding(
43 | padding: EdgeInsets.only(left: width * 0.1, right: width * 0.1),
44 | child: Center(
45 | child: SingleChildScrollView(
46 | child: _isLoading
47 | ? customListLoadingShimmer(
48 | context,
49 | listLength: 3,
50 | loadingMessage: 'Authenticating...',
51 | )
52 | : Form(
53 | key: formKey,
54 | child: Column(
55 | mainAxisAlignment: MainAxisAlignment.center,
56 | crossAxisAlignment: CrossAxisAlignment.center,
57 | children: [
58 | SizedBox(
59 | height: 25,
60 | ),
61 | Image.asset(
62 | 'lib/assets/images/gflogo.png',
63 | width: width * 0.25,
64 | ),
65 | SizedBox(
66 | height: 20,
67 | ),
68 | SizedBox(
69 | child: TextFormField(
70 | validator: (value) {
71 | if (value.isEmpty) {
72 | return 'Please enter username';
73 | }
74 | return null;
75 | },
76 | onSaved: (val) => _username = val,
77 | decoration: InputDecoration(
78 | border: OutlineInputBorder(),
79 | labelText: 'Username'),
80 | ),
81 | ),
82 | SizedBox(
83 | height: 20,
84 | ),
85 | SizedBox(
86 | child: TextFormField(
87 | validator: (value) {
88 | if (value.isEmpty) {
89 | return 'Please enter password';
90 | }
91 | return null;
92 | },
93 | onSaved: (val) => _password = val,
94 | obscureText: true,
95 | decoration: InputDecoration(
96 | border: OutlineInputBorder(),
97 | labelText: 'Password'),
98 | ),
99 | ),
100 | SizedBox(
101 | height: 20,
102 | ),
103 | SizedBox(
104 | child: TextFormField(
105 | validator: (value) {
106 | if (value.isEmpty) {
107 | return 'Please enter url';
108 | }
109 | return null;
110 | },
111 | onSaved: (val) => _url = val,
112 | decoration: InputDecoration(
113 | border: OutlineInputBorder(),
114 | labelText: 'URL',
115 | hintText: "http://example.com"),
116 | ),
117 | ),
118 | SizedBox(
119 | height: 20,
120 | ),
121 | GFButton(
122 | onPressed: () => submit(context),
123 | text: 'login',
124 | color: GFColors.DARK,
125 | ),
126 | ],
127 | ),
128 | ),
129 | ),
130 | ),
131 | )),
132 | );
133 | }
134 |
135 | void submit(context) async {
136 | final prefs = await SharedPreferences.getInstance();
137 | final form = formKey.currentState;
138 | RestDatasource api = new RestDatasource();
139 | if (form.validate()) {
140 | form.save();
141 | _toggleLoadingStatus(true);
142 | api.login(_username.trim(), _password.trim(), _url).then((User user) {
143 | prefs.setString('token', user.tokenType + " " + user.accessToken);
144 | prefs.setString('username', user.username);
145 | prefs.setString('password', user.password);
146 | prefs.setString('baseUrl', user.baseUrl);
147 | _toggleLoadingStatus(false);
148 | Navigator.pop(context);
149 | }).catchError((Object error) {
150 | _toggleLoadingStatus(false);
151 | _showSnackBar(error.toString());
152 | });
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/lib/screens/login/login2.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_auth_buttons/flutter_auth_buttons.dart';
5 | import 'package:getwidget/getwidget.dart';
6 | import 'package:google_sign_in/google_sign_in.dart';
7 | import 'package:openemr/screens/login/create_account.dart';
8 | import 'package:openemr/screens/register/register.dart';
9 | import 'package:openemr/screens/telehealth/telehealth.dart';
10 | import 'package:openemr/utils/customlistloadingshimmer.dart';
11 | import 'package:shared_preferences/shared_preferences.dart';
12 | import '../../models/user.dart';
13 | import 'package:firebase_auth/firebase_auth.dart';
14 |
15 | class LoginFirebaseScreen extends StatefulWidget {
16 | final String snackBarMessage;
17 |
18 | LoginFirebaseScreen({this.snackBarMessage});
19 |
20 | @override
21 | _LoginFirebaseScreenState createState() => _LoginFirebaseScreenState();
22 | }
23 |
24 | class _LoginFirebaseScreenState extends State {
25 | final FirebaseAuth _auth = FirebaseAuth.instance;
26 | final GoogleSignIn googleSignIn = GoogleSignIn();
27 | final userRef = Firestore.instance.collection('username');
28 | User currentUserWithInfo;
29 | User user;
30 | bool _isLoading = false;
31 |
32 | final formKey = new GlobalKey();
33 | String _email, _password;
34 |
35 | @override
36 | void initState() {
37 | Future.delayed(Duration.zero, () {
38 | if (widget.snackBarMessage != null) {
39 | _showSnackBar(widget.snackBarMessage);
40 | }
41 | });
42 | super.initState();
43 | }
44 |
45 | void _showSnackBar(String text) {
46 | ScaffoldMessenger.of(context)
47 | .showSnackBar(new SnackBar(content: new Text(text)));
48 | }
49 |
50 | void _toggleLoadingStatus(bool newLoadingState) {
51 | setState(() {
52 | _isLoading = newLoadingState;
53 | });
54 | }
55 |
56 | @override
57 | Widget build(BuildContext context) {
58 | double width = MediaQuery.of(context).size.width;
59 | return GestureDetector(
60 | onTap: () {
61 | FocusScopeNode currentFocus = FocusScope.of(context);
62 | if (!currentFocus.hasPrimaryFocus) {
63 | currentFocus.unfocus();
64 | }
65 | },
66 | child: Scaffold(
67 | backgroundColor: GFColors.LIGHT,
68 | body: Padding(
69 | padding: EdgeInsets.only(left: width * 0.1, right: width * 0.1),
70 | child: Center(
71 | child: SingleChildScrollView(
72 | child: Form(
73 | key: formKey,
74 | child: Column(
75 | mainAxisAlignment: MainAxisAlignment.center,
76 | crossAxisAlignment: CrossAxisAlignment.center,
77 | children: [
78 | SizedBox(
79 | height: 25,
80 | ),
81 | Image.asset(
82 | 'lib/assets/images/firebase.png',
83 | width: width * 0.25,
84 | ),
85 | SizedBox(
86 | height: 20,
87 | ),
88 | _isLoading
89 | ? customListLoadingShimmer(context,
90 | loadingMessage: 'Authenticating', listLength: 2)
91 | : Column(
92 | children: [
93 | SizedBox(
94 | child: TextFormField(
95 | validator: (value) {
96 | if (value.isEmpty) {
97 | return 'Please enter your email';
98 | }
99 | return null;
100 | },
101 | onSaved: (val) => _email = val,
102 | decoration: InputDecoration(
103 | border: OutlineInputBorder(),
104 | labelText: 'E-mail'),
105 | ),
106 | ),
107 | SizedBox(
108 | height: 20,
109 | ),
110 | SizedBox(
111 | child: TextFormField(
112 | validator: (value) {
113 | if (value.isEmpty) {
114 | return 'Please enter password';
115 | }
116 | return null;
117 | },
118 | onSaved: (val) => _password = val,
119 | obscureText: true,
120 | decoration: InputDecoration(
121 | border: OutlineInputBorder(),
122 | labelText: 'Password'),
123 | ),
124 | ),
125 | SizedBox(
126 | height: 20,
127 | ),
128 | GFButton(
129 | onPressed: () => handleSignIn(context),
130 | text: 'login',
131 | color: GFColors.DARK,
132 | ),
133 | GFButton(
134 | onPressed: () => handleRegister(context),
135 | text: 'Register',
136 | color: GFColors.DARK,
137 | type: GFButtonType.outline2x,
138 | ),
139 | Padding(
140 | padding: EdgeInsets.only(top: 25, bottom: 25),
141 | child: Text(
142 | "-------OR--------",
143 | style: TextStyle(
144 | fontSize: 14,
145 | fontWeight: FontWeight.w300,
146 | color: Colors.grey),
147 | ),
148 | ),
149 | GoogleSignInButton(
150 | onPressed: () {
151 | _toggleLoadingStatus(true);
152 | signInWithGoogle();
153 | },
154 | textStyle: TextStyle(
155 | fontSize: 14.0,
156 | fontWeight: FontWeight.w400,
157 | color: Colors.white,
158 | ),
159 | darkMode: true,
160 | ),
161 | SizedBox(
162 | height: 25,
163 | ),
164 | ],
165 | )
166 | ],
167 | ),
168 | ),
169 | ),
170 | ),
171 | )),
172 | );
173 | }
174 |
175 | void handleSignIn(context) async {
176 | FirebaseUser user;
177 | final form = formKey.currentState;
178 | _toggleLoadingStatus(true);
179 | if (form.validate()) {
180 | form.save();
181 | try {
182 | AuthResult result = await _auth.signInWithEmailAndPassword(
183 | email: _email, password: _password);
184 | user = result.user;
185 | } catch (error) {
186 | if (error.message != null) {
187 | _showSnackBar(error.message);
188 | } else {
189 | _showSnackBar('An unexpected error occured!');
190 | }
191 | _toggleLoadingStatus(false);
192 | return null;
193 | }
194 | }
195 | _toggleLoadingStatus(false);
196 | if (!user.isEmailVerified) {
197 | _showSnackBar("Email not verified");
198 | await _auth.signOut();
199 | } else {
200 | Navigator.pop(context);
201 | }
202 | }
203 |
204 | void handleRegister(context) async {
205 | Navigator.pushReplacement(
206 | context,
207 | MaterialPageRoute(
208 | builder: (BuildContext context) => RegisterFirebaseScreen()),
209 | );
210 | }
211 |
212 | Future signInWithGoogle() async {
213 | final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
214 | handleGSignIn(googleSignInAccount);
215 | }
216 |
217 | handleGSignIn(GoogleSignInAccount googleSignInAccount) async {
218 | if (googleSignInAccount != null) {
219 | final GoogleSignInAuthentication googleSignInAuthentication =
220 | await googleSignInAccount.authentication;
221 |
222 | final AuthCredential credential = GoogleAuthProvider.getCredential(
223 | accessToken: googleSignInAuthentication.accessToken,
224 | idToken: googleSignInAuthentication.idToken,
225 | );
226 |
227 | final AuthResult authResult =
228 | await _auth.signInWithCredential(credential);
229 | final FirebaseUser user = authResult.user;
230 |
231 | try {
232 | await createUserInFirestore(user);
233 | } catch (err) {
234 | _showSnackBar(err);
235 | _toggleLoadingStatus(false);
236 | _signOut();
237 | }
238 |
239 | shredprefUser(user.uid);
240 |
241 | _toggleLoadingStatus(false);
242 | } else {
243 | _showSnackBar('Please try again');
244 | _toggleLoadingStatus(false);
245 | }
246 | }
247 |
248 | Future shredprefUser(String uid) async {
249 | SharedPreferences prefs = await SharedPreferences.getInstance();
250 | prefs.setString('loggedUserId', uid);
251 | }
252 |
253 | createUserInFirestore(FirebaseUser user) async {
254 | DocumentSnapshot documentSnapshot = await userRef.document(user.uid).get();
255 | //go to createAccount page - only for first reigstration
256 | if (!documentSnapshot.exists) {
257 | _toggleLoadingStatus(false);
258 | Navigator.of(context).pushReplacement(MaterialPageRoute(
259 | builder: (context) => CreateAccount(
260 | dispUser: user,
261 | )));
262 | } else {
263 | _toggleLoadingStatus(false);
264 | Navigator.of(context).pushReplacement(
265 | MaterialPageRoute(builder: (context) => Telehealth()));
266 | }
267 | }
268 |
269 | Future _signOut() async {
270 | await googleSignIn.signOut();
271 | await _auth.signOut();
272 |
273 | SharedPreferences prefs = await SharedPreferences.getInstance();
274 | prefs.remove('loggedUserId');
275 | _toggleLoadingStatus(false);
276 | }
277 | }
278 |
--------------------------------------------------------------------------------
/lib/screens/medicine/medicine_recognition_ML_Kit.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:io';
3 | import 'package:http/http.dart' as http;
4 |
5 | import 'package:firebase_ml_vision/firebase_ml_vision.dart';
6 | import 'package:flutter/cupertino.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:getwidget/colors/gf_color.dart';
9 | import 'package:getwidget/components/appbar/gf_appbar.dart';
10 | import 'package:image_picker/image_picker.dart';
11 |
12 | class MedicineRecognitionMLKit extends StatefulWidget {
13 | @override
14 | _MedicineRecognitionMLKitState createState() =>
15 | _MedicineRecognitionMLKitState();
16 | }
17 |
18 | class _MedicineRecognitionMLKitState extends State {
19 | var _imageText = [];
20 | var _drugList = [];
21 | File image;
22 | ImagePicker imagePicker;
23 |
24 | String baseURL = "https://dailymed.nlm.nih.gov/dailymed/services";
25 | String version = "v2";
26 | String endpoint = "drugnames";
27 | String format = "json";
28 | String parameter_1 = "drug_name";
29 |
30 | _callAPI(String drugName) async {
31 | try {
32 | final response = await http
33 | .get('$baseURL/$version/$endpoint\.$format?$parameter_1=$drugName');
34 | print(response);
35 | if (response.statusCode == 200) {
36 | var data = json.decode(response.body);
37 | if (data.isNotEmpty) {
38 | int drugCount = data["metadata"]["total_elements"];
39 | if (drugCount > 0) {
40 | setState(() {
41 | _drugList.add(drugName);
42 | });
43 | }
44 | }
45 | }
46 | } on Exception catch (_) {
47 | setState(() {
48 | _drugList.clear();
49 | _drugList.add("No data available");
50 | });
51 | }
52 | }
53 |
54 | captureFromCamera() async {
55 | setState(() {
56 | image = null;
57 | _imageText.clear();
58 | _drugList.clear();
59 | });
60 | try {
61 | PickedFile pickedFile =
62 | await imagePicker.getImage(source: ImageSource.camera);
63 | File imageNew = File(pickedFile.path);
64 | setState(() {
65 | image = imageNew;
66 | convertImageToText();
67 | });
68 | } on Exception catch (e) {
69 | //print e to snackbar
70 | print(e);
71 | }
72 | }
73 |
74 | chooseFromGalery() async {
75 | setState(() {
76 | image = null;
77 | _imageText.clear();
78 | _drugList.clear();
79 | });
80 | try {
81 | PickedFile pickedFile =
82 | await imagePicker.getImage(source: ImageSource.gallery);
83 | File imageNew = File(pickedFile.path);
84 | setState(() {
85 | image = imageNew;
86 | convertImageToText();
87 | });
88 | } on Exception catch (e) {
89 | print(e);
90 | }
91 | }
92 |
93 | convertImageToText() async {
94 | final FirebaseVisionImage firebaseVisionImage =
95 | FirebaseVisionImage.fromFile(image);
96 | final TextRecognizer textRecognizer =
97 | FirebaseVision.instance.textRecognizer();
98 | VisionText visionText =
99 | await textRecognizer.processImage(firebaseVisionImage);
100 |
101 | setState(() {
102 | for (TextBlock textBlock in visionText.blocks) {
103 | for (TextLine textLine in textBlock.lines) {
104 | for (TextElement textElement in textLine.elements) {
105 | //remove all special symbols from string
106 | _imageText
107 | .add(textElement.text.replaceAll(new RegExp(r'[^\w\s]+'), ''));
108 | }
109 | }
110 | }
111 | //remove duplicates
112 | _imageText = _imageText.toSet().toList();
113 | for (String item in _imageText) {
114 | _callAPI(item);
115 | }
116 | });
117 | }
118 |
119 | @override
120 | void initState() {
121 | super.initState();
122 |
123 | imagePicker = ImagePicker();
124 | }
125 |
126 | @override
127 | Widget build(BuildContext context) {
128 | return Scaffold(
129 | appBar: GFAppBar(
130 | backgroundColor: GFColors.DARK,
131 | leading: InkWell(
132 | onTap: () {
133 | Navigator.pop(context);
134 | },
135 | child: Container(
136 | child: Icon(
137 | CupertinoIcons.back,
138 | color: GFColors.SUCCESS,
139 | ),
140 | )),
141 | title: const Text(
142 | 'Medicine Recognition',
143 | style: TextStyle(fontSize: 17),
144 | ),
145 | centerTitle: true,
146 | ),
147 | body: Center(
148 | child: SingleChildScrollView(
149 | child: Padding(
150 | padding: EdgeInsets.all(40.0),
151 | child: (image == null)
152 | ? Icon(
153 | Icons.search,
154 | size: 150,
155 | color: Colors.grey.withOpacity(0.44),
156 | )
157 | : Column(
158 | children: [
159 | Text(
160 | "Captured Image",
161 | style: TextStyle(
162 | fontSize: 20,
163 | fontWeight: FontWeight.w400,
164 | color: Colors.purple),
165 | ),
166 | SizedBox(height: 10.0),
167 | Image.file(
168 | image,
169 | width: 140,
170 | height: 192,
171 | fit: BoxFit.fill,
172 | ),
173 | SizedBox(height: 20.0),
174 |
175 | Text(
176 | "All words found in the image",
177 | style: TextStyle(
178 | fontSize: 20,
179 | fontWeight: FontWeight.w400,
180 | color: Colors.purple),
181 | ),
182 | SizedBox(height: 10.0),
183 | Text(_imageText.toString()),
184 | SizedBox(height: 20.0),
185 | Text(
186 | "Medicine words",
187 | style: TextStyle(
188 | fontSize: 20,
189 | fontWeight: FontWeight.w400,
190 | color: Colors.purple),
191 | ),
192 | SizedBox(height: 10.0),
193 | Text(_drugList.toString()),
194 | // (_drugs.isNotEmpty) ? Text(_drugs) : Text("No data available"),
195 | ],
196 | ),
197 | ),
198 | ),
199 | ),
200 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
201 | floatingActionButton: Padding(
202 | padding: const EdgeInsets.all(8.0),
203 | child: Row(
204 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
205 | children: [
206 | FloatingActionButton(
207 | heroTag: null,
208 | backgroundColor: GFColors.DARK,
209 | onPressed: () async {
210 | captureFromCamera();
211 | },
212 | child: Icon(Icons.camera),
213 | ),
214 | FloatingActionButton(
215 | heroTag: null,
216 | backgroundColor: GFColors.DARK,
217 | onPressed: () async {
218 | chooseFromGalery();
219 | },
220 | child: Icon(Icons.file_upload),
221 | )
222 | ],
223 | ),
224 | ));
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/lib/screens/patientList/patient_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:getwidget/getwidget.dart';
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:openemr/models/patient.dart';
5 | import 'package:openemr/screens/addpatient/add_patient.dart';
6 | import 'package:openemr/utils/rest_ds.dart';
7 | import 'package:shared_preferences/shared_preferences.dart';
8 |
9 | class PatientListPage extends StatefulWidget {
10 | @override
11 | _PatientListPageState createState() => _PatientListPageState();
12 | }
13 |
14 | class _PatientListPageState extends State {
15 | @override
16 | void initState() {
17 | fetchList();
18 | super.initState();
19 | }
20 |
21 | fetchList() async {
22 | historyPatientId = [];
23 | starredPatientId = [];
24 |
25 | patientList = [];
26 | historyPatientList = [];
27 | starredPatientList = [];
28 |
29 | RestDatasource api = new RestDatasource();
30 | final prefs = await SharedPreferences.getInstance();
31 | historyPatientId = prefs.getStringList("historyPatient") == null
32 | ? []
33 | : prefs.getStringList("historyPatient");
34 | starredPatientId = prefs.getStringList("starredPatient") == null
35 | ? []
36 | : prefs.getStringList("starredPatient");
37 | api
38 | .getPatientList(prefs.getString('baseUrl'), prefs.getString('token'))
39 | .then((List list) {
40 | list.forEach((element) {
41 | if (historyPatientId.indexOf(element.pid) != -1) {
42 | historyPatientList.add(element);
43 | }
44 | if (starredPatientId.indexOf(element.pid) != -1) {
45 | starredPatientList.add(element);
46 | }
47 | });
48 | setState(() {
49 | starredPatientList = starredPatientList;
50 | historyPatientList = historyPatientList;
51 | patientList = list;
52 | });
53 | }).catchError((Object error) => print(error.toString()));
54 | }
55 |
56 | List historyPatientId = [];
57 | List starredPatientId = [];
58 |
59 | List patientList = [];
60 | List historyPatientList = [];
61 | List starredPatientList = [];
62 |
63 | @override
64 | Widget build(BuildContext context) => Scaffold(
65 | appBar: GFAppBar(
66 | backgroundColor: GFColors.DARK,
67 | leading: InkWell(
68 | onTap: () {
69 | Navigator.pop(context);
70 | },
71 | child: Container(
72 | child: Icon(
73 | CupertinoIcons.back,
74 | color: GFColors.SUCCESS,
75 | ),
76 | )),
77 | title: const Text(
78 | 'Patient List',
79 | style: TextStyle(fontSize: 17),
80 | ),
81 | centerTitle: true,
82 | actions: [
83 | IconButton(
84 | icon: Icon(Icons.refresh),
85 | tooltip: "Refresh",
86 | color: GFColors.SUCCESS,
87 | onPressed: fetchList,
88 | ),
89 | IconButton(
90 | icon: Icon(Icons.add),
91 | tooltip: "Add Patient",
92 | color: GFColors.SUCCESS,
93 | onPressed: () {
94 | Navigator.push(
95 | context,
96 | MaterialPageRoute(
97 | builder: (BuildContext context) => AddPatientScreen()),
98 | );
99 | },
100 | ),
101 | IconButton(
102 | icon: Icon(Icons.exit_to_app),
103 | tooltip: "Logout",
104 | color: GFColors.DANGER,
105 | onPressed: () async {
106 | final prefs = await SharedPreferences.getInstance();
107 | prefs.remove('token');
108 | prefs.remove('username');
109 | prefs.remove('password');
110 | prefs.remove('baseUrl');
111 | Navigator.pushNamedAndRemoveUntil(context, '/', (_) => false);
112 | },
113 | )
114 | ],
115 | ),
116 | body: ListView(
117 | physics: const ScrollPhysics(),
118 | children: [
119 | GFSearchBar(
120 | searchList: patientList,
121 | searchQueryBuilder: (query, list) => list
122 | .where((item) =>
123 | item.fname.toLowerCase().contains(query.toLowerCase()))
124 | .toList(),
125 | overlaySearchListItemBuilder: (item) => Container(
126 | padding: const EdgeInsets.all(8),
127 | child: Row(
128 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
129 | children: [
130 | Text(
131 | item.fname,
132 | style: const TextStyle(fontSize: 18),
133 | )
134 | ],
135 | ),
136 | ),
137 | onItemSelected: (item) async {
138 | if (item != null) {
139 | final prefs = await SharedPreferences.getInstance();
140 | historyPatientId.remove(item.pid.toString());
141 | historyPatientId.insert(0, item.pid.toString());
142 | historyPatientList.remove(item);
143 | historyPatientList.insert(0, item);
144 | prefs.setStringList("historyPatient", historyPatientId);
145 | if (historyPatientList.length > 10) {
146 | historyPatientList.removeLast();
147 | }
148 | }
149 | this.setState(() {
150 | historyPatientList = historyPatientList;
151 | });
152 | }),
153 | const Padding(
154 | padding: EdgeInsets.only(left: 15, top: 30, bottom: 10),
155 | child: GFTypography(
156 | text: 'Starred Patient',
157 | type: GFTypographyType.typo5,
158 | dividerWidth: 25,
159 | dividerColor: Color(0xFF19CA4B),
160 | ),
161 | ),
162 | ListView.builder(
163 | shrinkWrap: true,
164 | physics: ClampingScrollPhysics(),
165 | itemCount: starredPatientList.length,
166 | itemBuilder: (item, i) {
167 | Patient p = starredPatientList[i];
168 | return GFListTile(
169 | titleText: p.fname + " " + p.lname,
170 | subtitleText: p.sex,
171 | icon: GFIconButton(
172 | onPressed: () async {
173 | final prefs = await SharedPreferences.getInstance();
174 | starredPatientId.remove(p.pid.toString());
175 | starredPatientList.remove(p);
176 | prefs.setStringList("starredPatient", starredPatientId);
177 | this.setState(() {
178 | starredPatientList = starredPatientList;
179 | });
180 | },
181 | icon: Icon(
182 | Icons.star,
183 | color: GFColors.DANGER,
184 | ),
185 | type: GFButtonType.transparent,
186 | ),
187 | );
188 | },
189 | ),
190 | const Padding(
191 | padding: EdgeInsets.only(left: 15, top: 30, bottom: 10),
192 | child: GFTypography(
193 | text: 'History',
194 | type: GFTypographyType.typo5,
195 | dividerWidth: 25,
196 | dividerColor: Color(0xFF19CA4B),
197 | ),
198 | ),
199 | ListView.builder(
200 | shrinkWrap: true,
201 | physics: ClampingScrollPhysics(),
202 | itemCount: historyPatientList.length,
203 | itemBuilder: (item, i) {
204 | Patient p = historyPatientList[i];
205 | return GFListTile(
206 | titleText: (p.fname != null ? p.fname + " " : "") +
207 | (p.mname != null ? p.mname + " " : "") +
208 | (p.lname != null ? p.lname + " " : ""),
209 | subtitleText: p.sex,
210 | icon: GFIconButton(
211 | onPressed: () async {
212 | final prefs = await SharedPreferences.getInstance();
213 | starredPatientId.insert(0, p.pid.toString());
214 | starredPatientList.insert(0, p);
215 | prefs.setStringList("starredPatient", starredPatientId);
216 | this.setState(() {
217 | starredPatientList = starredPatientList;
218 | });
219 | },
220 | icon: Icon(
221 | Icons.star_border,
222 | color: GFColors.DANGER,
223 | ),
224 | type: GFButtonType.transparent,
225 | ),
226 | );
227 | },
228 | ),
229 | ],
230 | ),
231 | );
232 | }
233 |
--------------------------------------------------------------------------------
/lib/screens/ppg/chart.dart:
--------------------------------------------------------------------------------
1 | import 'package:charts_flutter/flutter.dart' as charts;
2 | import 'package:flutter/material.dart';
3 |
4 | class Chart extends StatelessWidget {
5 | final List _data;
6 |
7 | Chart(this._data);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return new charts.TimeSeriesChart([
12 | charts.Series(
13 | id: 'Values',
14 | colorFn: (_, __) => charts.MaterialPalette.green.shadeDefault,
15 | domainFn: (SensorValue values, _) => values.time,
16 | measureFn: (SensorValue values, _) => values.value,
17 | data: _data,
18 | )
19 | ],
20 | animate: false,
21 | primaryMeasureAxis: charts.NumericAxisSpec(
22 | tickProviderSpec:
23 | charts.BasicNumericTickProviderSpec(zeroBound: false),
24 | renderSpec: charts.NoneRenderSpec(),
25 | ),
26 | domainAxis: new charts.DateTimeAxisSpec(
27 | renderSpec: new charts.NoneRenderSpec()));
28 | }
29 | }
30 |
31 | class SensorValue {
32 | final DateTime time;
33 | final double value;
34 |
35 | SensorValue(this.time, this.value);
36 | }
--------------------------------------------------------------------------------
/lib/screens/ppg/heartRate.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:camera/camera.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:wakelock/wakelock.dart';
6 | import 'chart.dart';
7 | import 'package:getwidget/getwidget.dart';
8 | import 'package:flutter/cupertino.dart';
9 |
10 | class PPG extends StatefulWidget {
11 | @override
12 | PPGView createState() {
13 | return PPGView();
14 | }
15 | }
16 |
17 | class PPGView extends State with SingleTickerProviderStateMixin {
18 | bool _toggled = false; // toggle button value
19 | List _data =
20 | List.empty(growable: true); // array to store the values
21 | CameraController _controller;
22 | double _alpha = 0.3; // factor for the mean value
23 | AnimationController _animationController;
24 | double _iconScale = 1;
25 | int _bpm = 0; // beats per minute
26 | int _fs = 30; // sampling frequency (fps)
27 | int _windowLen = 30 * 6; // window length to display - 6 seconds
28 | CameraImage _image; // store the last camera image
29 | double _avg; // store the average value during calculation
30 | DateTime _now; // store the now Datetime
31 | Timer _timer; // timer for image processing
32 |
33 | @override
34 | void initState() {
35 | super.initState();
36 | _animationController =
37 | AnimationController(duration: Duration(milliseconds: 500), vsync: this);
38 | _animationController
39 | ..addListener(() {
40 | setState(() {
41 | _iconScale = 1.0 + _animationController.value * 0.4;
42 | });
43 | });
44 | }
45 |
46 | @override
47 | void dispose() {
48 | _timer?.cancel();
49 | _toggled = false;
50 | _disposeController();
51 | Wakelock.disable();
52 | _animationController?.stop();
53 | _animationController?.dispose();
54 | super.dispose();
55 | }
56 |
57 | @override
58 | Widget build(BuildContext context) {
59 | return Scaffold(
60 | backgroundColor: Colors.white,
61 | appBar: GFAppBar(
62 | backgroundColor: GFColors.DARK,
63 | leading: InkWell(
64 | onTap: () {
65 | Navigator.pop(context);
66 | },
67 | child: Container(
68 | child: Icon(
69 | CupertinoIcons.back,
70 | color: GFColors.SUCCESS,
71 | ),
72 | )),
73 | title: const Text(
74 | 'Heart-rate Monitor',
75 | style: TextStyle(fontSize: 17),
76 | ),
77 | centerTitle: true,
78 | ),
79 | body: SafeArea(
80 | child: Column(
81 | children: [
82 | Expanded(
83 | flex: 1,
84 | child: Row(
85 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
86 | children: [
87 | Expanded(
88 | flex: 1,
89 | child: Padding(
90 | padding: EdgeInsets.all(12),
91 | child: ClipRRect(
92 | borderRadius: BorderRadius.all(
93 | Radius.circular(18),
94 | ),
95 | child: Stack(
96 | fit: StackFit.expand,
97 | alignment: Alignment.center,
98 | children: [
99 | _controller != null && _toggled
100 | ? AspectRatio(
101 | aspectRatio:
102 | _controller.value.aspectRatio,
103 | child: CameraPreview(_controller),
104 | )
105 | : Container(
106 | padding: EdgeInsets.all(12),
107 | alignment: Alignment.center,
108 | color: Colors.grey,
109 | ),
110 | Container(
111 | alignment: Alignment.center,
112 | padding: EdgeInsets.all(4),
113 | child: Text(
114 | _toggled
115 | ? "Cover both the camera and the flash with your finger"
116 | : "Camera feed will display here",
117 | style: TextStyle(
118 | backgroundColor: _toggled
119 | ? Colors.white
120 | : Colors.transparent),
121 | textAlign: TextAlign.center,
122 | ),
123 | )
124 | ],
125 | ),
126 | ),
127 | ),
128 | ),
129 | Expanded(
130 | flex: 1,
131 | child: Center(
132 | child: Column(
133 | mainAxisSize: MainAxisSize.min,
134 | crossAxisAlignment: CrossAxisAlignment.center,
135 | children: [
136 | Text(
137 | "Estimated BPM",
138 | style: TextStyle(fontSize: 18, color: Colors.grey),
139 | ),
140 | Text(
141 | (_bpm > 30 && _bpm < 150 ? _bpm.toString() : "--"),
142 | style: TextStyle(
143 | fontSize: 32, fontWeight: FontWeight.bold),
144 | ),
145 | ],
146 | )),
147 | ),
148 | ],
149 | )),
150 | Expanded(
151 | flex: 1,
152 | child: Center(
153 | child: Transform.scale(
154 | scale: _iconScale,
155 | child: IconButton(
156 | icon:
157 | Icon(_toggled ? Icons.favorite : Icons.favorite_border),
158 | color: Colors.red,
159 | iconSize: 128,
160 | onPressed: () {
161 | if (_toggled) {
162 | _untoggle();
163 | } else {
164 | _toggle();
165 | }
166 | },
167 | ),
168 | ),
169 | ),
170 | ),
171 | Expanded(
172 | flex: 1,
173 | child: Container(
174 | margin: EdgeInsets.all(12),
175 | decoration: BoxDecoration(
176 | borderRadius: BorderRadius.all(
177 | Radius.circular(18),
178 | ),
179 | color: Colors.black),
180 | child: Chart(_data),
181 | ),
182 | ),
183 | ],
184 | ),
185 | ),
186 | );
187 | }
188 |
189 | void _clearData() {
190 | // create array of 128 ~= 255/2
191 | _data.clear();
192 | int now = DateTime.now().millisecondsSinceEpoch;
193 | for (int i = 0; i < _windowLen; i++)
194 | _data.insert(
195 | 0,
196 | SensorValue(
197 | DateTime.fromMillisecondsSinceEpoch(now - i * 1000 ~/ _fs), 128));
198 | }
199 |
200 | void _toggle() {
201 | _clearData();
202 | _initController().then((onValue) {
203 | Wakelock.enable();
204 | _animationController?.repeat(reverse: true);
205 | setState(() {
206 | _toggled = true;
207 | });
208 | // after is toggled
209 | _initTimer();
210 | _updateBPM();
211 | });
212 | }
213 |
214 | void _untoggle() {
215 | _disposeController();
216 | Wakelock.disable();
217 | _animationController?.stop();
218 | _animationController?.value = 0.0;
219 | setState(() {
220 | _toggled = false;
221 | });
222 | }
223 |
224 | void _disposeController() {
225 | _controller?.dispose();
226 | _controller = null;
227 | }
228 |
229 | Future _initController() async {
230 | try {
231 | List _cameras = await availableCameras();
232 | _controller = CameraController(_cameras.first, ResolutionPreset.low);
233 | await _controller.initialize();
234 | Future.delayed(Duration(milliseconds: 100)).then((onValue) {
235 | _controller.setFlashMode(FlashMode.torch);
236 | });
237 | _controller.startImageStream((CameraImage image) {
238 | _image = image;
239 | });
240 | } catch (Exception) {
241 | debugPrint(Exception);
242 | }
243 | }
244 |
245 | void _initTimer() {
246 | _timer = Timer.periodic(Duration(milliseconds: 1000 ~/ _fs), (timer) {
247 | if (_toggled) {
248 | if (_image != null) _scanImage(_image);
249 | } else {
250 | timer.cancel();
251 | }
252 | });
253 | }
254 |
255 | void _scanImage(CameraImage image) {
256 | _now = DateTime.now();
257 | _avg =
258 | image.planes.first.bytes.reduce((value, element) => value + element) /
259 | image.planes.first.bytes.length;
260 | if (_data.length >= _windowLen) {
261 | _data.removeAt(0);
262 | }
263 | setState(() {
264 | _data.add(SensorValue(_now, _avg));
265 | });
266 | }
267 |
268 | void _updateBPM() async {
269 | // Bear in mind that the method used to calculate the BPM is very rudimentar
270 | // feel free to improve it :)
271 |
272 | // Since this function doesn't need to be so "exact" regarding the time it executes,
273 | // I only used the a Future.delay to repeat it from time to time.
274 | // Ofc you can also use a Timer object to time the callback of this function
275 | List _values;
276 | double _avg;
277 | int _n;
278 | double _m;
279 | double _threshold;
280 | double _bpm;
281 | int _counter;
282 | int _previous;
283 | while (_toggled) {
284 | _values = List.from(_data); // create a copy of the current data array
285 | _avg = 0;
286 | _n = _values.length;
287 | _m = 0;
288 | _values.forEach((SensorValue value) {
289 | _avg += value.value / _n;
290 | if (value.value > _m) _m = value.value;
291 | });
292 | _threshold = (_m + _avg) / 2;
293 | _bpm = 0;
294 | _counter = 0;
295 | _previous = 0;
296 | for (int i = 1; i < _n; i++) {
297 | if (_values[i - 1].value < _threshold &&
298 | _values[i].value > _threshold) {
299 | if (_previous != 0) {
300 | _counter++;
301 | _bpm += 60 *
302 | 1000 /
303 | (_values[i].time.millisecondsSinceEpoch - _previous);
304 | }
305 | _previous = _values[i].time.millisecondsSinceEpoch;
306 | }
307 | }
308 | if (_counter > 0) {
309 | _bpm = _bpm / _counter;
310 | print(_bpm);
311 | setState(() {
312 | this._bpm = ((1 - _alpha) * _bpm + _alpha * _bpm).toInt();
313 | });
314 | }
315 | await Future.delayed(Duration(
316 | milliseconds:
317 | 1000 * _windowLen ~/ _fs)); // wait for a new set of _data values
318 | }
319 | }
320 | }
321 |
--------------------------------------------------------------------------------
/lib/screens/register/register.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_auth_buttons/flutter_auth_buttons.dart';
4 | import 'package:getwidget/getwidget.dart';
5 | import 'package:google_sign_in/google_sign_in.dart';
6 | import 'package:openemr/screens/login/create_account.dart';
7 | import 'package:openemr/screens/login/login2.dart';
8 | import 'package:openemr/screens/telehealth/telehealth.dart';
9 | import 'package:openemr/utils/customlistloadingshimmer.dart';
10 | import 'package:shared_preferences/shared_preferences.dart';
11 | import '../../models/user.dart';
12 | import 'package:firebase_auth/firebase_auth.dart';
13 | import 'package:cloud_firestore/cloud_firestore.dart';
14 |
15 | class RegisterFirebaseScreen extends StatefulWidget {
16 | @override
17 | _RegisterFirebaseScreenState createState() => _RegisterFirebaseScreenState();
18 | }
19 |
20 | class _RegisterFirebaseScreenState extends State {
21 | final FirebaseAuth _auth = FirebaseAuth.instance;
22 | final Firestore _store = Firestore.instance;
23 | final GoogleSignIn googleSignIn = GoogleSignIn();
24 | final userRef = Firestore.instance.collection('username');
25 | User user;
26 | bool _isLoading = false;
27 |
28 | final formKey = new GlobalKey();
29 | String _email, _password, _name, _userid;
30 |
31 | void _showSnackBar(String text) {
32 | ScaffoldMessenger.of(context)
33 | .showSnackBar(new SnackBar(content: new Text(text)));
34 | }
35 |
36 | void _toggleLoadingStatus(bool newLoadingState) {
37 | setState(() {
38 | _isLoading = newLoadingState;
39 | });
40 | }
41 |
42 | @override
43 | Widget build(BuildContext context) {
44 | double width = MediaQuery.of(context).size.width;
45 | return GestureDetector(
46 | onTap: () {
47 | FocusScopeNode currentFocus = FocusScope.of(context);
48 | if (!currentFocus.hasPrimaryFocus) {
49 | currentFocus.unfocus();
50 | }
51 | },
52 | child: Scaffold(
53 | backgroundColor: GFColors.LIGHT,
54 | body: Padding(
55 | padding: EdgeInsets.only(left: width * 0.1, right: width * 0.1),
56 | child: Center(
57 | child: SingleChildScrollView(
58 | child: Form(
59 | key: formKey,
60 | child: Column(
61 | mainAxisAlignment: MainAxisAlignment.center,
62 | crossAxisAlignment: CrossAxisAlignment.center,
63 | children: [
64 | SizedBox(
65 | height: 25,
66 | ),
67 | Image.asset(
68 | 'lib/assets/images/firebase.png',
69 | width: width * 0.25,
70 | ),
71 | SizedBox(
72 | height: 20,
73 | ),
74 | _isLoading
75 | ? customListLoadingShimmer(context,
76 | loadingMessage: 'Authenticating', listLength: 4)
77 | : Column(
78 | children: [
79 | SizedBox(
80 | child: TextFormField(
81 | validator: (value) {
82 | if (value.isEmpty) {
83 | return 'Please enter your full name';
84 | }
85 | return null;
86 | },
87 | onSaved: (val) => _name = val,
88 | decoration: InputDecoration(
89 | border: OutlineInputBorder(),
90 | labelText: 'Full Name'),
91 | ),
92 | ),
93 | SizedBox(
94 | height: 20,
95 | ),
96 | SizedBox(
97 | child: TextFormField(
98 | validator: (value) {
99 | if (value.isEmpty) {
100 | return 'Username can\'t be blank';
101 | }
102 | return null;
103 | },
104 | onSaved: (val) => _userid = val,
105 | decoration: InputDecoration(
106 | border: OutlineInputBorder(),
107 | labelText: 'Username'),
108 | ),
109 | ),
110 | SizedBox(
111 | height: 20,
112 | ),
113 | SizedBox(
114 | child: TextFormField(
115 | validator: (value) {
116 | if (value.isEmpty) {
117 | return 'Please enter your email';
118 | }
119 | return null;
120 | },
121 | onSaved: (val) => _email = val,
122 | decoration: InputDecoration(
123 | border: OutlineInputBorder(),
124 | labelText: 'E-mail'),
125 | ),
126 | ),
127 | SizedBox(
128 | height: 20,
129 | ),
130 | SizedBox(
131 | child: TextFormField(
132 | validator: (value) {
133 | if (value.isEmpty) {
134 | return 'Please enter password';
135 | }
136 | return null;
137 | },
138 | onSaved: (val) => _password = val,
139 | obscureText: true,
140 | decoration: InputDecoration(
141 | border: OutlineInputBorder(),
142 | labelText: 'Password'),
143 | ),
144 | ),
145 | SizedBox(
146 | height: 20,
147 | ),
148 | GFButton(
149 | onPressed: () => handleRegister(context),
150 | text: 'Register',
151 | color: GFColors.DARK,
152 | ),
153 | GFButton(
154 | onPressed: () => handleSignIn(context),
155 | text: 'login',
156 | color: GFColors.DARK,
157 | type: GFButtonType.outline2x,
158 | ),
159 | Padding(
160 | padding: EdgeInsets.only(top: 25, bottom: 25),
161 | child: Text(
162 | "-------OR--------",
163 | style: TextStyle(
164 | fontSize: 14,
165 | fontWeight: FontWeight.w300,
166 | color: Colors.grey),
167 | ),
168 | ),
169 | GoogleSignInButton(
170 | onPressed: () {
171 | _toggleLoadingStatus(true);
172 | signInWithGoogle();
173 | },
174 | textStyle: TextStyle(
175 | fontSize: 14.0,
176 | fontWeight: FontWeight.w400,
177 | color: Colors.white,
178 | ),
179 | darkMode: true,
180 | ),
181 | SizedBox(
182 | height: 25,
183 | ),
184 | ],
185 | )
186 | ],
187 | ),
188 | ),
189 | ),
190 | ),
191 | )),
192 | );
193 | }
194 |
195 | void handleSignIn(context) async {
196 | Navigator.pushReplacement(
197 | context,
198 | MaterialPageRoute(
199 | builder: (BuildContext context) => LoginFirebaseScreen()),
200 | );
201 | }
202 |
203 | void handleRegister(context) async {
204 | FirebaseUser user;
205 | final form = formKey.currentState;
206 | if (form.validate()) {
207 | form.save();
208 | _toggleLoadingStatus(true);
209 | QuerySnapshot ref = await _store
210 | .collection('username')
211 | .where("id", isEqualTo: _userid)
212 | .snapshots()
213 | .first;
214 | if (ref.documentChanges.isNotEmpty) {
215 | _toggleLoadingStatus(false);
216 | _showSnackBar("Username already exist");
217 | return null;
218 | }
219 | try {
220 | AuthResult result = await _auth.createUserWithEmailAndPassword(
221 | email: _email, password: _password);
222 | user = result.user;
223 | } catch (error) {
224 | if (error.message != null) {
225 | _showSnackBar(error.message);
226 | } else {
227 | _showSnackBar('An unexpected error occured!');
228 | }
229 | _toggleLoadingStatus(false);
230 | return null;
231 | }
232 | }
233 | await _store
234 | .collection('username')
235 | .document(user.uid)
236 | .setData({"id": _userid, "name": _name});
237 | try {
238 | UserUpdateInfo updateInfo = UserUpdateInfo();
239 | updateInfo.displayName = _name;
240 | await user.updateProfile(updateInfo);
241 | } catch (error) {
242 | if (error.message != null) {
243 | _showSnackBar(error.message);
244 | } else {
245 | _showSnackBar('An unexpected error occured!');
246 | }
247 | return null;
248 | }
249 | await user.sendEmailVerification();
250 | await _auth.signOut();
251 | _toggleLoadingStatus(false);
252 | Navigator.pushReplacement(
253 | context,
254 | MaterialPageRoute(
255 | builder: (BuildContext context) => LoginFirebaseScreen(
256 | snackBarMessage:
257 | 'A verification link has been sent to your e-mail account'),
258 | ),
259 | );
260 | }
261 |
262 | Future signInWithGoogle() async {
263 | final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
264 | handleGSignIn(googleSignInAccount);
265 | }
266 |
267 | handleGSignIn(GoogleSignInAccount googleSignInAccount) async {
268 | if (googleSignInAccount != null) {
269 | final GoogleSignInAuthentication googleSignInAuthentication =
270 | await googleSignInAccount.authentication;
271 |
272 | final AuthCredential credential = GoogleAuthProvider.getCredential(
273 | accessToken: googleSignInAuthentication.accessToken,
274 | idToken: googleSignInAuthentication.idToken,
275 | );
276 |
277 | final AuthResult authResult =
278 | await _auth.signInWithCredential(credential);
279 | final FirebaseUser user = authResult.user;
280 |
281 | try {
282 | await createUserInFirestore(user);
283 | } catch (err) {
284 | _showSnackBar(err);
285 | _toggleLoadingStatus(false);
286 | _signOut();
287 | }
288 |
289 | shredprefUser(user.uid);
290 |
291 | _toggleLoadingStatus(false);
292 | } else {
293 | _showSnackBar('Please try again');
294 | _toggleLoadingStatus(false);
295 | }
296 | }
297 |
298 | Future shredprefUser(String uid) async {
299 | SharedPreferences prefs = await SharedPreferences.getInstance();
300 | prefs.setString('loggedUserId', uid);
301 | }
302 |
303 | createUserInFirestore(FirebaseUser user) async {
304 | DocumentSnapshot documentSnapshot = await userRef.document(user.uid).get();
305 | //go to createAccount page - only for first reigstration
306 | if (!documentSnapshot.exists) {
307 | _toggleLoadingStatus(false);
308 | Navigator.of(context).pushReplacement(MaterialPageRoute(
309 | builder: (context) => CreateAccount(
310 | dispUser: user,
311 | )));
312 | } else {
313 | _toggleLoadingStatus(false);
314 | Navigator.of(context).pushReplacement(
315 | MaterialPageRoute(builder: (context) => Telehealth()));
316 | }
317 | }
318 |
319 | Future _signOut() async {
320 | await googleSignIn.signOut();
321 | await _auth.signOut();
322 |
323 | SharedPreferences prefs = await SharedPreferences.getInstance();
324 | prefs.remove('loggedUserId');
325 | _toggleLoadingStatus(false);
326 | }
327 | }
328 |
--------------------------------------------------------------------------------
/lib/screens/shimmer/shimmer.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:getwidget/getwidget.dart';
4 |
5 | class ShimmerPage extends StatefulWidget {
6 | @override
7 | _ShimmerPageState createState() => _ShimmerPageState();
8 | }
9 |
10 | class _ShimmerPageState extends State {
11 | @override
12 | Widget build(BuildContext context) => Scaffold(
13 | appBar: AppBar(
14 | backgroundColor: GFColors.DARK,
15 | leading: InkWell(
16 | onTap: () {
17 | Navigator.pop(context);
18 | },
19 | child: Icon(
20 | CupertinoIcons.back,
21 | color: GFColors.SUCCESS,
22 | ),
23 | ),
24 | title: const Text(
25 | 'Shimmer',
26 | style: TextStyle(fontSize: 17),
27 | ),
28 | centerTitle: true,
29 | ),
30 | body: Column(children: [
31 | const Padding(
32 | padding: EdgeInsets.only(left: 15, top: 30, bottom: 20),
33 | child: GFTypography(
34 | text: 'Basic Shimmer Effect',
35 | type: GFTypographyType.typo5,
36 | dividerWidth: 25,
37 | dividerColor: Color(0xFF19CA4B),
38 | ),
39 | ),
40 | GFShimmer(
41 | mainColor: GFColors.DARK.withOpacity(0.22),
42 | child: Padding(
43 | padding: const EdgeInsets.symmetric(horizontal: 16),
44 | child: Row(
45 | crossAxisAlignment: CrossAxisAlignment.start,
46 | children: [
47 | Container(
48 | width: 80,
49 | height: 80,
50 | color: Colors.white,
51 | ),
52 | const Padding(
53 | padding: EdgeInsets.symmetric(horizontal: 6),
54 | ),
55 | Expanded(
56 | child: Column(
57 | crossAxisAlignment: CrossAxisAlignment.start,
58 | children: [
59 | Container(
60 | width: double.infinity,
61 | height: 12,
62 | color: Colors.white,
63 | ),
64 | const Padding(
65 | padding: EdgeInsets.symmetric(vertical: 2),
66 | ),
67 | Container(
68 | width: MediaQuery.of(context).size.width * 0.5,
69 | height: 12,
70 | color: Colors.white,
71 | ),
72 | const Padding(
73 | padding: EdgeInsets.symmetric(vertical: 2),
74 | ),
75 | Container(
76 | width: MediaQuery.of(context).size.width * 0.25,
77 | height: 12,
78 | color: Colors.white,
79 | ),
80 | ],
81 | ),
82 | )
83 | ],
84 | ),
85 | ),
86 | ),
87 | // const Padding(
88 | // padding: EdgeInsets.only(left: 15, top: 30, bottom: 20),
89 | // child: GFTypography(
90 | // text: 'Shimmer Effect on Text',
91 | // type: GFTypographyType.typo5,
92 | // dividerWidth: 25,
93 | // dividerColor: Color(0xFF19CA4B),
94 | // ),
95 | // ),
96 | // GFShimmer(
97 | // child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
98 | // const Text(
99 | // 'Hurray!! Order Placed',
100 | // style: TextStyle(fontSize: 25, fontWeight: FontWeight.w700),
101 | // ),
102 | // const SizedBox(width: 5),
103 | // Icon(Icons.sentiment_very_satisfied),
104 | // ]),
105 | // direction: GFShimmerDirection.rightToLeft,
106 | // showGradient: true,
107 | // gradient: LinearGradient(
108 | // begin: Alignment.bottomRight,
109 | // end: Alignment.centerLeft,
110 | // stops: const [0, 0.3, 0.6, 0.9, 1],
111 | // colors: const [
112 | // GFColors.DANGER,
113 | // GFColors.PRIMARY,
114 | // GFColors.WARNING,
115 | // GFColors.SECONDARY,
116 | // Colors.red,
117 | // ],
118 | // ),
119 | // ),
120 | ]));
121 | }
122 |
--------------------------------------------------------------------------------
/lib/screens/telehealth/chat.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import 'package:cloud_firestore/cloud_firestore.dart';
5 | import 'package:firebase_storage/firebase_storage.dart';
6 | import 'package:flutter/cupertino.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:image_picker/image_picker.dart';
9 | import 'package:intl/intl.dart';
10 | import 'package:dash_chat/dash_chat.dart';
11 | import 'package:getwidget/getwidget.dart';
12 | import 'package:openemr/utils/customlistloadingshimmer.dart';
13 |
14 | class ChatScreen extends StatefulWidget {
15 | final String messagesId;
16 | final String chatDocumentId;
17 | final String heading;
18 | final String userId;
19 | final String userName;
20 |
21 | ChatScreen(
22 | {Key key,
23 | @required this.messagesId,
24 | @required this.heading,
25 | @required this.userId,
26 | @required this.userName,
27 | @required this.chatDocumentId})
28 | : super(key: key);
29 |
30 | @override
31 | _ChatScreenState createState() => _ChatScreenState();
32 | }
33 |
34 | class _ChatScreenState extends State {
35 | final GlobalKey _chatViewKey = GlobalKey();
36 | final Firestore _store = Firestore.instance;
37 | ChatUser chatUser;
38 | final picker = ImagePicker();
39 |
40 | List messages = List.empty(growable: true);
41 | var m = List.empty(growable: true);
42 |
43 | var i = 0;
44 |
45 | @override
46 | void initState() {
47 | chatUser = ChatUser(
48 | name: widget.userName,
49 | uid: widget.userId,
50 | );
51 | this.setState(() {
52 | chatUser = chatUser;
53 | });
54 | super.initState();
55 | }
56 |
57 | void systemMessage() {
58 | Timer(Duration(milliseconds: 300), () {
59 | if (i < 6) {
60 | setState(() {
61 | messages = [...messages, m[i]];
62 | });
63 | i++;
64 | }
65 | Timer(Duration(milliseconds: 300), () {
66 | _chatViewKey.currentState.scrollController
67 | ..animateTo(
68 | _chatViewKey.currentState.scrollController.position.maxScrollExtent,
69 | curve: Curves.easeOut,
70 | duration: const Duration(milliseconds: 300),
71 | );
72 | });
73 | });
74 | }
75 |
76 | void onSend(ChatMessage message) async {
77 | print(message.toJson());
78 | await _store.collection('messages').document(widget.messagesId).updateData({
79 | "messages": FieldValue.arrayUnion([message.toJson()])
80 | });
81 | if (message.text != null && message.text != "") {
82 | _store
83 | .collection('chats')
84 | .document(widget.chatDocumentId)
85 | .updateData({"lastMessage": message.text});
86 | }
87 | }
88 |
89 | void uploadImage(result) async {
90 | final StorageReference storageRef = FirebaseStorage.instance
91 | .ref()
92 | .child(widget.userId + "-" + DateTime.now().toString());
93 |
94 | StorageUploadTask uploadTask = storageRef.putFile(
95 | result,
96 | StorageMetadata(
97 | contentType: 'image/jpg',
98 | ),
99 | );
100 | StorageTaskSnapshot download = await uploadTask.onComplete;
101 |
102 | String url = await download.ref.getDownloadURL();
103 |
104 | ChatMessage message = ChatMessage(text: "", user: chatUser, image: url);
105 | onSend(message);
106 | }
107 |
108 | @override
109 | Widget build(BuildContext context) {
110 | return Scaffold(
111 | appBar: AppBar(
112 | backgroundColor: GFColors.DARK,
113 | leading: InkWell(
114 | onTap: () {
115 | Navigator.pop(context);
116 | },
117 | child: Icon(
118 | CupertinoIcons.back,
119 | color: GFColors.SUCCESS,
120 | ),
121 | ),
122 | title: Text(
123 | widget.heading,
124 | style: TextStyle(fontSize: 17),
125 | ),
126 | centerTitle: true,
127 | ),
128 | body: StreamBuilder(
129 | stream: Firestore.instance
130 | .collection('messages')
131 | .document(widget.messagesId)
132 | .snapshots(),
133 | builder: (context, snapshot) {
134 | if (snapshot.connectionState == ConnectionState.waiting ||
135 | !snapshot.hasData) {
136 | return Center(
137 | child: customListLoadingShimmer(context,
138 | loadingMessage: 'Loading your Messages...',
139 | listLength: 6),
140 | );
141 | } else {
142 | DocumentSnapshot items = snapshot.data;
143 | List msg = items.data["messages"] == null
144 | ? []
145 | : items.data["messages"];
146 | List messages = [];
147 | msg.forEach((item) => messages.add(ChatMessage.fromJson(item)));
148 | return DashChat(
149 | key: _chatViewKey,
150 | inverted: false,
151 | onSend: onSend,
152 | sendOnEnter: true,
153 | textInputAction: TextInputAction.send,
154 | user: chatUser,
155 | inputDecoration: InputDecoration.collapsed(
156 | hintText: "Add message here..."),
157 | dateFormat: DateFormat('yyyy-MMM-dd'),
158 | timeFormat: DateFormat('HH:mm'),
159 | messages: messages,
160 | showUserAvatar: true,
161 | showAvatarForEveryMessage: true,
162 | scrollToBottom: true,
163 | onPressAvatar: (ChatUser user) {
164 | print("OnPressAvatar: ${user.name}");
165 | },
166 | onLongPressAvatar: (ChatUser user) {
167 | print("OnLongPressAvatar: ${user.name}");
168 | },
169 | inputMaxLines: 5,
170 | messageContainerPadding:
171 | EdgeInsets.only(left: 5.0, right: 5.0),
172 | alwaysShowSend: false,
173 | inputTextStyle: TextStyle(fontSize: 16.0),
174 | inputContainerStyle: BoxDecoration(
175 | border: Border.all(width: 0.0),
176 | color: Colors.white,
177 | ),
178 | onLoadEarlier: () {
179 | print("laoding...");
180 | },
181 | shouldShowLoadEarlier: false,
182 | showTraillingBeforeSend: true,
183 | trailing: [
184 | IconButton(
185 | icon: Icon(Icons.camera_alt),
186 | onPressed: () async {
187 | final pickedFile = await picker.getImage(
188 | source: ImageSource.camera,
189 | imageQuality: 80,
190 | maxHeight: 400,
191 | maxWidth: 400,
192 | );
193 |
194 | if (pickedFile != null) {
195 | File result = File(pickedFile.path);
196 | uploadImage(result);
197 | }
198 | },
199 | ),
200 | IconButton(
201 | icon: Icon(Icons.photo),
202 | onPressed: () async {
203 | final pickedFile = await picker.getImage(
204 | source: ImageSource.gallery,
205 | imageQuality: 80,
206 | maxHeight: 400,
207 | maxWidth: 400,
208 | );
209 |
210 | if (pickedFile != null) {
211 | File result = File(pickedFile.path);
212 | uploadImage(result);
213 | }
214 | },
215 | ),
216 | ],
217 | );
218 | }
219 | }));
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/lib/screens/telehealth/local_widgets/profileShimmer.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:getwidget/components/shimmer/gf_shimmer.dart';
3 |
4 | Widget profileShimmer(BuildContext context) {
5 | return GFShimmer(
6 | child: Column(
7 | crossAxisAlignment: CrossAxisAlignment.center,
8 | children: [
9 | SizedBox(
10 | height: 40,
11 | ),
12 | Container(
13 | height: 100,
14 | width: MediaQuery.of(context).size.width * 0.8,
15 | color: Colors.white,
16 | ),
17 | SizedBox(
18 | height: 10,
19 | ),
20 | Row(
21 | mainAxisAlignment: MainAxisAlignment.center,
22 | crossAxisAlignment: CrossAxisAlignment.start,
23 | children: [
24 | CircleAvatar(
25 | backgroundColor: Colors.white,
26 | radius: 40,
27 | ),
28 | Container(
29 | padding: const EdgeInsets.fromLTRB(10, 5, 5, 5),
30 | child: Column(
31 | crossAxisAlignment: CrossAxisAlignment.start,
32 | children: [
33 | Container(
34 | height: 25,
35 | width: MediaQuery.of(context).size.width * 0.55,
36 | color: Colors.white,
37 | ),
38 | SizedBox(height: 10),
39 | Container(
40 | height: 20,
41 | width: MediaQuery.of(context).size.width * 0.5,
42 | color: Colors.white,
43 | ),
44 | ],
45 | ),
46 | )
47 | ],
48 | ),
49 | Container(
50 | margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 15),
51 | child: Column(
52 | crossAxisAlignment: CrossAxisAlignment.start,
53 | children: [
54 | Container(
55 | height: 25,
56 | width: MediaQuery.of(context).size.width * 0.7,
57 | color: Colors.white,
58 | ),
59 | SizedBox(height: 10),
60 | Container(
61 | height: 20,
62 | width: MediaQuery.of(context).size.width * 0.7,
63 | color: Colors.white,
64 | ),
65 | ],
66 | ),
67 | ),
68 | Row(
69 | mainAxisAlignment: MainAxisAlignment.center,
70 | children: [
71 | Container(
72 | margin: const EdgeInsets.all(5),
73 | height: 100,
74 | width: 100,
75 | color: Colors.white,
76 | ),
77 | Container(
78 | margin: const EdgeInsets.all(5),
79 | height: 100,
80 | width: 100,
81 | color: Colors.white,
82 | ),
83 | Container(
84 | margin: const EdgeInsets.all(5),
85 | height: 100,
86 | width: 100,
87 | color: Colors.white,
88 | ),
89 | ],
90 | )
91 | ],
92 | ));
93 | }
94 |
--------------------------------------------------------------------------------
/lib/screens/telehealth/profile.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:getwidget/getwidget.dart';
4 | import 'package:modal_progress_hud/modal_progress_hud.dart';
5 | import 'package:openemr/screens/telehealth/telehealth.dart';
6 | import '../../models/user.dart';
7 | import 'package:firebase_auth/firebase_auth.dart';
8 | import 'package:cloud_firestore/cloud_firestore.dart';
9 |
10 | class FirebaseProfileScreen extends StatefulWidget {
11 | //user's current display name
12 | final String dispName;
13 | FirebaseProfileScreen({Key key, @required this.dispName}) : super(key: key);
14 | @override
15 | _FirebaseProfileScreenState createState() => _FirebaseProfileScreenState();
16 | }
17 |
18 | class _FirebaseProfileScreenState extends State {
19 | final FirebaseAuth _auth = FirebaseAuth.instance;
20 | final Firestore _store = Firestore.instance;
21 | User user;
22 |
23 | final formKey = new GlobalKey();
24 | String _name;
25 |
26 | //decides when to active/inactive spinner indicator
27 | bool showSpinner = false;
28 |
29 | void _showSnackBar(String text) {
30 | ScaffoldMessenger.of(context)
31 | .showSnackBar(new SnackBar(content: new Text(text)));
32 | }
33 |
34 | @override
35 | Widget build(BuildContext context) {
36 | double width = MediaQuery.of(context).size.width;
37 | return GestureDetector(
38 | onTap: () {
39 | FocusScopeNode currentFocus = FocusScope.of(context);
40 | if (!currentFocus.hasPrimaryFocus) {
41 | currentFocus.unfocus();
42 | }
43 | },
44 | child: Scaffold(
45 | backgroundColor: GFColors.LIGHT,
46 | body: ModalProgressHUD(
47 | // color: Colors.blueAccent,
48 | inAsyncCall: showSpinner,
49 | child: Padding(
50 | padding: EdgeInsets.only(left: width * 0.1, right: width * 0.1),
51 | child: Center(
52 | child: SingleChildScrollView(
53 | child: Form(
54 | key: formKey,
55 | child: Column(
56 | mainAxisAlignment: MainAxisAlignment.center,
57 | crossAxisAlignment: CrossAxisAlignment.center,
58 | children: [
59 | SizedBox(
60 | height: 25,
61 | ),
62 | SizedBox(
63 | height: 20,
64 | ),
65 | SizedBox(
66 | child: TextFormField(
67 | //set initial value as the dispName
68 | initialValue: widget.dispName,
69 | validator: (value) {
70 | if (value.isEmpty) {
71 | return 'Display name can\'t be blank';
72 | }
73 | return null;
74 | },
75 | onSaved: (val) => _name = val,
76 | decoration: InputDecoration(
77 | border: OutlineInputBorder(),
78 | labelText: 'Display name'),
79 | ),
80 | ),
81 | SizedBox(
82 | height: 20,
83 | ),
84 | GFButton(
85 | onPressed: () => updateProfile(context),
86 | text: 'Update',
87 | color: GFColors.DARK,
88 | ),
89 | ],
90 | ),
91 | ),
92 | ),
93 | ),
94 | ),
95 | ),
96 | ),
97 | );
98 | }
99 |
100 | void updateProfile(context) async {
101 | //start showing the spinner
102 | setState(() {
103 | showSpinner = true;
104 | });
105 | FirebaseUser user;
106 | String errorMessage;
107 | final form = formKey.currentState;
108 | if (form.validate()) {
109 | form.save();
110 | try {
111 | user = await _auth.currentUser();
112 | UserUpdateInfo updateInfo = UserUpdateInfo();
113 | updateInfo.displayName = _name;
114 | await user.updateProfile(updateInfo);
115 | } catch (error) {
116 | //stop showing the spinner
117 | setState(() {
118 | showSpinner = false;
119 | });
120 | switch (error.code) {
121 | case "ERROR_USER_DISABLED":
122 | errorMessage = "Your acount has been disabled";
123 | break;
124 | case "ERROR_USER_NOT_FOUND":
125 | errorMessage = "Account not found";
126 | break;
127 | default:
128 | errorMessage = error.code == null
129 | ? "An undefined Error happened."
130 | : error.code;
131 | }
132 | }
133 | }
134 | if (errorMessage != null) {
135 | //stop showing the spinner
136 | setState(() {
137 | showSpinner = false;
138 | });
139 | _showSnackBar(errorMessage);
140 | return null;
141 | }
142 | await _store
143 | .collection('username')
144 | .document(user.uid)
145 | .updateData({"name": _name});
146 | Navigator.of(context).pushAndRemoveUntil(
147 | MaterialPageRoute(builder: (context) => Telehealth()),
148 | (route) => false);
149 | //stop showing the spinner
150 | setState(() {
151 | showSpinner = false;
152 | });
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/lib/screens/telehealth/signaling.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:async';
3 | import 'package:flutter_webrtc/webrtc.dart';
4 | import 'package:openemr/utils/websocket.dart';
5 |
6 | enum SignalingState {
7 | CallStateNew,
8 | CallStateRinging,
9 | CallStateInvite,
10 | CallStateConnected,
11 | CallStateBye,
12 | ConnectionOpen,
13 | ConnectionClosed,
14 | ConnectionError,
15 | }
16 |
17 | /*
18 | * callbacks for Signaling API.
19 | */
20 | typedef void SignalingStateCallback(SignalingState state);
21 | typedef void StreamStateCallback(MediaStream stream);
22 | typedef void OtherEventCallback(dynamic event);
23 | typedef void DataChannelMessageCallback(
24 | RTCDataChannel dc, RTCDataChannelMessage data);
25 | typedef void DataChannelCallback(RTCDataChannel dc);
26 |
27 | class Signaling {
28 | JsonEncoder _encoder = new JsonEncoder();
29 | String _selfId;
30 | SimpleWebSocket _socket;
31 | var _sessionId;
32 | var _host;
33 | var _port = 8086;
34 | var _peerConnections = new Map();
35 | var _dataChannels = new Map();
36 | var _remoteCandidates = [];
37 |
38 | MediaStream _localStream;
39 | List _remoteStreams;
40 | SignalingStateCallback onStateChange;
41 | StreamStateCallback onLocalStream;
42 | StreamStateCallback onAddRemoteStream;
43 | StreamStateCallback onRemoveRemoteStream;
44 | OtherEventCallback onPeersUpdate;
45 | DataChannelMessageCallback onDataChannelMessage;
46 | DataChannelCallback onDataChannel;
47 |
48 | Map _iceServers = {
49 | 'iceServers': [
50 | {'url': 'stun:stun.l.google.com:19302'},
51 | ]
52 | };
53 |
54 | final Map _config = {
55 | 'mandatory': {},
56 | 'optional': [
57 | {'DtlsSrtpKeyAgreement': true},
58 | ],
59 | };
60 |
61 | final Map _constraints = {
62 | 'mandatory': {
63 | 'OfferToReceiveAudio': true,
64 | 'OfferToReceiveVideo': true,
65 | },
66 | 'optional': [],
67 | };
68 |
69 | final Map _dcConstraints = {
70 | 'mandatory': {
71 | 'OfferToReceiveAudio': false,
72 | 'OfferToReceiveVideo': false,
73 | },
74 | 'optional': [],
75 | };
76 |
77 | Signaling(this._host, this._selfId);
78 |
79 | close() {
80 | if (_localStream != null) {
81 | _localStream.dispose();
82 | _localStream = null;
83 | }
84 |
85 | _peerConnections.forEach((key, pc) {
86 | pc.close();
87 | });
88 | if (_socket != null) _socket.close();
89 | }
90 |
91 | void switchCamera() {
92 | if (_localStream != null) {
93 | _localStream.getVideoTracks()[0].switchCamera();
94 | }
95 | }
96 |
97 | void invite(String peerId, String media) {
98 | this._sessionId = this._selfId + '-' + peerId;
99 |
100 | if (this.onStateChange != null) {
101 | this.onStateChange(SignalingState.CallStateNew);
102 | }
103 |
104 | _createPeerConnection(peerId, media).then((pc) {
105 | _peerConnections[peerId] = pc;
106 | if (media == 'data') {
107 | _createDataChannel(peerId, pc);
108 | }
109 | _createOffer(peerId, pc, media);
110 | });
111 | }
112 |
113 | void bye() {
114 | _send('bye', {
115 | 'session_id': this._sessionId,
116 | 'from': this._selfId,
117 | });
118 | }
119 |
120 | void onMessage(message) async {
121 | Map mapData = message;
122 | var data = mapData['data'];
123 |
124 | switch (mapData['type']) {
125 | case 'peers':
126 | {
127 | List peers = data;
128 | if (this.onPeersUpdate != null) {
129 | Map event = new Map();
130 | event['self'] = _selfId;
131 | event['peers'] = peers;
132 | this.onPeersUpdate(event);
133 | }
134 | }
135 | break;
136 | case 'offer':
137 | {
138 | var id = data['from'];
139 | var description = data['description'];
140 | var media = data['media'];
141 | var sessionId = data['session_id'];
142 | this._sessionId = sessionId;
143 |
144 | if (this.onStateChange != null) {
145 | this.onStateChange(SignalingState.CallStateNew);
146 | }
147 |
148 | var pc = await _createPeerConnection(id, media);
149 | _peerConnections[id] = pc;
150 | await pc.setRemoteDescription(new RTCSessionDescription(
151 | description['sdp'], description['type']));
152 | await _createAnswer(id, pc, media);
153 | if (this._remoteCandidates.length > 0) {
154 | _remoteCandidates.forEach((candidate) async {
155 | await pc.addCandidate(candidate);
156 | });
157 | _remoteCandidates.clear();
158 | }
159 | }
160 | break;
161 | case 'answer':
162 | {
163 | var id = data['from'];
164 | var description = data['description'];
165 |
166 | var pc = _peerConnections[id];
167 | if (pc != null) {
168 | await pc.setRemoteDescription(new RTCSessionDescription(
169 | description['sdp'], description['type']));
170 | }
171 | }
172 | break;
173 | case 'candidate':
174 | {
175 | var id = data['from'];
176 | var candidateMap = data['candidate'];
177 | var pc = _peerConnections[id];
178 | RTCIceCandidate candidate = new RTCIceCandidate(
179 | candidateMap['candidate'],
180 | candidateMap['sdpMid'],
181 | candidateMap['sdpMLineIndex']);
182 | if (pc != null) {
183 | await pc.addCandidate(candidate);
184 | } else {
185 | _remoteCandidates.add(candidate);
186 | }
187 | }
188 | break;
189 | case 'leave':
190 | {
191 | var id = data;
192 | var pc = _peerConnections.remove(id);
193 | _dataChannels.remove(id);
194 |
195 | if (_localStream != null) {
196 | _localStream.dispose();
197 | _localStream = null;
198 | }
199 |
200 | if (pc != null) {
201 | pc.close();
202 | }
203 | this._sessionId = null;
204 | if (this.onStateChange != null) {
205 | this.onStateChange(SignalingState.CallStateBye);
206 | }
207 | }
208 | break;
209 | case 'bye':
210 | {
211 | var to = data['to'];
212 | var sessionId = data['session_id'];
213 | print('bye: ' + sessionId);
214 |
215 | if (_localStream != null) {
216 | _localStream.dispose();
217 | _localStream = null;
218 | }
219 |
220 | var pc = _peerConnections[to];
221 | if (pc != null) {
222 | pc.close();
223 | _peerConnections.remove(to);
224 | }
225 |
226 | var dc = _dataChannels[to];
227 | if (dc != null) {
228 | dc.close();
229 | _dataChannels.remove(to);
230 | }
231 |
232 | this._sessionId = null;
233 | if (this.onStateChange != null) {
234 | this.onStateChange(SignalingState.CallStateBye);
235 | }
236 | }
237 | break;
238 | case 'keepalive':
239 | {
240 | print('keepalive response!');
241 | }
242 | break;
243 | default:
244 | break;
245 | }
246 | }
247 |
248 | void connect(name) async {
249 | var url = 'https://$_host:$_port/ws';
250 | _socket = SimpleWebSocket(url);
251 |
252 | print('connect to $url');
253 |
254 | _socket.onOpen = () {
255 | print('onOpen');
256 | this?.onStateChange(SignalingState.ConnectionOpen);
257 | _send('new', {'name': name, 'id': _selfId, 'user_agent': "initiator"});
258 | };
259 |
260 | _socket.onMessage = (message) {
261 | print('Received data: ' + message);
262 | JsonDecoder decoder = new JsonDecoder();
263 | this.onMessage(decoder.convert(message));
264 | };
265 |
266 | _socket.onClose = (int code, String reason) {
267 | print('Closed by server [$code => $reason]!');
268 | if (this.onStateChange != null) {
269 | this.onStateChange(SignalingState.ConnectionClosed);
270 | }
271 | };
272 |
273 | await _socket.connect();
274 | }
275 |
276 | Future createStream(media) async {
277 | final Map mediaConstraints = {
278 | 'audio': true,
279 | 'video': {
280 | 'mandatory': {
281 | 'minWidth': '640',
282 | 'minHeight': '480',
283 | 'minFrameRate': '30',
284 | },
285 | 'facingMode': 'user',
286 | 'optional': [],
287 | }
288 | };
289 |
290 | MediaStream stream = await navigator.getUserMedia(mediaConstraints);
291 | if (this.onLocalStream != null) {
292 | this.onLocalStream(stream);
293 | }
294 | return stream;
295 | }
296 |
297 | _createPeerConnection(id, media) async {
298 | if (media != 'data') _localStream = await createStream(media);
299 | RTCPeerConnection pc = await createPeerConnection(_iceServers, _config);
300 | if (media != 'data') pc.addStream(_localStream);
301 | pc.onIceCandidate = (candidate) {
302 | _send('candidate', {
303 | 'to': id,
304 | 'from': _selfId,
305 | 'candidate': {
306 | 'sdpMLineIndex': candidate.sdpMlineIndex,
307 | 'sdpMid': candidate.sdpMid,
308 | 'candidate': candidate.candidate,
309 | },
310 | 'session_id': this._sessionId,
311 | });
312 | };
313 |
314 | pc.onIceConnectionState = (state) {};
315 |
316 | pc.onAddStream = (stream) {
317 | if (this.onAddRemoteStream != null) this.onAddRemoteStream(stream);
318 | //_remoteStreams.add(stream);
319 | };
320 |
321 | pc.onRemoveStream = (stream) {
322 | if (this.onRemoveRemoteStream != null) this.onRemoveRemoteStream(stream);
323 | _remoteStreams.removeWhere((it) {
324 | return (it.id == stream.id);
325 | });
326 | };
327 |
328 | pc.onDataChannel = (channel) {
329 | _addDataChannel(id, channel);
330 | };
331 |
332 | return pc;
333 | }
334 |
335 | _addDataChannel(id, RTCDataChannel channel) {
336 | channel.onDataChannelState = (e) {};
337 | channel.onMessage = (RTCDataChannelMessage data) {
338 | if (this.onDataChannelMessage != null)
339 | this.onDataChannelMessage(channel, data);
340 | };
341 | _dataChannels[id] = channel;
342 |
343 | if (this.onDataChannel != null) this.onDataChannel(channel);
344 | }
345 |
346 | _createDataChannel(id, RTCPeerConnection pc, {label: 'fileTransfer'}) async {
347 | RTCDataChannelInit dataChannelDict = new RTCDataChannelInit();
348 | RTCDataChannel channel = await pc.createDataChannel(label, dataChannelDict);
349 | _addDataChannel(id, channel);
350 | }
351 |
352 | _createOffer(String id, RTCPeerConnection pc, String media) async {
353 | try {
354 | RTCSessionDescription s =
355 | await pc.createOffer(media == 'data' ? _dcConstraints : _constraints);
356 | pc.setLocalDescription(s);
357 | _send('offer', {
358 | 'to': id,
359 | 'from': _selfId,
360 | 'description': {'sdp': s.sdp, 'type': s.type},
361 | 'session_id': this._sessionId,
362 | 'media': media,
363 | });
364 | } catch (e) {
365 | print(e.toString());
366 | }
367 | }
368 |
369 | _createAnswer(String id, RTCPeerConnection pc, media) async {
370 | try {
371 | RTCSessionDescription s = await pc
372 | .createAnswer(media == 'data' ? _dcConstraints : _constraints);
373 | pc.setLocalDescription(s);
374 | _send('answer', {
375 | 'to': id,
376 | 'from': _selfId,
377 | 'description': {'sdp': s.sdp, 'type': s.type},
378 | 'session_id': this._sessionId,
379 | });
380 | } catch (e) {
381 | print(e.toString());
382 | }
383 | }
384 |
385 | _send(event, data) {
386 | var request = new Map();
387 | request["type"] = event;
388 | request["data"] = data;
389 | _socket.send(_encoder.convert(request));
390 | }
391 | }
392 |
--------------------------------------------------------------------------------
/lib/utils/common.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | void showToast(BuildContext context, String msg) {
4 | ScaffoldMessenger.of(context).showSnackBar(
5 | SnackBar(
6 | content: Text(msg),
7 | ),
8 | );
9 | }
10 |
11 | bool isValidUrl(url) {
12 | return Uri.parse(url).isAbsolute;
13 | }
14 |
--------------------------------------------------------------------------------
/lib/utils/customlistloadingshimmer.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:getwidget/components/shimmer/gf_shimmer.dart';
3 |
4 | Widget listItemShimmer(BuildContext context) {
5 | return GFShimmer(
6 | child: Container(
7 | alignment: Alignment.center,
8 | margin: const EdgeInsets.all(5),
9 | child: Row(
10 | children: [
11 | CircleAvatar(
12 | backgroundColor: Colors.white,
13 | radius: 30,
14 | ),
15 | Container(
16 | padding: const EdgeInsets.fromLTRB(10, 5, 5, 5),
17 | child: Column(
18 | crossAxisAlignment: CrossAxisAlignment.start,
19 | children: [
20 | Container(
21 | height: 25,
22 | width: MediaQuery.of(context).size.width * 0.55,
23 | color: Colors.white,
24 | ),
25 | SizedBox(height: 10),
26 | Container(
27 | height: 20,
28 | width: MediaQuery.of(context).size.width * 0.5,
29 | color: Colors.white,
30 | ),
31 | ],
32 | ),
33 | )
34 | ],
35 | ),
36 | ),
37 | );
38 | }
39 |
40 | Widget customListLoadingShimmer(BuildContext context,
41 | {String loadingMessage, int listLength = 1}) {
42 | return Container(
43 | alignment: Alignment.center,
44 | width: MediaQuery.of(context).size.width * 0.8,
45 | margin: const EdgeInsets.symmetric(vertical: 10),
46 | child: Column(
47 | mainAxisAlignment: MainAxisAlignment.center,
48 | crossAxisAlignment: CrossAxisAlignment.center,
49 | children: [
50 | loadingMessage == null
51 | ? Container()
52 | : Padding(
53 | padding: const EdgeInsets.only(bottom: 10),
54 | child: Text(
55 | loadingMessage,
56 | style: TextStyle(fontSize: 16, color: Colors.grey),
57 | ),
58 | ),
59 | for (var i = 0; i < listLength; i++) listItemShimmer(context)
60 | ],
61 | ),
62 | );
63 | }
64 |
--------------------------------------------------------------------------------
/lib/utils/network.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:convert';
3 | import 'dart:io';
4 | import 'package:http/http.dart' as http;
5 | import 'package:http_parser/http_parser.dart';
6 | import 'package:path/path.dart';
7 | import 'common.dart';
8 |
9 | class NetworkUtil {
10 | // next three lines makes this class a Singleton
11 | static NetworkUtil _instance = new NetworkUtil.internal();
12 | NetworkUtil.internal();
13 | factory NetworkUtil() => _instance;
14 |
15 | final JsonDecoder _decoder = new JsonDecoder();
16 |
17 | Future get(String url, {Map headers}) {
18 | if (!isValidUrl(url)) {
19 | return Future.error("Invalid API URL");
20 | }
21 | return http.get(url, headers: headers).then((http.Response response) {
22 | final String res = response.body;
23 | final int statusCode = response.statusCode;
24 |
25 | if (statusCode < 200 || statusCode > 400 || json == null) {
26 | throw new Exception("Error while fetching data");
27 | }
28 | return _decoder.convert(res);
29 | });
30 | }
31 |
32 | Future post(String url, {Map headers, body, encoding}) {
33 | if (!isValidUrl(url)) {
34 | return Future.error("Invalid API URL");
35 | }
36 | return http
37 | .post(url,
38 | body: json.encode(body), headers: headers, encoding: encoding)
39 | .then((http.Response response) {
40 | final res = response.body;
41 | final int statusCode = response.statusCode;
42 | if (statusCode == 401) {
43 | throw new Exception(statusCode.toString() + "Invalid Credentials");
44 | } else if (statusCode == 400) {
45 | final resData = json.decode(res);
46 | var validationErrorData = resData['validationErrors'];
47 | List