├── lib ├── constants.dart ├── colors.dart ├── model │ ├── user_model.dart │ └── shared_preference.dart ├── widget │ ├── show_dialog.dart │ ├── show_dialog_html.dart │ ├── connectivity_banner.dart │ ├── show_confirmation_alert.dart │ └── animated_list_item.dart ├── services │ ├── awareness_data_service.dart │ ├── notification_service.dart │ └── member_service.dart ├── main.dart └── screens │ ├── terms_and_conditions_page.dart │ ├── privacy_policy_page.dart │ ├── auth │ ├── recover_password_page.dart │ └── forgot_password_page.dart │ └── user │ ├── awareness_material_page.dart │ ├── notification_page.dart │ └── userMaterial │ ├── diet_chart_page.dart │ ├── bp_ramazan_page.dart │ └── bp_heart_disease_page.dart ├── ios ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-50x50@1x.png │ │ │ ├── Icon-App-50x50@2x.png │ │ │ ├── Icon-App-57x57@1x.png │ │ │ ├── Icon-App-57x57@2x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-72x72@1x.png │ │ │ ├── Icon-App-72x72@2x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── GoogleService-Info.plist │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── .gitignore ├── Podfile └── Podfile.lock ├── android ├── gradle.properties ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ └── launcher_icon.png │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-v21 │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── flutter_hami │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── google-services.json │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle └── build.gradle ├── assets ├── images │ ├── male.png │ ├── female.png │ ├── header.png │ ├── bmi_icon.png │ ├── hami_logo.png │ └── blood_glucose_icon.png └── fonts │ └── Jameel-Noori-Nastaleeq-Regular.ttf ├── .gitignore ├── test └── widget_test.dart ├── analysis_options.yaml ├── README.md └── pubspec.yaml /lib/constants.dart: -------------------------------------------------------------------------------- 1 | double kInputBorderWidth = 1; -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /assets/images/male.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/assets/images/male.png -------------------------------------------------------------------------------- /assets/images/female.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/assets/images/female.png -------------------------------------------------------------------------------- /assets/images/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/assets/images/header.png -------------------------------------------------------------------------------- /assets/images/bmi_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/assets/images/bmi_icon.png -------------------------------------------------------------------------------- /assets/images/hami_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/assets/images/hami_logo.png -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /assets/images/blood_glucose_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/assets/images/blood_glucose_icon.png -------------------------------------------------------------------------------- /assets/fonts/Jameel-Noori-Nastaleeq-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/assets/fonts/Jameel-Noori-Nastaleeq-Regular.ttf -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/android/app/src/main/res/mipmap-hdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/android/app/src/main/res/mipmap-mdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /lib/colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const kColorPrimary = Color(0xffb61b1c); 4 | const kColorSecondary = Color(0xffFF7940); 5 | const kColorTertiary = Color(0xff0A57DE); -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_hami/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_hami 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madeeldev/flutter-doctor-consultation-app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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-7.4-all.zip 7 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.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/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /lib/model/user_model.dart: -------------------------------------------------------------------------------- 1 | class UserModel { 2 | String? registrationPin; 3 | String? userName; 4 | String? userMobile; 5 | String? userEmail; 6 | String? userPassword; 7 | String? userCity; 8 | 9 | UserModel({ 10 | this.registrationPin, 11 | this.userName, 12 | this.userMobile, 13 | this.userEmail, 14 | this.userPassword, 15 | this.userCity, 16 | }); 17 | } -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/widget/show_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | showDialogBox(BuildContext context) => showDialog(context: context, barrierDismissible: false, builder: (BuildContext context) { 4 | return Dialog( 5 | child: SizedBox( 6 | height: 100, 7 | child: Column( 8 | mainAxisAlignment: MainAxisAlignment.center, 9 | crossAxisAlignment: CrossAxisAlignment.center, 10 | children: const [ 11 | CircularProgressIndicator(), 12 | SizedBox( 13 | height: 10, 14 | ), 15 | Text("Loading..."), 16 | ], 17 | ), 18 | ), 19 | ); 20 | }); -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.1.2' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | classpath 'com.google.gms:google-services:4.3.13' 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | mavenCentral() 19 | } 20 | } 21 | 22 | rootProject.buildDir = '../build' 23 | subprojects { 24 | project.buildDir = "${rootProject.buildDir}/${project.name}" 25 | } 26 | subprojects { 27 | project.evaluationDependsOn(':app') 28 | } 29 | 30 | task clean(type: Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /lib/widget/show_dialog_html.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_html/flutter_html.dart'; 3 | 4 | showDialogHtml(BuildContext context, String title, String content, actions) { 5 | return showDialog( 6 | barrierDismissible: false, 7 | context: context, 8 | builder: (_) => AlertDialog( 9 | contentPadding: const EdgeInsets.symmetric( 10 | horizontal: 18, 11 | ), 12 | title: Center(child: Text(title)), 13 | content: SingleChildScrollView( 14 | child: Column( 15 | children: [ 16 | Html( 17 | data: """ 18 | $content 19 | """, 20 | ), 21 | ], 22 | ), 23 | ), 24 | actions: actions, 25 | ), 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /lib/widget/connectivity_banner.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | connectivityBanner(context, String contentText, onPressed) => ScaffoldMessenger.of(context) 4 | ..removeCurrentMaterialBanner() 5 | ..showMaterialBanner( 6 | MaterialBanner( 7 | elevation: 1, 8 | backgroundColor: const Color(0xff2c3e50), 9 | leadingPadding: const EdgeInsets.only(right: 12), 10 | content: Text( 11 | contentText, 12 | overflow: TextOverflow.ellipsis, 13 | style: const TextStyle(color: Colors.white,), 14 | ), 15 | contentTextStyle: const TextStyle(color: Colors.white), 16 | forceActionsBelow: true, 17 | actions: [ 18 | TextButton( 19 | onPressed: onPressed, 20 | child: const Text('Dismiss', style: TextStyle(color: Colors.white),), 21 | ), 22 | ], 23 | ), 24 | ); -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/widget/show_confirmation_alert.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | Future showConfirmationAlert(BuildContext context, onPressedCancelBtn, onPressedContinueBtn, alertTitle, alertMessage) { 4 | Widget cancelButton = TextButton( 5 | style: TextButton.styleFrom( 6 | primary: Colors.blue, 7 | ), 8 | onPressed: onPressedCancelBtn, 9 | child: const Text("Cancel"), 10 | ); 11 | Widget continueButton = TextButton( 12 | style: TextButton.styleFrom( 13 | primary: Colors.blue, 14 | ), 15 | onPressed: onPressedContinueBtn, 16 | child: const Text("Continue"), 17 | ); 18 | 19 | return showDialog( 20 | context: context, 21 | builder: (BuildContext context) { 22 | return AlertDialog( 23 | title: Text(alertTitle), 24 | content: Text(alertMessage), 25 | actions: [ 26 | cancelButton, 27 | continueButton, 28 | ], 29 | ); 30 | }, 31 | ); 32 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Web related 36 | lib/generated_plugin_registrant.dart 37 | 38 | # Symbolication related 39 | app.*.symbols 40 | 41 | # Obfuscation related 42 | app.*.map.json 43 | 44 | # Android Studio will place build artifacts here 45 | /android/app/debug 46 | /android/app/profile 47 | /android/app/release 48 | lib/config.dart 49 | lib/services/auth_service.dart 50 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /lib/services/awareness_data_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:http/http.dart' as http; 5 | 6 | import '../config.dart'; 7 | 8 | class AwarenessDataService { 9 | 10 | Future> loadData(String mobile) async { 11 | try { 12 | final postUrl = '${Config.getAwarenessDataUrl}?mobile=$mobile&api_key=${Config.apiKey}'; 13 | final res = await http.get(Uri.parse(postUrl), headers: { 14 | "Accept": "application/json", 15 | "Access-Control-Allow-Origin": "*" 16 | }); 17 | if (res.statusCode == 200) { 18 | final result = json.decode(res.body)['_Hami_Awareness_Material_Result']; 19 | return { 20 | 'message': 'success', 21 | 'data': result 22 | }; 23 | } else { 24 | return { 25 | 'message': 'Server error occurred', 26 | 'data': [] 27 | }; 28 | } 29 | } on SocketException catch (_) { 30 | return { 31 | 'message': 'Internet is not connected', 32 | 'data': [] 33 | }; 34 | } 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "767130457964", 4 | "project_id": "flutter-hami", 5 | "storage_bucket": "flutter-hami.appspot.com" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:767130457964:android:93ce2da648ece9df048549", 11 | "android_client_info": { 12 | "package_name": "com.adeel.flutter_hami" 13 | } 14 | }, 15 | "oauth_client": [ 16 | { 17 | "client_id": "767130457964-mkg3lidmbs9h5ikfc2elq2q4l43ev12j.apps.googleusercontent.com", 18 | "client_type": 3 19 | } 20 | ], 21 | "api_key": [ 22 | { 23 | "current_key": "AIzaSyB5JLyS2s2Ba5GWusaCalpffL6mOsX6ynQ" 24 | } 25 | ], 26 | "services": { 27 | "appinvite_service": { 28 | "other_platform_oauth_client": [ 29 | { 30 | "client_id": "767130457964-mkg3lidmbs9h5ikfc2elq2q4l43ev12j.apps.googleusercontent.com", 31 | "client_type": 3 32 | } 33 | ] 34 | } 35 | } 36 | } 37 | ], 38 | "configuration_version": "1" 39 | } -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:flutter_hami/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 767130457964-v99t3lmgl8g0prr8r66r1054ucrhfcsq.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.767130457964-v99t3lmgl8g0prr8r66r1054ucrhfcsq 9 | API_KEY 10 | AIzaSyAxPAb2izWD3ucujVdOmrZ5UVa72F32WsQ 11 | GCM_SENDER_ID 12 | 767130457964 13 | PLIST_VERSION 14 | 1 15 | BUNDLE_ID 16 | com.adeel.flutterhami 17 | PROJECT_ID 18 | flutter-hami 19 | STORAGE_BUCKET 20 | flutter-hami.appspot.com 21 | IS_ADS_ENABLED 22 | 23 | IS_ANALYTICS_ENABLED 24 | 25 | IS_APPINVITE_ENABLED 26 | 27 | IS_GCM_ENABLED 28 | 29 | IS_SIGNIN_ENABLED 30 | 31 | GOOGLE_APP_ID 32 | 1:767130457964:ios:83a171ab4310ed3e048549 33 | 34 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '10.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 16 | 20 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /lib/model/shared_preference.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_hami/model/user_model.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | 4 | class SharedPreference { 5 | 6 | Future saveUser(UserModel user) async { 7 | final prefs = await SharedPreferences.getInstance(); 8 | prefs.setString('registrationPin', user.registrationPin!); 9 | prefs.setString('userName', user.userName!); 10 | prefs.setString('userMobile', user.userMobile!); 11 | prefs.setString('userEmail', user.userEmail!); 12 | prefs.setString('userPassword', user.userPassword!); 13 | prefs.setString('userCity', user.userCity!); 14 | return Future.value(true); 15 | } 16 | 17 | Future saveUserMobile(String mobile) async { 18 | final prefs = await SharedPreferences.getInstance(); 19 | prefs.setString('userMobile', mobile); 20 | return true; 21 | } 22 | 23 | Future getUserInfo() async { 24 | final prefs = await SharedPreferences.getInstance(); 25 | final registrationPin = prefs.getString('registrationPin'); 26 | final userName = prefs.getString('userName'); 27 | final userMobile = prefs.getString('userMobile'); 28 | final userEmail = prefs.getString('userEmail'); 29 | final userPassword = prefs.getString('userPassword'); 30 | final userCity = prefs.getString('userCity'); 31 | return UserModel( 32 | registrationPin: registrationPin, 33 | userName: userName, 34 | userMobile: userMobile, 35 | userEmail: userEmail, 36 | userPassword: userPassword, 37 | userCity: userCity, 38 | ); 39 | } 40 | 41 | Future removeUser() async { 42 | final prefs = await SharedPreferences.getInstance(); 43 | prefs.remove('registrationPin'); 44 | prefs.remove('userName'); 45 | prefs.remove('userMobile'); 46 | prefs.remove('userEmail'); 47 | prefs.remove('userPassword'); 48 | prefs.remove('userCity'); 49 | } 50 | 51 | Future saveRegistrationPin(String pin) async { 52 | final prefs = await SharedPreferences.getInstance(); 53 | prefs.setString('registrationPin', pin); 54 | return true; 55 | } 56 | 57 | Future removeRegistrationPin() async { 58 | final prefs = await SharedPreferences.getInstance(); 59 | prefs.remove('registrationPin'); 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CADisableMinimumFrameDurationOnPhone 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleDisplayName 10 | Flutter Hami 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | flutter_hami 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | $(FLUTTER_BUILD_NAME) 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | $(FLUTTER_BUILD_NUMBER) 27 | LSRequiresIPhoneOS 28 | 29 | NSAppTransportSecurity 30 | 31 | NSAllowsArbitraryLoads 32 | 33 | 34 | NSCameraUsageDescription 35 | This app requires access to the camera. 36 | NSMicrophoneUsageDescription 37 | This app does not require access to the microphone. 38 | NSPhotoLibraryUsageDescription 39 | This app requires access to the photo library. 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UIMainStoryboardFile 43 | Main 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UISupportedInterfaceOrientations~ipad 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationPortraitUpsideDown 54 | UIInterfaceOrientationLandscapeLeft 55 | UIInterfaceOrientationLandscapeRight 56 | 57 | UIViewControllerBasedStatusBarAppearance 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | apply plugin: 'com.google.gms.google-services' 28 | 29 | android { 30 | compileSdkVersion 33 31 | ndkVersion flutter.ndkVersion 32 | 33 | compileOptions { 34 | sourceCompatibility JavaVersion.VERSION_1_8 35 | targetCompatibility JavaVersion.VERSION_1_8 36 | } 37 | 38 | kotlinOptions { 39 | jvmTarget = '1.8' 40 | } 41 | 42 | sourceSets { 43 | main.java.srcDirs += 'src/main/kotlin' 44 | } 45 | 46 | defaultConfig { 47 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 48 | applicationId "com.adeel.flutter_hami" 49 | // You can update the following values to match your application needs. 50 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. 51 | minSdkVersion 23 52 | targetSdkVersion flutter.targetSdkVersion 53 | versionCode flutterVersionCode.toInteger() 54 | versionName flutterVersionName 55 | } 56 | 57 | buildTypes { 58 | release { 59 | // TODO: Add your own signing config for the release build. 60 | // Signing with the debug keys for now, so `flutter run --release` works. 61 | signingConfig signingConfigs.debug 62 | } 63 | } 64 | } 65 | 66 | flutter { 67 | source '../..' 68 | } 69 | 70 | dependencies { 71 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" 72 | implementation platform('com.google.firebase:firebase-bom:30.3.2') 73 | } 74 | -------------------------------------------------------------------------------- /lib/services/notification_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_messaging/firebase_messaging.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_hami/main.dart'; 4 | import 'package:flutter_hami/screens/user/notification_page.dart'; 5 | import 'package:flutter_local_notifications/flutter_local_notifications.dart'; 6 | 7 | class NotificationService { 8 | 9 | static const AndroidNotificationChannel _channel = AndroidNotificationChannel( 10 | 'hami_notification_channel', //id 11 | 'High Importance Notification', // name 12 | description: 'This channel is used for important notifications', //description 13 | importance: Importance.max, 14 | playSound: true, 15 | ); 16 | 17 | static final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); 18 | 19 | static void initialize() async { 20 | 21 | await _flutterLocalNotificationsPlugin 22 | .resolvePlatformSpecificImplementation< 23 | AndroidFlutterLocalNotificationsPlugin>() 24 | ?.createNotificationChannel(_channel); 25 | 26 | const IOSInitializationSettings initializationSettingsIOS = 27 | IOSInitializationSettings( 28 | requestSoundPermission: false, 29 | requestBadgePermission: false, 30 | requestAlertPermission: false, 31 | ); 32 | 33 | // initializationSettings for Android 34 | const InitializationSettings initializationSettings = 35 | InitializationSettings( 36 | android: AndroidInitializationSettings("@mipmap/ic_launcher"), 37 | iOS: initializationSettingsIOS 38 | ); 39 | 40 | _flutterLocalNotificationsPlugin.initialize( 41 | initializationSettings, 42 | onSelectNotification: (String? id) async { 43 | debugPrint("✅ onSelectNotification"); 44 | navigatorKey.currentState?.push(MaterialPageRoute(builder: (_) => const NotificationPage())); 45 | }, 46 | ); 47 | } 48 | 49 | static void display(RemoteMessage message) async { 50 | try { 51 | RemoteNotification? remoteNotification = message.notification; 52 | AndroidNotification? androidNotification = message.notification?.android; 53 | if(remoteNotification != null && androidNotification != null) { 54 | _flutterLocalNotificationsPlugin.show( 55 | remoteNotification.hashCode, 56 | remoteNotification.title, 57 | remoteNotification.body, 58 | NotificationDetails( 59 | android: AndroidNotificationDetails( 60 | _channel.id, 61 | _channel.name, 62 | channelDescription: _channel.description, 63 | color: Colors.blue, 64 | playSound: true, 65 | icon: '@mipmap/ic_launcher', 66 | importance: Importance.high, 67 | priority: Priority.high, 68 | ), 69 | ), 70 | // payload: message.data['_customKeyData'] 71 | ); 72 | } 73 | } on Exception catch (e) { 74 | debugPrint(e.toString()); 75 | } 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_messaging/firebase_messaging.dart'; 2 | import 'package:firebase_core/firebase_core.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_hami/screens/auth/login_page.dart'; 6 | import 'package:flutter_hami/model/shared_preference.dart'; 7 | import 'package:flutter_hami/screens/auth/verify_pin_page.dart'; 8 | import 'package:flutter_hami/screens/dashboard_page.dart'; 9 | import 'package:flutter_hami/services/notification_service.dart'; 10 | 11 | import 'model/user_model.dart'; 12 | 13 | Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { 14 | debugPrint('✅ FUNCTION CALLED: _firebaseMessagingBackgroundHandler'); 15 | } 16 | 17 | final GlobalKey navigatorKey = GlobalKey(); 18 | 19 | Future main() async { 20 | WidgetsFlutterBinding.ensureInitialized(); 21 | await Firebase.initializeApp(); 22 | FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); 23 | 24 | NotificationService.initialize(); 25 | 26 | await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( 27 | alert: true, 28 | badge: true, 29 | sound: true, 30 | ); 31 | 32 | SystemChrome.setPreferredOrientations([ 33 | DeviceOrientation.portraitUp, 34 | DeviceOrientation.portraitDown, 35 | ]).then((value) => runApp(const MyApp())); 36 | runApp(const MyApp()); 37 | } 38 | 39 | class MyApp extends StatelessWidget { 40 | const MyApp({Key? key}) : super(key: key); 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | Future getUserInfo() => SharedPreference().getUserInfo(); 45 | return MaterialApp( 46 | debugShowCheckedModeBanner: false, 47 | navigatorKey: navigatorKey, 48 | home: FutureBuilder( 49 | future: getUserInfo(), 50 | builder: (context, snapshot) { 51 | switch (snapshot.connectionState) { 52 | case ConnectionState.none: 53 | case ConnectionState.waiting: 54 | return const Scaffold( 55 | body: CircularProgressIndicator(), 56 | ); 57 | default: 58 | if (snapshot.hasError) { 59 | return Text('Error: ${snapshot.error}'); 60 | } else if (snapshot.data == null) { 61 | return const LoginPage(); 62 | } else { 63 | UserModel data = snapshot.data! as UserModel; 64 | if (data.registrationPin == null && data.userMobile == null) { 65 | return const LoginPage(); 66 | } else if (data.registrationPin != null && 67 | data.userMobile != null) { 68 | return const VerifyPinPage(); 69 | } else { 70 | return DashboardPage( 71 | mobile: data.userMobile!, 72 | ); 73 | } 74 | } 75 | } 76 | }, 77 | ), 78 | ); 79 | } 80 | } 81 | 82 | -------------------------------------------------------------------------------- /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"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"72x72","expected-size":"72","filename":"72.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"50x50","expected-size":"100","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"50x50","expected-size":"50","filename":"50.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"72x72","expected-size":"144","filename":"144.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"}]} -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /lib/screens/terms_and_conditions_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | class TermsAndConditionsPage extends StatelessWidget { 5 | const TermsAndConditionsPage({Key? key}) : super(key: key); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | Size size = MediaQuery.of(context).size; 10 | return Scaffold( 11 | backgroundColor: Colors.white, 12 | appBar: PreferredSize( 13 | preferredSize: Size.zero, 14 | child: AppBar( 15 | elevation: 0, 16 | backgroundColor: Colors.white, //ios status bar colors 17 | systemOverlayStyle: const SystemUiOverlayStyle( 18 | statusBarColor: Colors.white, //android status bar color 19 | statusBarBrightness: Brightness.light, // For iOS: (dark icons) 20 | statusBarIconBrightness: 21 | Brightness.dark, // For Android: (dark icons) 22 | ), 23 | ), 24 | ), 25 | body: ListView( 26 | children: [ 27 | SizedBox( 28 | height: size.height, 29 | width: size.width, 30 | child: Column( 31 | children: [ 32 | Padding( 33 | padding: const EdgeInsets.fromLTRB(18, 20, 18, 0), 34 | child: Row( 35 | children: [ 36 | InkWell( 37 | onTap: () { 38 | Navigator.of(context).pop(); 39 | }, 40 | child: const Icon( 41 | Icons.arrow_back_ios, 42 | size: 20, 43 | ), 44 | ), 45 | Expanded( 46 | child: Container( 47 | padding: EdgeInsets.only(right: size.width * 0.06), 48 | alignment: Alignment.center, 49 | child: const Text( 50 | 'Terms & Conditions', 51 | style: TextStyle( 52 | fontWeight: FontWeight.w500, 53 | fontSize: 18 54 | ), 55 | ), 56 | ), 57 | ), 58 | ], 59 | ), 60 | ), 61 | const SizedBox( 62 | height: 20, 63 | ), 64 | Expanded( 65 | child: Container( 66 | padding: const EdgeInsets.symmetric( 67 | horizontal: 20, 68 | ), 69 | child: const Text.rich( 70 | TextSpan( 71 | style: TextStyle(fontSize: 14, height: 1.3), 72 | children: [ 73 | TextSpan(text: 'By '), 74 | TextSpan( 75 | text: 'downloading or using the app', 76 | style: TextStyle(fontWeight: FontWeight.bold), 77 | ), 78 | TextSpan( 79 | text: 80 | ' these terms & conditions will automatically apply to you!', 81 | ), 82 | ], 83 | ), 84 | textAlign: TextAlign.justify, 85 | ), 86 | ), 87 | ), 88 | ], 89 | ), 90 | ), 91 | ], 92 | ), 93 | ); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /lib/screens/privacy_policy_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | 4 | class PrivacyPolicyPage extends StatelessWidget { 5 | const PrivacyPolicyPage({Key? key}) : super(key: key); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | Size size = MediaQuery.of(context).size; 10 | return Scaffold( 11 | backgroundColor: Colors.white, 12 | appBar: PreferredSize( 13 | preferredSize: Size.zero, 14 | child: AppBar( 15 | elevation: 0, 16 | backgroundColor: Colors.white, //ios status bar colors 17 | systemOverlayStyle: const SystemUiOverlayStyle( 18 | statusBarColor: Colors.white, //android status bar color 19 | statusBarBrightness: Brightness.light, // For iOS: (dark icons) 20 | statusBarIconBrightness: 21 | Brightness.dark, // For Android: (dark icons) 22 | ), 23 | ), 24 | ), 25 | body: ListView( 26 | children: [ 27 | SizedBox( 28 | height: size.height, 29 | width: size.width, 30 | child: Column( 31 | children: [ 32 | Padding( 33 | padding: const EdgeInsets.fromLTRB(18, 20, 18, 0), 34 | child: Row( 35 | children: [ 36 | InkWell( 37 | onTap: () { 38 | Navigator.of(context).pop(); 39 | }, 40 | child: const Icon( 41 | Icons.arrow_back_ios, 42 | size: 20, 43 | ), 44 | ), 45 | Expanded( 46 | child: Container( 47 | padding: EdgeInsets.only(right: size.width * 0.06), 48 | alignment: Alignment.center, 49 | child: const Text( 50 | 'Privacy Policy', 51 | style: TextStyle( 52 | fontWeight: FontWeight.w500, 53 | fontSize: 18 54 | ), 55 | ), 56 | ), 57 | ), 58 | ], 59 | ), 60 | ), 61 | const SizedBox( 62 | height: 20, 63 | ), 64 | Expanded( 65 | child: Container( 66 | padding: const EdgeInsets.symmetric( 67 | horizontal: 20, 68 | ), 69 | child: const Text.rich( 70 | TextSpan( 71 | style: TextStyle(fontSize: 14, height: 1.3), 72 | children: [ 73 | TextSpan( 74 | text: 'Open Source Contribution', 75 | style: TextStyle(fontWeight: FontWeight.bold), 76 | ), 77 | TextSpan( 78 | text: ' built the HAMI app as a', 79 | ), 80 | TextSpan( 81 | text: ' Free App', 82 | style: TextStyle(fontWeight: FontWeight.bold), 83 | ), 84 | TextSpan( 85 | text: 86 | ' This service is provided by Open Source Contribution at no cost.', 87 | ), 88 | ], 89 | ), 90 | textAlign: TextAlign.justify, 91 | ), 92 | ), 93 | ), 94 | ], 95 | ), 96 | ), 97 | ], 98 | ), 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /lib/widget/animated_list_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_hami/colors.dart'; 3 | 4 | class AnimatedListItem extends StatelessWidget { 5 | final Map item; 6 | final Animation animation; 7 | const AnimatedListItem( 8 | {required this.item, required this.animation, Key? key}) 9 | : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) => SizeTransition( 13 | sizeFactor: animation, 14 | child: buildItem(), 15 | ); 16 | 17 | Widget buildItem() => SizedBox( 18 | height: 90, 19 | child: Container( 20 | margin: const EdgeInsets.symmetric( 21 | horizontal: 5, 22 | vertical: 5, 23 | ), 24 | decoration: const BoxDecoration( 25 | color: Colors.orange, 26 | borderRadius: BorderRadius.only( 27 | topLeft: Radius.circular(7), 28 | bottomLeft: Radius.circular(7), 29 | ), 30 | boxShadow: [ 31 | BoxShadow( 32 | color: Colors.black12, 33 | offset: Offset(5, 5), 34 | blurRadius: 2, 35 | ), 36 | ], 37 | ), 38 | child: Container( 39 | margin: const EdgeInsets.only( 40 | left: 3, 41 | ), 42 | color: Colors.white, 43 | child: Padding( 44 | padding: const EdgeInsets.symmetric(horizontal: 20), 45 | child: Row( 46 | children: [ 47 | CircleAvatar( 48 | radius: 20, 49 | backgroundColor: Colors.white, 50 | backgroundImage: AssetImage( 51 | item['HP_Gender'] 52 | ? 'assets/images/male.png' 53 | : 'assets/images/female.png', 54 | ), 55 | ), 56 | const SizedBox( 57 | width: 10, 58 | ), 59 | Expanded( 60 | child: Column( 61 | mainAxisAlignment: MainAxisAlignment.center, 62 | crossAxisAlignment: CrossAxisAlignment.start, 63 | children: [ 64 | Text( 65 | item['HP_Name'], 66 | style: const TextStyle( 67 | fontSize: 15, 68 | fontWeight: FontWeight.bold, 69 | ), 70 | ), 71 | const SizedBox( 72 | height: 2, 73 | ), 74 | Row( 75 | children: [ 76 | Text( 77 | item['HP_Gender'] ? 'Male' : 'Female', 78 | style: const TextStyle( 79 | fontSize: 12, 80 | color: Colors.black87, 81 | ), 82 | ), 83 | const SizedBox( 84 | width: 5, 85 | ), 86 | Text( 87 | '- ${item['HP_Age']} years old', 88 | style: const TextStyle( 89 | fontSize: 12, 90 | color: Colors.black87, 91 | ), 92 | ), 93 | ], 94 | ) 95 | ], 96 | ), 97 | ), 98 | const SizedBox( 99 | child: Icon(Icons.swipe, color: kColorPrimary,), 100 | ), 101 | ], 102 | ), 103 | ), 104 | ), 105 | ), 106 | ); 107 | } 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # doctor-consultation-app 2 | 3 |

4 | 5 | [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/madeeldev/flutter-doctor-consultation-app/graphs/commit-activity) 6 | [![ForTheBadge built-with-love](http://ForTheBadge.com/images/badges/built-with-love.svg)](https://github.com/madeeldev/) 7 | [![ForTheBadge made-with-flutter](https://img.shields.io/badge/flutter-made%20with%20flutter-blue.svg)](https://flutter.dev) 8 | [![ForTheBadge ios-supported](https://img.shields.io/badge/IOS-IOS%20Supported-lightgrey.svg)](https://flutter.dev) 9 | [![ForTheBadge ios-android](https://img.shields.io/badge/android-android%20supported-green.svg)](https://flutter.dev) 10 | 11 |

12 | 13 | Hypertension Awareness & Management application built with Flutter 14 | 15 | ## ❤️ Found this project useful? 16 | If you found this project useful, then please consider giving it a ⭐️ on Github and sharing it with your friends via social media. 17 | 18 | ## Download the APK 19 | 20 | In Github, Go to [flutter-doctor-consultation-app releases](https://github.com/madeeldev/flutter-doctor-consultation-app/releases), choose the version (latest is recommanded) and download the .apk file. 21 | 22 | ### Install the APK 23 | 24 | On your Android phone: 25 | 1. Go to **Settings** > **security** > **install unknown apps**. 26 | 2. Go to **files** and enable **Allow from this source**. 27 | *Don't forget to disable it when you have finished* 28 | 3. Exit **Settings** and go to **files**. 29 | 4. Go to **Downloads**, select the **.apk** and click **install**. 30 | 6. The app is now installed on your phone. 31 | 32 | ## Getting Started 33 | 34 | This project is a starting point for a Flutter application. 35 | 36 | To clone this project, 37 | open your terminal or cmd 38 | 39 | ``` 40 | cd folder/to/clone-into/ 41 | ``` 42 | 43 | ``` 44 | git clone https://github.com/madeeldev/flutter-doctor-consultation-app.git 45 | ``` 46 | 47 | Then 48 | locate the project on your system and open with android studio or Vscode or intellij IDE. 49 | 50 | To Run: 51 | ``` 52 | C:\path\to\project> flutter pub get 53 | 54 | ``` 55 | then run: 56 | 57 | ``` 58 | C:\path\to\project> flutter run 59 | 60 | ``` 61 | 62 | ## Build release version 63 | 64 | ``` 65 | run: flutter build e.g flutter build ios 66 | ``` 67 | 68 | ## Resources 69 | 70 | A few resources to get you started if this is your first Flutter project: 71 | 72 | - [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab) 73 | - [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook) 74 | 75 | For help getting started with Flutter, view our 76 | [online documentation](https://flutter.io/docs), which offers tutorials, 77 | samples, guidance on mobile development, and a full API reference. 78 | 79 | ## Prerequisites 80 | 81 | What things you need to run the app 82 | 83 | ``` 84 | * Android Studio/Vscode/Intellij IDE 85 | * Flutter SDK 86 | * Android SDK 87 | * Windows/MacBook 88 | ``` 89 | 90 | ## How to contribute 91 | 92 | - **Fork the repository and clone it locally**. Connect your local to the original “upstream” repository by adding it as a remote. Pull in changes from “upstream” often so that you stay up to date so that when you submit your pull request, merge conflicts will be less likely. (See more detailed instructions here.) 93 | - **Create a branch** for your edits. 94 | - **Reference any relevant issues** or supporting documentation in your PR (for example, “Closes #37.”) 95 | - **Include screenshots of the before and after** if your changes include differences in HTML/CSS. Drag and drop the images into the body of your pull request. 96 | - **Test your changes!** Run your changes against any existing tests if they exist and create new ones when needed. Whether tests exist or not, make sure your changes don’t break the existing project. 97 | - **Contribute in the style of the project** to the best of your abilities. This may mean using indents, semi-colons or comments differently than you would in your own repository, but makes it easier for the maintainer to merge, others to understand and maintain in the future. 98 | 99 | ## Built With 100 | 101 | - [Android Studio](https://developer.android.com/studio/install) - How to install Android Studio 102 | - [Flutter](https://flutter.io) - Flutter Official website 103 | 104 | ## Author 😊 105 | 106 | **Adeel Safdar** 107 | 108 | - [**Linkedin**](https://www.linkedin.com/in/madeeldev/) 109 | - [**Email**](mailto:adeelsafdar.dev@gmail.com) 110 | 111 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_hami 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.17.5 <3.0.0" 22 | 23 | # Dependencies specify other packages that your package needs in order to work. 24 | # To automatically upgrade your package dependencies to the latest versions 25 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 26 | # dependencies can be manually updated by changing the version numbers below to 27 | # the latest version available on pub.dev. To see which dependencies have newer 28 | # versions available, run `flutter pub outdated`. 29 | dependencies: 30 | flutter: 31 | sdk: flutter 32 | 33 | 34 | # The following adds the Cupertino Icons font to your application. 35 | # Use with the CupertinoIcons class for iOS style icons. 36 | cupertino_icons: ^1.0.2 37 | email_validator: ^2.0.1 38 | connectivity_plus: ^2.3.6 39 | provider: ^6.0.3 40 | internet_connection_checker: ^0.0.1+4 41 | http: ^0.13.4 42 | fluttertoast: ^8.0.9 43 | shared_preferences: ^2.0.15 44 | flutter_slidable: ^2.0.0 45 | badges: ^2.0.3 46 | flutter_datetime_picker: ^1.5.1 47 | intl: ^0.17.0 48 | flutter_tabler_icons: ^1.1.1 49 | video_player: ^2.4.6 50 | cached_network_image: ^3.2.1 51 | photo_view: ^0.14.0 52 | date_format: ^2.0.6 53 | flutter_platform_widgets: ^2.0.0 54 | image_picker: ^0.8.5+3 55 | flutter_html: ^3.0.0-alpha.5 56 | firebase_core: ^1.21.0 57 | firebase_messaging: ^12.0.3 58 | flutter_local_notifications: ^9.8.0+1 59 | flutter_launcher_icons: ^0.10.0 60 | 61 | flutter_icons: 62 | android: true 63 | ios: true 64 | image_path: "assets/images/header.png" 65 | 66 | 67 | dev_dependencies: 68 | flutter_test: 69 | sdk: flutter 70 | 71 | # The "flutter_lints" package below contains a set of recommended lints to 72 | # encourage good coding practices. The lint set provided by the package is 73 | # activated in the `analysis_options.yaml` file located at the root of your 74 | # package. See that file for information about deactivating specific lint 75 | # rules and activating additional ones. 76 | flutter_lints: ^2.0.0 77 | 78 | # For information on the generic Dart part of this file, see the 79 | # following page: https://dart.dev/tools/pub/pubspec 80 | 81 | # The following section is specific to Flutter packages. 82 | flutter: 83 | 84 | # The following line ensures that the Material Icons font is 85 | # included with your application, so that you can use the icons in 86 | # the material Icons class. 87 | uses-material-design: true 88 | 89 | # To add assets to your application, add an assets section, like this: 90 | assets: 91 | - assets/images/ 92 | 93 | # An image asset can refer to one or more resolution-specific "variants", see 94 | # https://flutter.dev/assets-and-images/#resolution-aware 95 | 96 | # For details regarding adding assets from package dependencies, see 97 | # https://flutter.dev/assets-and-images/#from-packages 98 | 99 | # To add custom fonts to your application, add a fonts section here, 100 | # in this "flutter" section. Each entry in this list should have a 101 | # "family" key with the font family name, and a "fonts" key with a 102 | # list giving the asset and other descriptors for the font. For 103 | # example: 104 | fonts: 105 | - family: Jameel-Noori-Nastaleeq 106 | fonts: 107 | - asset: assets/fonts/Jameel-Noori-Nastaleeq-Regular.ttf 108 | # - asset: fonts/Schyler-Italic.ttf 109 | # style: italic 110 | # - family: Trajan Pro 111 | # fonts: 112 | # - asset: fonts/TrajanPro.ttf 113 | # - asset: fonts/TrajanPro_Bold.ttf 114 | # weight: 700 115 | # 116 | # For details regarding fonts from package dependencies, 117 | # see https://flutter.dev/custom-fonts/#from-packages 118 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - connectivity_plus (0.0.1): 3 | - Flutter 4 | - ReachabilitySwift 5 | - Firebase/CoreOnly (9.4.0): 6 | - FirebaseCore (= 9.4.0) 7 | - Firebase/Messaging (9.4.0): 8 | - Firebase/CoreOnly 9 | - FirebaseMessaging (~> 9.4.0) 10 | - firebase_core (1.21.0): 11 | - Firebase/CoreOnly (= 9.4.0) 12 | - Flutter 13 | - firebase_messaging (12.0.3): 14 | - Firebase/Messaging (= 9.4.0) 15 | - firebase_core 16 | - Flutter 17 | - FirebaseCore (9.4.0): 18 | - FirebaseCoreDiagnostics (~> 9.0) 19 | - FirebaseCoreInternal (~> 9.0) 20 | - GoogleUtilities/Environment (~> 7.7) 21 | - GoogleUtilities/Logger (~> 7.7) 22 | - FirebaseCoreDiagnostics (9.5.0): 23 | - GoogleDataTransport (< 10.0.0, >= 9.1.4) 24 | - GoogleUtilities/Environment (~> 7.7) 25 | - GoogleUtilities/Logger (~> 7.7) 26 | - nanopb (< 2.30910.0, >= 2.30908.0) 27 | - FirebaseCoreInternal (9.5.0): 28 | - "GoogleUtilities/NSData+zlib (~> 7.7)" 29 | - FirebaseInstallations (9.5.0): 30 | - FirebaseCore (~> 9.0) 31 | - GoogleUtilities/Environment (~> 7.7) 32 | - GoogleUtilities/UserDefaults (~> 7.7) 33 | - PromisesObjC (~> 2.1) 34 | - FirebaseMessaging (9.4.0): 35 | - FirebaseCore (~> 9.0) 36 | - FirebaseInstallations (~> 9.0) 37 | - GoogleDataTransport (< 10.0.0, >= 9.1.4) 38 | - GoogleUtilities/AppDelegateSwizzler (~> 7.7) 39 | - GoogleUtilities/Environment (~> 7.7) 40 | - GoogleUtilities/Reachability (~> 7.7) 41 | - GoogleUtilities/UserDefaults (~> 7.7) 42 | - nanopb (< 2.30910.0, >= 2.30908.0) 43 | - Flutter (1.0.0) 44 | - flutter_local_notifications (0.0.1): 45 | - Flutter 46 | - fluttertoast (0.0.2): 47 | - Flutter 48 | - Toast 49 | - FMDB (2.7.5): 50 | - FMDB/standard (= 2.7.5) 51 | - FMDB/standard (2.7.5) 52 | - GoogleDataTransport (9.2.0): 53 | - GoogleUtilities/Environment (~> 7.7) 54 | - nanopb (< 2.30910.0, >= 2.30908.0) 55 | - PromisesObjC (< 3.0, >= 1.2) 56 | - GoogleUtilities/AppDelegateSwizzler (7.7.0): 57 | - GoogleUtilities/Environment 58 | - GoogleUtilities/Logger 59 | - GoogleUtilities/Network 60 | - GoogleUtilities/Environment (7.7.0): 61 | - PromisesObjC (< 3.0, >= 1.2) 62 | - GoogleUtilities/Logger (7.7.0): 63 | - GoogleUtilities/Environment 64 | - GoogleUtilities/Network (7.7.0): 65 | - GoogleUtilities/Logger 66 | - "GoogleUtilities/NSData+zlib" 67 | - GoogleUtilities/Reachability 68 | - "GoogleUtilities/NSData+zlib (7.7.0)" 69 | - GoogleUtilities/Reachability (7.7.0): 70 | - GoogleUtilities/Logger 71 | - GoogleUtilities/UserDefaults (7.7.0): 72 | - GoogleUtilities/Logger 73 | - image_picker_ios (0.0.1): 74 | - Flutter 75 | - nanopb (2.30909.0): 76 | - nanopb/decode (= 2.30909.0) 77 | - nanopb/encode (= 2.30909.0) 78 | - nanopb/decode (2.30909.0) 79 | - nanopb/encode (2.30909.0) 80 | - path_provider_ios (0.0.1): 81 | - Flutter 82 | - PromisesObjC (2.1.1) 83 | - ReachabilitySwift (5.0.0) 84 | - shared_preferences_ios (0.0.1): 85 | - Flutter 86 | - sqflite (0.0.2): 87 | - Flutter 88 | - FMDB (>= 2.7.5) 89 | - Toast (4.0.0) 90 | - video_player_avfoundation (0.0.1): 91 | - Flutter 92 | 93 | DEPENDENCIES: 94 | - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) 95 | - firebase_core (from `.symlinks/plugins/firebase_core/ios`) 96 | - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) 97 | - Flutter (from `Flutter`) 98 | - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) 99 | - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) 100 | - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) 101 | - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) 102 | - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) 103 | - sqflite (from `.symlinks/plugins/sqflite/ios`) 104 | - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) 105 | 106 | SPEC REPOS: 107 | trunk: 108 | - Firebase 109 | - FirebaseCore 110 | - FirebaseCoreDiagnostics 111 | - FirebaseCoreInternal 112 | - FirebaseInstallations 113 | - FirebaseMessaging 114 | - FMDB 115 | - GoogleDataTransport 116 | - GoogleUtilities 117 | - nanopb 118 | - PromisesObjC 119 | - ReachabilitySwift 120 | - Toast 121 | 122 | EXTERNAL SOURCES: 123 | connectivity_plus: 124 | :path: ".symlinks/plugins/connectivity_plus/ios" 125 | firebase_core: 126 | :path: ".symlinks/plugins/firebase_core/ios" 127 | firebase_messaging: 128 | :path: ".symlinks/plugins/firebase_messaging/ios" 129 | Flutter: 130 | :path: Flutter 131 | flutter_local_notifications: 132 | :path: ".symlinks/plugins/flutter_local_notifications/ios" 133 | fluttertoast: 134 | :path: ".symlinks/plugins/fluttertoast/ios" 135 | image_picker_ios: 136 | :path: ".symlinks/plugins/image_picker_ios/ios" 137 | path_provider_ios: 138 | :path: ".symlinks/plugins/path_provider_ios/ios" 139 | shared_preferences_ios: 140 | :path: ".symlinks/plugins/shared_preferences_ios/ios" 141 | sqflite: 142 | :path: ".symlinks/plugins/sqflite/ios" 143 | video_player_avfoundation: 144 | :path: ".symlinks/plugins/video_player_avfoundation/ios" 145 | 146 | SPEC CHECKSUMS: 147 | connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e 148 | Firebase: 7703fc4022824b6d6db1bf7bea58d13b8e17ec46 149 | firebase_core: c1cdfa024dc7f3d1ba8055a2ae323dba3dce4fa1 150 | firebase_messaging: b65dacd4de1b469893dea0d754b1947f45ef4f7a 151 | FirebaseCore: 9a2b10270a854731c4d4d8a97d0aa8380ec3458d 152 | FirebaseCoreDiagnostics: 17cbf4e72b1dbd64bfdc33d4b1f07bce4f16f1d8 153 | FirebaseCoreInternal: 50a8e39cae8abf72d5145d07ea34c3244f70862b 154 | FirebaseInstallations: 41f811b530c41dd90973d0174381cdb3fcb5e839 155 | FirebaseMessaging: 4e220eddd356181469ba2ec5f7d5fafbc2312841 156 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 157 | flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 158 | fluttertoast: 16fbe6039d06a763f3533670197d01fc73459037 159 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a 160 | GoogleDataTransport: 1c8145da7117bd68bbbed00cf304edb6a24de00f 161 | GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1 162 | image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb 163 | nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 164 | path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 165 | PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb 166 | ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 167 | shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad 168 | sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 169 | Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 170 | video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff 171 | 172 | PODFILE CHECKSUM: fe0e1ee7f3d1f7d00b11b474b62dd62134535aea 173 | 174 | COCOAPODS: 1.11.3 175 | -------------------------------------------------------------------------------- /lib/services/member_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_hami/config.dart'; 5 | import 'package:http/http.dart' as http; 6 | 7 | class MemberService { 8 | Future onSaveMember( 9 | Map member, String mobile) async { 10 | try { 11 | final postUrl = 12 | '${Config.saveMemberUrl}?mobile=$mobile&pName=${member['HP_Name']}&age=${member['HP_Age']}&gender=${member['HP_Gender']}&api_key=${Config.apiKey}'; 13 | final res = await http.get(Uri.parse(postUrl), headers: { 14 | "Accept": "application/json", 15 | "Access-Control-Allow-Origin": "*" 16 | }); 17 | if (res.statusCode == 200) { 18 | final result = json.decode(res.body)['hAMI_AP_Result'][0]['remarks']; 19 | return result; 20 | } else { 21 | return 'Server error occurred'; 22 | } 23 | } on SocketException catch (_) { 24 | return 'Internet is not connected'; 25 | } 26 | } 27 | 28 | Future> onLoadMembers(String mobile) async { 29 | try { 30 | final postUrl = 31 | '${Config.getMembersUrl}?mobile=$mobile&api_key=${Config.apiKey}'; 32 | final res = await http.get(Uri.parse(postUrl), headers: { 33 | "Accept": "application/json", 34 | "Access-Control-Allow-Origin": "*" 35 | }); 36 | if (res.statusCode == 200) { 37 | final result = json.decode(res.body)['sp_Hami_Patient_Result']; 38 | return {'message': 'success', 'data': result}; 39 | } else { 40 | return { 41 | 'message': 'Server error occurred', 42 | }; 43 | } 44 | } on SocketException catch (_) { 45 | return { 46 | 'message': 'Internet is not connected', 47 | }; 48 | } 49 | } 50 | 51 | // 52 | Future onRemoveMember(int memberId) async { 53 | try { 54 | final postUrl = 55 | '${Config.removeMemberUrl}?pID=$memberId&api_key=${Config.apiKey}'; 56 | final res = await http.get(Uri.parse(postUrl), headers: { 57 | "Accept": "application/json", 58 | "Access-Control-Allow-Origin": "*" 59 | }); 60 | if (res.statusCode == 200) { 61 | final result = json.decode(res.body)['_HAMI_DP_Result'][0]['remarks']; 62 | return result; 63 | } else { 64 | return 'Server error occurred'; 65 | } 66 | } on SocketException catch (_) { 67 | return 'Internet is not connected'; 68 | } 69 | } 70 | 71 | // 72 | Future> onLoadMemberRecord( 73 | String mobile, String memberId) async { 74 | try { 75 | final postUrl = 76 | '${Config.getMemberRecordUrl}?mobile=$mobile&hp_id=$memberId&api_key=${Config.apiKey}'; 77 | final res = await http.get(Uri.parse(postUrl), headers: { 78 | "Accept": "application/json", 79 | "Access-Control-Allow-Origin": "*" 80 | }); 81 | if (res.statusCode == 200) { 82 | final result = json.decode(res.body)['_Hami_Patient_Diary_Result']; 83 | return {'message': 'success', 'data': result}; 84 | } else { 85 | return { 86 | 'message': 'Server error occurred', 87 | }; 88 | } 89 | } on SocketException catch (_) { 90 | return { 91 | 'message': 'Internet is not connected', 92 | }; 93 | } 94 | } 95 | 96 | // 97 | Future onSaveMemberRecord(Map recordMap) async { 98 | try { 99 | final postUrl = Config.saveMemberRecordUrl; 100 | final res = await http.post( 101 | Uri.parse(postUrl), 102 | headers: { 103 | 'Content-Type': 'application/json; charset=UTF-8', 104 | }, 105 | body: jsonEncode(>>{ 106 | 'Hami_Patient_Diary': [recordMap], 107 | }), 108 | ); 109 | if (res.statusCode == 200) { 110 | final result = json.decode(res.body)['message']; 111 | return result; 112 | } else { 113 | return 'Server error occurred'; 114 | } 115 | } on SocketException catch (_) { 116 | return 'Internet is not connected'; 117 | } 118 | } 119 | 120 | Future onRemoveMemberRecord(String mobile, int recordId) async { 121 | try { 122 | final postUrl = 123 | '${Config.removeMemberRecordUrl}?mobile=$mobile&hpd_id=$recordId&api_key=${Config.apiKey}'; 124 | final res = await http.get(Uri.parse(postUrl), headers: { 125 | "Accept": "application/json", 126 | "Access-Control-Allow-Origin": "*" 127 | }); 128 | if (res.statusCode == 200) { 129 | final result = json.decode(res.body)['_HDPD_Result'][0]['remarks']; 130 | return result; 131 | } else { 132 | return 'Server error occurred'; 133 | } 134 | } on SocketException catch (_) { 135 | return 'Internet is not connected'; 136 | } 137 | } 138 | 139 | Future> onLoadMemberMedicineRecord( 140 | String mobile, String memberId) async { 141 | try { 142 | final postUrl = 143 | '${Config.getMemberMedicineRecordUrl}?mobile=$mobile&hp_id=$memberId&api_key=${Config.apiKey}'; 144 | final res = await http.get(Uri.parse(postUrl), headers: { 145 | "Accept": "application/json", 146 | "Access-Control-Allow-Origin": "*" 147 | }); 148 | if (res.statusCode == 200) { 149 | final result = json.decode(res.body)['_Hami_Medicine_Result']; 150 | return {'message': 'success', 'data': result}; 151 | } else { 152 | return { 153 | 'message': 'Server error occurred', 154 | }; 155 | } 156 | } on SocketException catch (_) { 157 | return { 158 | 'message': 'Internet is not connected', 159 | }; 160 | } 161 | } 162 | 163 | Future> onSaveMemberMedicineRecord( 164 | String mobile, 165 | String memberId, 166 | String medName, 167 | String dose, 168 | String tim1, 169 | String tim2, 170 | String tim3, 171 | String tim4, 172 | ) async { 173 | try { 174 | final postUrl = 175 | '${Config.saveMemberMedicineRecordUrl}?mobile=$mobile&pt_id=$memberId&med_desc=$medName&dose=$dose&time1=$tim1&time2=$tim2&time3=$tim3&time4=$tim4&api_key=${Config.apiKey}'; 176 | final res = await http.get(Uri.parse(postUrl), headers: { 177 | "Accept": "application/json", 178 | "Access-Control-Allow-Origin": "*" 179 | }); 180 | if (res.statusCode == 200) { 181 | final result = json.decode(res.body)['aDD_MED_Result']; 182 | return {'message': 'success', 'data': result}; 183 | } else { 184 | return { 185 | 'message': 'Server error occurred', 186 | }; 187 | } 188 | } on SocketException catch (_) { 189 | return { 190 | 'message': 'Internet is not connected', 191 | }; 192 | } 193 | } 194 | 195 | Future> onRemoveMemberMedicineRecord( 196 | String mobile, 197 | String medId, 198 | ) async { 199 | try { 200 | final postUrl = 201 | '${Config.removeMemberMedicineRecordUrl}?mobile=$mobile&hm_id=$medId&api_key=${Config.apiKey}'; 202 | final res = await http.get(Uri.parse(postUrl), headers: { 203 | "Accept": "application/json", 204 | "Access-Control-Allow-Origin": "*" 205 | }); 206 | if (res.statusCode == 200) { 207 | final result = json.decode(res.body)['_Hami_DeleteMedicine_Result']; 208 | return {'message': 'success', 'data': result}; 209 | } else { 210 | return { 211 | 'message': 'Server error occurred', 212 | }; 213 | } 214 | } on SocketException catch (_) { 215 | return { 216 | 'message': 'Internet is not connected', 217 | }; 218 | } 219 | } 220 | 221 | Future> onUploadMemberRXImage( 222 | String mobile, 223 | File image, 224 | ) async { 225 | try { 226 | String uniqueImgID = DateTime.now() 227 | .toString() 228 | .split("-") 229 | .join("") 230 | .split(" ") 231 | .join("") 232 | .split(":") 233 | .join("") 234 | .split(".")[0] + 235 | mobile; 236 | String ext = image.path.split('/').last.split('.').last; 237 | final postUrl = Config.saveMemberRXImageUrl; 238 | final req = http.MultipartRequest("POST", Uri.parse(postUrl)); 239 | req.fields["name"] = uniqueImgID; 240 | final uploadImage = await http.MultipartFile.fromPath("file", image.path); 241 | req.files.add(uploadImage); 242 | var res = await req.send(); 243 | var resData = await res.stream.toBytes(); 244 | var resString = String.fromCharCodes(resData); 245 | var result = json.decode(resString); 246 | return { 247 | 'result': result, 248 | 'uniqueImgID': uniqueImgID, 249 | 'ext': ext 250 | }; 251 | } on SocketException catch (_) { 252 | return { 253 | 'result': { 254 | 'success': false, 255 | 'message': 'Internet is not connected' 256 | }, 257 | }; 258 | } 259 | } 260 | 261 | Future> onSaveMemberRXImageData( 262 | String mobile, 263 | String memberId, 264 | String uniqueImgID, 265 | String ext, 266 | ) async { 267 | try { 268 | final postUrl = 269 | '${Config.saveMemberRXImageDataUrl}?mobile=$mobile&pt_id=$memberId&url=${Config.saveMemberRXImageFolderUrl}$uniqueImgID.$ext&api_key=${Config.apiKey}'; 270 | final res = await http.get(Uri.parse(postUrl), headers: { 271 | "Accept": "application/json", 272 | "Access-Control-Allow-Origin": "*" 273 | }); 274 | if (res.statusCode == 200) { 275 | final result = json.decode(res.body)['hami_Add_Medicine_Image_Result']; 276 | return {'message': 'success', 'data': result}; 277 | } else { 278 | return { 279 | 'message': 'Server error occurred', 280 | }; 281 | } 282 | } on SocketException catch (_) { 283 | return { 284 | 'message': 'Internet is not connected', 285 | }; 286 | } 287 | } 288 | 289 | Future> onLoadMemberRXImage( 290 | String mobile, 291 | String memberId, 292 | ) async { 293 | try { 294 | final postUrl = '${Config.getMemberRXImageUrl}?mobile=$mobile&pt_id=$memberId&api_key=${Config.apiKey}'; 295 | final res = await http.get(Uri.parse(postUrl), headers: { 296 | "Accept": "application/json", 297 | "Access-Control-Allow-Origin": "*" 298 | }); 299 | if (res.statusCode == 200) { 300 | final result = json.decode(res.body)['hami_Medicine_Image_Result']; 301 | return {'message': 'success', 'data': result}; 302 | } else { 303 | return { 304 | 'message': 'Server error occurred', 305 | }; 306 | } 307 | } on SocketException catch (_) { 308 | return { 309 | 'message': 'Internet is not connected', 310 | }; 311 | } 312 | } 313 | 314 | Future> onSendMemberMsg( 315 | String mobile, 316 | String memberId, 317 | String msg, 318 | ) async { 319 | try { 320 | final postUrl = '${Config.saveMemberMsgUrl}?mobile=$mobile&hp_id=$memberId&ask=$msg&api_key=${Config.apiKey}'; 321 | final res = await http.get(Uri.parse(postUrl), headers: { 322 | "Accept": "application/json", 323 | "Access-Control-Allow-Origin": "*" 324 | }); 325 | if (res.statusCode == 200) { 326 | final result = json.decode(res.body)['aSK_Result']; 327 | return {'message': 'success', 'data': result}; 328 | } else { 329 | return { 330 | 'message': 'Server error occurred', 331 | }; 332 | } 333 | } on SocketException catch (_) { 334 | return { 335 | 'message': 'Internet is not connected', 336 | }; 337 | } 338 | } 339 | 340 | Future> onLoadMemberNotifications( 341 | String mobile, 342 | ) async { 343 | try { 344 | final postUrl = '${Config.getNotificationsUrl}?mobile=$mobile&api_key=${Config.apiKey}'; 345 | final res = await http.get(Uri.parse(postUrl), headers: { 346 | "Accept": "application/json", 347 | "Access-Control-Allow-Origin": "*" 348 | }); 349 | if (res.statusCode == 200) { 350 | final result = json.decode(res.body)['_Hami_Notifications_Result']; 351 | return {'message': 'success', 'data': result}; 352 | } else { 353 | return { 354 | 'message': 'Server error occurred', 355 | }; 356 | } 357 | } on SocketException catch (_) { 358 | return { 359 | 'message': 'Internet is not connected', 360 | }; 361 | } 362 | } 363 | 364 | Future> onUpdateMemberNotification( 365 | int aId 366 | ) async { 367 | try { 368 | final postUrl = '${Config.updateNotificationUrl}?haa_id=$aId&api_key=${Config.apiKey}'; 369 | final res = await http.get(Uri.parse(postUrl), headers: { 370 | "Accept": "application/json", 371 | "Access-Control-Allow-Origin": "*" 372 | }); 373 | if (res.statusCode == 200) { 374 | final result = json.decode(res.body)['_Hami_UpdateAnswerView_Result']; 375 | return {'message': 'success', 'data': result}; 376 | } else { 377 | return { 378 | 'message': 'Server error occurred', 379 | }; 380 | } 381 | } on SocketException catch (_) { 382 | return { 383 | 'message': 'Internet is not connected', 384 | }; 385 | } 386 | } 387 | } 388 | -------------------------------------------------------------------------------- /lib/screens/auth/recover_password_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_hami/model/shared_preference.dart'; 5 | import 'package:flutter_hami/screens/auth/forgot_password_page.dart'; 6 | import 'package:flutter_hami/screens/auth/login_page.dart'; 7 | import 'package:fluttertoast/fluttertoast.dart'; 8 | import 'package:internet_connection_checker/internet_connection_checker.dart'; 9 | 10 | import '../../colors.dart'; 11 | import '../../model/user_model.dart'; 12 | import '../../services/auth_service.dart'; 13 | import '../../widget/connectivity_banner.dart'; 14 | import '../../widget/show_dialog.dart'; 15 | 16 | class RecoverPasswordPage extends StatefulWidget { 17 | const RecoverPasswordPage({Key? key}) : super(key: key); 18 | 19 | @override 20 | State createState() => _RecoverPasswordPageState(); 21 | } 22 | 23 | class _RecoverPasswordPageState extends State { 24 | 25 | final _recoverPassFormKey = GlobalKey(); 26 | 27 | DateTime? _currentBackPressTime; 28 | 29 | // controllers 30 | final _passwordCtrl = TextEditingController(); 31 | final _confirmPassCtrl = TextEditingController(); 32 | 33 | // show password 34 | bool _showPassword = false; 35 | 36 | // user info 37 | String _userMobile = ''; 38 | 39 | // has internet 40 | late StreamSubscription internetSubscription; 41 | 42 | @override 43 | initState() { 44 | internetSubscription = InternetConnectionChecker().onStatusChange.listen((status) { 45 | final hasInternet = status == InternetConnectionStatus.connected; 46 | if(!hasInternet) { 47 | connectivityBanner(context, 'No internet connection.', 48 | () => ScaffoldMessenger.of(context).hideCurrentMaterialBanner() 49 | ); 50 | } else { 51 | ScaffoldMessenger.of(context).hideCurrentMaterialBanner(); 52 | } 53 | }); 54 | // 55 | _loadUserData(); 56 | // 57 | super.initState(); 58 | } 59 | 60 | _loadUserData() async { 61 | await SharedPreference().getUserInfo().then((userModel) { 62 | if (userModel.registrationPin != null && userModel.userMobile != null) { 63 | setState(() { 64 | if (userModel.userMobile != null) { 65 | _userMobile = userModel.userMobile!; 66 | } 67 | }); 68 | } else { 69 | Navigator.of(context).push( 70 | MaterialPageRoute( 71 | builder: (context) => const ForgotPasswordPage(), 72 | ), 73 | ); 74 | } 75 | }); 76 | } 77 | 78 | // check internet 79 | Future _hasInternetConnection() async { 80 | return await InternetConnectionChecker().hasConnection; 81 | } 82 | 83 | // validator 84 | String? _validatePassword (String? val){ 85 | if(val == null || val.isEmpty) { 86 | return 'Password is required'; 87 | } 88 | if(val.length < 4) { 89 | return 'Password must be at least 5 characters long'; 90 | } 91 | return null; 92 | } 93 | String? _validateConfirmPassword (String? val){ 94 | if(val == null || val.isEmpty) { 95 | return 'Confirm is required'; 96 | } 97 | if(_passwordCtrl.value.text != val) { 98 | return 'Confirmation password does not match'; 99 | } 100 | return null; 101 | } 102 | 103 | @override 104 | void dispose() { 105 | _passwordCtrl.dispose(); 106 | _confirmPassCtrl.dispose(); 107 | internetSubscription.cancel(); 108 | // TODO: implement dispose 109 | super.dispose(); 110 | } 111 | 112 | Future _onWillPop() async { 113 | DateTime now = DateTime.now(); 114 | if (_currentBackPressTime == null || 115 | now.difference(_currentBackPressTime!) > const Duration(seconds: 2)) { 116 | _currentBackPressTime = now; 117 | Fluttertoast.showToast( 118 | msg: 'Press back again to exit!', 119 | toastLength: Toast.LENGTH_SHORT, 120 | ); 121 | return Future.value(false); 122 | } else { 123 | Fluttertoast.cancel(); 124 | await SharedPreference().removeUser().then((_) { 125 | Navigator.of(context).pushAndRemoveUntil( 126 | MaterialPageRoute(builder: (context) => const LoginPage()), 127 | (Route route) => false, 128 | ); 129 | }); 130 | return Future.value(true); 131 | } 132 | } 133 | 134 | @override 135 | Widget build(BuildContext context) { 136 | Size size = MediaQuery.of(context).size; 137 | return WillPopScope( 138 | onWillPop: _onWillPop, 139 | child: Scaffold( 140 | body: ListView( 141 | children: [ 142 | Padding( 143 | padding: const EdgeInsets.fromLTRB(18, 20, 18, 0), 144 | child: Row( 145 | children: [ 146 | Expanded( 147 | child: Container( 148 | padding: EdgeInsets.only(right: size.width * 0.06), 149 | alignment: Alignment.center, 150 | child: const Text( 151 | 'Recover Password', 152 | style: TextStyle( 153 | fontWeight: FontWeight.w500, 154 | fontSize: 18 155 | ), 156 | ), 157 | ), 158 | ), 159 | ], 160 | ), 161 | ), 162 | SizedBox(height: size.height * 0.05,), 163 | Form( 164 | key: _recoverPassFormKey, 165 | child: SizedBox( 166 | height: size.height * 0.70, 167 | width: double.infinity, 168 | child:Column( 169 | mainAxisAlignment: MainAxisAlignment.center, 170 | crossAxisAlignment: CrossAxisAlignment.center, 171 | children: [ 172 | Padding( 173 | padding: const EdgeInsets.symmetric(horizontal: 24), 174 | child: Stack( 175 | children: [ 176 | Container( 177 | height: 58, 178 | decoration: BoxDecoration( 179 | color: Colors.white, 180 | boxShadow: [ 181 | BoxShadow( 182 | color: kColorPrimary.withOpacity(0.2), 183 | blurRadius: 1, 184 | offset: const Offset( 185 | 0.0, 186 | 3 187 | ), 188 | ), 189 | ], 190 | borderRadius: BorderRadius.circular(5), 191 | ), 192 | ), 193 | TextFormField( 194 | controller: _passwordCtrl, 195 | cursorColor: Colors.black, 196 | obscureText: _showPassword ? false : true, 197 | decoration: InputDecoration( 198 | enabledBorder: const OutlineInputBorder( 199 | borderSide: BorderSide(color: kColorPrimary, width: 1), 200 | ), 201 | border: const OutlineInputBorder(), 202 | focusedBorder: const OutlineInputBorder( 203 | borderSide: BorderSide(color: kColorPrimary, width: 1), 204 | ), 205 | hintText: 'Password', 206 | suffix: GestureDetector( 207 | onTap: () => setState(() => _showPassword = !_showPassword), 208 | child: Text( 209 | _showPassword ? 'Hide' : 'Show', 210 | ), 211 | ), 212 | suffixStyle: const TextStyle( 213 | color: Colors.black, 214 | fontWeight: FontWeight.w600, 215 | ), 216 | ), 217 | autovalidateMode: AutovalidateMode.onUserInteraction, 218 | validator: _validatePassword, 219 | ), 220 | ], 221 | ), 222 | ), 223 | const SizedBox(height: 15,), 224 | Padding( 225 | padding: const EdgeInsets.symmetric(horizontal: 24), 226 | child: Stack( 227 | children: [ 228 | Container( 229 | height: 58, 230 | decoration: BoxDecoration( 231 | color: Colors.white, 232 | boxShadow: [ 233 | BoxShadow( 234 | color: kColorPrimary.withOpacity(0.2), 235 | blurRadius: 1, 236 | offset: const Offset( 237 | 0.0, 238 | 3 239 | ), 240 | ), 241 | ], 242 | borderRadius: BorderRadius.circular(5), 243 | ), 244 | ), 245 | TextFormField( 246 | controller: _confirmPassCtrl, 247 | cursorColor: Colors.black, 248 | obscureText: true, 249 | decoration: const InputDecoration( 250 | enabledBorder: OutlineInputBorder( 251 | borderSide: BorderSide(color: kColorPrimary, width: 1), 252 | ), 253 | border: OutlineInputBorder(), 254 | focusedBorder: OutlineInputBorder( 255 | borderSide: BorderSide(color: kColorPrimary, width: 1), 256 | ), 257 | hintText: 'Confirm Password', 258 | ), 259 | autovalidateMode: AutovalidateMode.onUserInteraction, 260 | validator: _validateConfirmPassword, 261 | ), 262 | ], 263 | ), 264 | ), 265 | const SizedBox(height: 20,), 266 | Padding( 267 | padding: const EdgeInsets.symmetric(horizontal: 20), 268 | child: Material( 269 | color: kColorPrimary, 270 | borderRadius: BorderRadius.circular(2), 271 | child: InkWell( 272 | onTap: _onPressedChangePassword, 273 | child: Container( 274 | alignment: Alignment.center, 275 | width: double.infinity, 276 | height: size.height * 0.065, 277 | child: Text( 278 | 'Change Password', 279 | style: TextStyle( 280 | color: Colors.white, 281 | fontWeight: FontWeight.w600, 282 | fontSize: size.width * 0.045, 283 | ), 284 | ), 285 | ), 286 | ), 287 | ), 288 | ), 289 | ], 290 | ), 291 | ), 292 | ), 293 | ], 294 | ), 295 | ), 296 | ); 297 | } 298 | 299 | _onPressedChangePassword() { 300 | bool formValidation = _recoverPassFormKey.currentState!.validate(); 301 | if(formValidation) { 302 | showDialogBox(context); 303 | _onPostChangePassword().then((res) { 304 | final String message = res['message'] ?? 'Error occurred'; 305 | if (message == 'Password updated!') { 306 | _onDoneChangePassword(); 307 | } else { 308 | Navigator.pop(context); 309 | Fluttertoast.showToast( 310 | msg: message, 311 | toastLength: Toast.LENGTH_SHORT, 312 | ); 313 | } 314 | }); 315 | } 316 | } 317 | 318 | Future> _onPostChangePassword() async { 319 | // check internet connectivity 320 | final hasInternet = await _hasInternetConnection(); 321 | if(hasInternet) { 322 | final user = UserModel( 323 | userMobile: _userMobile, 324 | userPassword: _passwordCtrl.value.text 325 | ); 326 | final res = await AuthService().onVerifyUser(user, false, true); 327 | return { 328 | 'message' : res, 329 | }; 330 | } 331 | return { 332 | 'message' : 'No internet connection', 333 | }; 334 | } 335 | 336 | _onDoneChangePassword() async { 337 | Navigator.pop(context); 338 | await SharedPreference().removeUser().then((_) { 339 | Fluttertoast.showToast( 340 | msg: 'Password has been updated.', 341 | toastLength: Toast.LENGTH_LONG, 342 | ); 343 | Navigator.of(context).pushAndRemoveUntil( 344 | MaterialPageRoute(builder: (context) => const LoginPage()), 345 | (Route route) => false, 346 | ); 347 | }); 348 | } 349 | 350 | } 351 | -------------------------------------------------------------------------------- /lib/screens/user/awareness_material_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_hami/screens/dashboard_page.dart'; 6 | import 'package:flutter_hami/screens/user/userMaterial/bp_heart_disease_page.dart'; 7 | import 'package:flutter_hami/screens/user/userMaterial/bp_ramazan_page.dart'; 8 | import 'package:flutter_hami/screens/user/userMaterial/diet_chart_page.dart'; 9 | import 'package:flutter_hami/screens/user/userMaterial/videos_page.dart'; 10 | import 'package:flutter_tabler_icons/flutter_tabler_icons.dart'; 11 | import 'package:fluttertoast/fluttertoast.dart'; 12 | import 'package:internet_connection_checker/internet_connection_checker.dart'; 13 | 14 | import '../../colors.dart'; 15 | import '../../widget/connectivity_banner.dart'; 16 | 17 | const kColorBg = Color(0xfff2f6fe); 18 | 19 | enum TileType { 20 | videos, 21 | dietChart, 22 | bloodPressureRamazan, 23 | bloodPressureHeartDisease 24 | } 25 | 26 | class AwarenessMaterialPage extends StatefulWidget { 27 | final String mobile; 28 | const AwarenessMaterialPage({required this.mobile, Key? key}) 29 | : super(key: key); 30 | 31 | @override 32 | State createState() => _AwarenessMaterialPageState(); 33 | } 34 | 35 | class _AwarenessMaterialPageState extends State { 36 | final _actionLabel = [ 37 | { 38 | 'en': 'Videos', 39 | 'ur': 'ویڈیوز', 40 | 'icon': TablerIcons.video, 41 | 'action': TileType.videos 42 | }, 43 | { 44 | 'en': 'Diet Chart', 45 | 'ur': 'ڈائیٹ چارٹ', 46 | 'icon': Icons.medical_information, 47 | 'action': TileType.dietChart 48 | }, 49 | { 50 | 'en': 'Blood Pressure', 51 | 'en1': '& Ramazan', 52 | 'ur': 'بلڈپریشراوررمضان ', 53 | 'icon': TablerIcons.stethoscope, 54 | 'action': TileType.bloodPressureRamazan 55 | }, 56 | { 57 | 'en': 'Blood Pressure', 58 | 'en1': '& Heart Disease', 59 | 'ur': 'بلڈپریشراورامراضِ دل', 60 | 'icon': Icons.monitor_heart_outlined, 61 | 'action': TileType.bloodPressureHeartDisease 62 | }, 63 | ]; 64 | 65 | // has internet 66 | late StreamSubscription internetSubscription; 67 | 68 | @override 69 | void initState() { 70 | internetSubscription = 71 | InternetConnectionChecker().onStatusChange.listen((status) { 72 | final hasInternet = status == InternetConnectionStatus.connected; 73 | if (!hasInternet) { 74 | connectivityBanner(context, 'No internet connection.', 75 | () => ScaffoldMessenger.of(context).hideCurrentMaterialBanner()); 76 | } else { 77 | ScaffoldMessenger.of(context).hideCurrentMaterialBanner(); 78 | } 79 | }); 80 | super.initState(); 81 | } 82 | 83 | // check internet 84 | Future _hasInternetConnection() async { 85 | return await InternetConnectionChecker().hasConnection; 86 | } 87 | 88 | @override 89 | void dispose() { 90 | internetSubscription.cancel(); 91 | // TODO: implement dispose 92 | super.dispose(); 93 | } 94 | 95 | @override 96 | Widget build(BuildContext context) { 97 | Size size = MediaQuery.of(context).size; 98 | return WillPopScope( 99 | onWillPop: () { 100 | Navigator.of(context).pushAndRemoveUntil( 101 | MaterialPageRoute( 102 | builder: (context) => 103 | DashboardPage(mobile: widget.mobile), 104 | ), 105 | (Route route) => false, 106 | ); 107 | return Future.value(true); 108 | }, 109 | child: Scaffold( 110 | backgroundColor: kColorBg, 111 | appBar: PreferredSize( 112 | preferredSize: Size.zero, 113 | child: AppBar( 114 | elevation: 0, 115 | backgroundColor: kColorPrimary, //ios status bar colors 116 | systemOverlayStyle: const SystemUiOverlayStyle( 117 | statusBarColor: kColorPrimary, //android status bar color 118 | statusBarBrightness: Brightness.dark, // For iOS: (dark icons) 119 | statusBarIconBrightness: 120 | Brightness.light, // For Android: (dark icons) 121 | ), 122 | ), 123 | ), 124 | body: Column( 125 | children: [ 126 | Container( 127 | width: double.infinity, 128 | height: size.height * 0.38, 129 | decoration: const BoxDecoration( 130 | color: kColorPrimary, 131 | borderRadius: BorderRadius.vertical(bottom: Radius.circular(40)), 132 | ), 133 | child: Stack( 134 | children: [ 135 | Positioned( 136 | left: size.width * 0.045, 137 | top: 20, 138 | child: Row( 139 | children: [ 140 | InkWell( 141 | onTap: () { 142 | Navigator.of(context).pushAndRemoveUntil( 143 | MaterialPageRoute( 144 | builder: (context) => DashboardPage( 145 | mobile: widget.mobile, 146 | ), 147 | ), 148 | (Route route) => false, 149 | ); 150 | }, 151 | child: const Icon( 152 | Icons.arrow_back_ios, 153 | size: 18, 154 | color: Colors.white, 155 | ), 156 | ), 157 | Container( 158 | width: size.width * 0.89, 159 | padding: EdgeInsets.only( 160 | right: size.width * 0.06, 161 | ), 162 | alignment: Alignment.center, 163 | child: const Text( 164 | 'Awareness Material', 165 | style: TextStyle( 166 | fontWeight: FontWeight.w500, 167 | fontSize: 18, 168 | color: Colors.white, 169 | ), 170 | ), 171 | ), 172 | ], 173 | ), 174 | ), 175 | Center( 176 | child: Container( 177 | margin: const EdgeInsets.only(top: 25), 178 | padding: const EdgeInsets.all(10), 179 | decoration: const BoxDecoration( 180 | color: Colors.white, 181 | shape: BoxShape.circle, 182 | ), 183 | child: Container( 184 | padding: const EdgeInsets.all(10), 185 | decoration: const BoxDecoration( 186 | color: Colors.grey, 187 | shape: BoxShape.circle, 188 | ), 189 | child: Container( 190 | height: size.height * 0.15, 191 | width: size.height * 0.15, 192 | decoration: const BoxDecoration( 193 | color: Colors.white38, 194 | shape: BoxShape.circle, 195 | boxShadow: [ 196 | BoxShadow( 197 | color: Colors.white, 198 | offset: Offset.zero, 199 | blurRadius: 30, 200 | spreadRadius: 8, 201 | blurStyle: BlurStyle.inner, 202 | ), 203 | ]), 204 | child: Column( 205 | mainAxisAlignment: MainAxisAlignment.center, 206 | children: [ 207 | SizedBox( 208 | height: size.height * 0.15, 209 | width: size.height * 0.15, 210 | child: const CircleAvatar( 211 | backgroundColor: Colors.white, 212 | backgroundImage: AssetImage( 213 | 'assets/images/header.png', 214 | ), 215 | ), 216 | ), 217 | ], 218 | ), 219 | ), 220 | ), 221 | ), 222 | ), 223 | ], 224 | ), 225 | ), 226 | Expanded( 227 | child: Padding( 228 | padding: const EdgeInsets.all(20), 229 | child: GridView( 230 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( 231 | crossAxisCount: 2, 232 | mainAxisExtent: size.height * 0.24, 233 | crossAxisSpacing: 20, 234 | mainAxisSpacing: 10, 235 | ), 236 | children: List.generate(4, (idx) { 237 | return Material( 238 | borderRadius: BorderRadius.circular(10), 239 | color: Colors.white.withOpacity(0.9), 240 | child: InkWell( 241 | borderRadius: BorderRadius.all( 242 | Radius.circular((size.height * 0.01)), 243 | ), 244 | onTap: () async { 245 | // check internet connectivity 246 | final hasInternet = await _hasInternetConnection(); 247 | if (hasInternet) { 248 | onPressedGridTile( 249 | _actionLabel[idx]['action'] as TileType, 250 | ); 251 | } else { 252 | Fluttertoast.showToast( 253 | msg: 'No internet connection', 254 | toastLength: Toast.LENGTH_LONG, 255 | ); 256 | } 257 | }, 258 | child: Column( 259 | mainAxisAlignment: MainAxisAlignment.center, 260 | children: [ 261 | Transform.rotate( 262 | angle: 4, 263 | child: Container( 264 | width: 50, 265 | height: 50, 266 | color: kColorPrimary.withOpacity(0.9), 267 | child: Transform.rotate( 268 | angle: -4, 269 | child: Icon( 270 | _actionLabel[idx]['icon'] as IconData, 271 | size: 25, 272 | color: Colors.white, 273 | ), 274 | ), 275 | ), 276 | ), 277 | const SizedBox( 278 | height: 15, 279 | ), 280 | _actionLabel[idx]['en1'] == null 281 | ? Column( 282 | children: [ 283 | Text( 284 | _actionLabel[idx]['en'].toString(), 285 | style: const TextStyle( 286 | fontSize: 17, 287 | fontWeight: FontWeight.w500, 288 | ), 289 | ), 290 | const SizedBox( 291 | height: 3, 292 | ), 293 | Text( 294 | _actionLabel[idx]['ur'].toString(), 295 | style: const TextStyle( 296 | fontSize: 20, 297 | fontWeight: FontWeight.w500, 298 | fontFamily: 'Jameel-Noori-Nastaleeq', 299 | ), 300 | ), 301 | ], 302 | ) 303 | : Column( 304 | //mainAxisAlignment: MainAxisAlignment.center, 305 | children: [ 306 | Text( 307 | _actionLabel[idx]['en'].toString(), 308 | style: const TextStyle( 309 | fontSize: 15, 310 | fontWeight: FontWeight.w500, 311 | ), 312 | ), 313 | Text( 314 | _actionLabel[idx]['en1'].toString(), 315 | style: const TextStyle( 316 | fontSize: 15, 317 | fontWeight: FontWeight.w500, 318 | ), 319 | ), 320 | const SizedBox( 321 | height: 4, 322 | ), 323 | Text( 324 | _actionLabel[idx]['ur'].toString(), 325 | style: const TextStyle( 326 | fontSize: 20, 327 | fontWeight: FontWeight.w500, 328 | fontFamily: 'Jameel-Noori-Nastaleeq', 329 | ), 330 | ), 331 | ], 332 | ), 333 | ], 334 | ), 335 | ), 336 | ); 337 | }), 338 | ), 339 | ), 340 | ), 341 | ], 342 | ), 343 | ), 344 | ); 345 | } 346 | 347 | // 348 | onPressedGridTile(TileType action) { 349 | switch (action) { 350 | case TileType.videos: 351 | Navigator.push( 352 | context, 353 | MaterialPageRoute( 354 | builder: (context) => VideosPage( 355 | mobile: widget.mobile, 356 | ), 357 | ), 358 | ); 359 | break; 360 | case TileType.dietChart: 361 | Navigator.push( 362 | context, 363 | MaterialPageRoute( 364 | builder: (context) => DietChartPage( 365 | mobile: widget.mobile, 366 | ), 367 | ), 368 | ); 369 | break; 370 | case TileType.bloodPressureRamazan: 371 | Navigator.push( 372 | context, 373 | MaterialPageRoute( 374 | builder: (context) => BpRamazanPage( 375 | mobile: widget.mobile, 376 | ), 377 | ), 378 | ); 379 | break; 380 | case TileType.bloodPressureHeartDisease: 381 | Navigator.push( 382 | context, 383 | MaterialPageRoute( 384 | builder: (context) => BPHeartDiseasePage( 385 | mobile: widget.mobile, 386 | ), 387 | ), 388 | ); 389 | break; 390 | } 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /lib/screens/user/notification_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_hami/widget/show_dialog_html.dart'; 6 | import 'package:internet_connection_checker/internet_connection_checker.dart'; 7 | import 'package:intl/intl.dart'; 8 | 9 | import '../../model/shared_preference.dart'; 10 | import '../../services/member_service.dart'; 11 | import '../../widget/connectivity_banner.dart'; 12 | import '../dashboard_page.dart'; 13 | 14 | const kColorBg = Color(0xfff2f6fe); 15 | 16 | class NotificationPage extends StatefulWidget { 17 | final String? mobile; 18 | const NotificationPage({this.mobile, Key? key}) : super(key: key); 19 | 20 | @override 21 | State createState() => _NotificationPageState(); 22 | } 23 | 24 | class _NotificationPageState extends State { 25 | // data 26 | List _notificationData = []; 27 | 28 | // check if page is loaded 29 | bool _isPageLoaded = false; 30 | String? _mobile; 31 | 32 | // has internet 33 | late StreamSubscription internetSubscription; 34 | 35 | @override 36 | void initState() { 37 | internetSubscription = 38 | InternetConnectionChecker().onStatusChange.listen((status) { 39 | final hasInternet = status == InternetConnectionStatus.connected; 40 | if (!hasInternet) { 41 | connectivityBanner(context, 'No internet connection.', 42 | () => ScaffoldMessenger.of(context).hideCurrentMaterialBanner()); 43 | } else { 44 | ScaffoldMessenger.of(context).hideCurrentMaterialBanner(); 45 | } 46 | }); 47 | // load notifications data 48 | _loadUserData(); 49 | super.initState(); 50 | } 51 | 52 | _loadUserData() async { 53 | final data = await SharedPreference().getUserInfo(); 54 | if(widget.mobile == null) { 55 | _mobile = data.userMobile; 56 | } else { 57 | _mobile = widget.mobile; 58 | } 59 | _loadNotificationData(); 60 | } 61 | 62 | // check internet 63 | Future _hasInternetConnection() async { 64 | return await InternetConnectionChecker().hasConnection; 65 | } 66 | 67 | _loadNotificationData() async { 68 | // check internet connectivity 69 | final hasInternet = await _hasInternetConnection(); 70 | if (hasInternet) { 71 | final result = await MemberService().onLoadMemberNotifications( 72 | _mobile!, 73 | ); 74 | final message = result['message']; 75 | if (message == 'success') { 76 | final data = result['data']; 77 | _notificationData = data; 78 | debugPrint(_notificationData.toString()); 79 | _isPageLoaded = true; 80 | setState(() {}); 81 | } 82 | } else { 83 | _isPageLoaded = true; 84 | setState(() {}); 85 | } 86 | } 87 | 88 | @override 89 | void dispose() { 90 | internetSubscription.cancel(); 91 | // TODO: implement dispose 92 | super.dispose(); 93 | } 94 | 95 | @override 96 | Widget build(BuildContext context) { 97 | Size size = MediaQuery.of(context).size; 98 | return WillPopScope( 99 | onWillPop: () async { 100 | Navigator.of(context).pushAndRemoveUntil( 101 | MaterialPageRoute(builder: (context) => DashboardPage(mobile: widget.mobile!)), 102 | (Route route) => false, 103 | ); 104 | return Future.value(true); 105 | }, 106 | child: Scaffold( 107 | backgroundColor: kColorBg, 108 | appBar: PreferredSize( 109 | preferredSize: Size.zero, 110 | child: AppBar( 111 | elevation: 0, 112 | backgroundColor: kColorBg, //ios status bar colors 113 | systemOverlayStyle: const SystemUiOverlayStyle( 114 | statusBarColor: kColorBg, //android status bar color 115 | statusBarBrightness: Brightness.light, // For iOS: (dark icons) 116 | statusBarIconBrightness: 117 | Brightness.dark, // For Android: (dark icons) 118 | ), 119 | ), 120 | ), 121 | body: SizedBox( 122 | width: size.width, 123 | height: size.height, 124 | child: _isPageLoaded 125 | ? Column( 126 | children: [ 127 | Padding( 128 | padding: const EdgeInsets.fromLTRB(18, 20, 18, 0), 129 | child: Row( 130 | children: [ 131 | InkWell( 132 | onTap: () { 133 | Navigator.of(context).pushAndRemoveUntil( 134 | MaterialPageRoute( 135 | builder: (context) => 136 | DashboardPage(mobile: _mobile!), 137 | ), 138 | (Route route) => false, 139 | ); 140 | }, 141 | child: const Icon( 142 | Icons.arrow_back_ios, 143 | size: 18, 144 | ), 145 | ), 146 | Expanded( 147 | child: Container( 148 | padding: EdgeInsets.only(right: size.width * 0.06), 149 | alignment: Alignment.center, 150 | child: const Text( 151 | 'Notifications', 152 | style: TextStyle( 153 | fontWeight: FontWeight.w500, 154 | fontSize: 18, 155 | ), 156 | ), 157 | ), 158 | ), 159 | ], 160 | ), 161 | ), 162 | const SizedBox( 163 | height: 20, 164 | ), 165 | Container( 166 | alignment: Alignment.centerLeft, 167 | padding: const EdgeInsets.only( 168 | left: 20, 169 | bottom: 8, 170 | ), 171 | child: const Text( 172 | 'All notifications list', 173 | style: TextStyle( 174 | fontSize: 15, 175 | fontWeight: FontWeight.w600, 176 | ), 177 | ), 178 | ), 179 | Expanded(child: _buildShowNotification()), 180 | ], 181 | ) 182 | : const Center( 183 | child: CircularProgressIndicator(), 184 | ), 185 | ), 186 | ), 187 | ); 188 | } 189 | 190 | // 191 | Widget _buildShowNotification() { 192 | Size size = MediaQuery.of(context).size; 193 | return _notificationData.isNotEmpty 194 | ? ListView.builder( 195 | itemCount: _notificationData.length, 196 | itemBuilder: (_, idx) { 197 | return Material( 198 | color: _notificationData[idx]['HAA_IsViewed'] 199 | ? Colors.white.withOpacity(0.5) 200 | : const Color(0xFFeceff5), 201 | child: InkWell( 202 | onTap: () => _onPressedNotification( 203 | _notificationData[idx]['HA_Question'], 204 | _notificationData[idx]['HAA_Answer'], 205 | _notificationData[idx]['HAA_ID'], 206 | _notificationData[idx]['HAA_IsViewed'] 207 | ), 208 | child: Container( 209 | height: 80, 210 | decoration: BoxDecoration( 211 | border: Border( 212 | bottom: BorderSide( 213 | color: Colors.grey.withOpacity(0.5), 214 | width: 1, 215 | style: BorderStyle.solid, 216 | ), 217 | )), 218 | child: Column( 219 | children: [ 220 | Expanded( 221 | child: Row( 222 | children: [ 223 | SizedBox( 224 | width: size.width * 0.16, 225 | height: double.maxFinite, 226 | child: Container( 227 | margin: EdgeInsets.all(size.width * 0.035), 228 | decoration: const BoxDecoration( 229 | color: Colors.black, 230 | shape: BoxShape.circle, 231 | ), 232 | child: const Icon( 233 | Icons.notifications, 234 | color: Colors.white, 235 | ), 236 | ), 237 | ), 238 | Expanded( 239 | child: Padding( 240 | padding: const EdgeInsets.only( 241 | left: 2, 242 | right: 10, 243 | top: 5, 244 | bottom: 10, 245 | ), 246 | child: Column( 247 | mainAxisAlignment: MainAxisAlignment.center, 248 | crossAxisAlignment: CrossAxisAlignment.start, 249 | children: [ 250 | Text.rich( 251 | TextSpan( 252 | children: [ 253 | const TextSpan( 254 | text: 'Answer for your question: ', 255 | style: TextStyle( 256 | fontSize: 14, 257 | color: Colors.black, 258 | ), 259 | ), 260 | TextSpan( 261 | text: _notificationData[idx] 262 | ['HA_Question'], 263 | style: const TextStyle( 264 | fontSize: 14, 265 | fontWeight: FontWeight.w600, 266 | ), 267 | ), 268 | ], 269 | ), 270 | maxLines: 2, 271 | overflow: TextOverflow.ellipsis, 272 | ), 273 | const SizedBox( 274 | height: 1, 275 | ), 276 | Text( 277 | DateFormat('MMM dd, yyyy') 278 | .format(DateTime.now()), 279 | style: const TextStyle( 280 | fontSize: 10, 281 | fontWeight: FontWeight.w600, 282 | ), 283 | ), 284 | ], 285 | ), 286 | ), 287 | ), 288 | ], 289 | ), 290 | ), 291 | ], 292 | ), 293 | ), 294 | ), 295 | ); 296 | }, 297 | ) 298 | : Container( 299 | margin: EdgeInsets.only( 300 | bottom: size.height * 0.15, 301 | ), 302 | child: Column( 303 | mainAxisAlignment: MainAxisAlignment.center, 304 | crossAxisAlignment: CrossAxisAlignment.center, 305 | children: [ 306 | const Icon( 307 | Icons.notifications_off_sharp, 308 | size: 80, 309 | color: Colors.redAccent, 310 | ), 311 | const SizedBox( 312 | height: 10, 313 | ), 314 | const Text( 315 | 'No notifications', 316 | style: TextStyle( 317 | fontSize: 28, 318 | fontWeight: FontWeight.w600, 319 | ), 320 | ), 321 | const SizedBox( 322 | height: 10, 323 | ), 324 | const Text( 325 | 'When you have notification\n you\'ll see then here', 326 | textAlign: TextAlign.center, 327 | style: TextStyle( 328 | color: Colors.black, 329 | fontWeight: FontWeight.w500, 330 | ), 331 | ), 332 | const SizedBox( 333 | height: 10, 334 | ), 335 | ElevatedButton( 336 | onPressed: () => Navigator.of(context).pushReplacement( 337 | MaterialPageRoute( 338 | builder: (context) => 339 | DashboardPage(mobile: _mobile!), 340 | ), 341 | ), 342 | child: const Text( 343 | 'Go Back', 344 | style: TextStyle( 345 | fontSize: 15, 346 | ), 347 | ), 348 | ), 349 | ], 350 | ), 351 | ); 352 | } 353 | // 354 | _onPressedNotification(String q, String a, int aId, bool isViewed) {//question, answer, askAnswerId, isAnswerViewed 355 | showDialogHtml(context, 'View Answer', a, [ 356 | TextButton( 357 | style: TextButton.styleFrom( 358 | primary: Colors.blue, 359 | ), 360 | child: const Text("View Question"), 361 | onPressed: () => _onPressedViewQuestion(q), 362 | ), 363 | TextButton( 364 | style: TextButton.styleFrom( 365 | primary: Colors.blue, 366 | ), 367 | child: const Text("Close"), 368 | onPressed: () { 369 | Navigator.of(context).pop(); 370 | }, 371 | ), 372 | ]); 373 | if(isViewed == false) { 374 | _updateNotification(aId); 375 | } 376 | } 377 | // 378 | _onPressedViewQuestion(String q) { 379 | Navigator.of(context).pop(); 380 | showDialogHtml(context, 'View Question', q, [ 381 | TextButton( 382 | style: TextButton.styleFrom( 383 | primary: Colors.blue, 384 | ), 385 | child: const Text("Hide"), 386 | onPressed: () { 387 | Navigator.of(context).pop(); 388 | }, 389 | ), 390 | ]); 391 | } 392 | // 393 | _updateNotification(int aId) async { 394 | // check internet connectivity 395 | final hasInternet = await _hasInternetConnection(); 396 | if (hasInternet) { 397 | _onPostUpdateNotification(aId); 398 | } 399 | } 400 | // 401 | _onPostUpdateNotification(int aId) async { 402 | final result = await MemberService().onUpdateMemberNotification(aId); 403 | final message = result['message']; 404 | if(message == 'success') { 405 | final data = result['data']; 406 | final remarks = data[0]['remarks']; 407 | if(remarks == 'Success!') { 408 | final notificationData = _notificationData; 409 | final notification = notificationData.where((e) => e['HAA_ID'] == aId).first; 410 | final index = notificationData.indexOf(notification); 411 | notificationData[index]['HAA_IsViewed'] = true; 412 | setState(() {}); 413 | } else { 414 | debugPrint(remarks); 415 | } 416 | } else { 417 | debugPrint(message); 418 | } 419 | } 420 | } 421 | -------------------------------------------------------------------------------- /lib/screens/auth/forgot_password_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_hami/screens/auth/login_page.dart'; 6 | import 'package:flutter_hami/screens/auth/verify_pin_page.dart'; 7 | import 'package:flutter_hami/widget/show_dialog.dart'; 8 | import 'package:fluttertoast/fluttertoast.dart'; 9 | import 'package:internet_connection_checker/internet_connection_checker.dart'; 10 | import 'dart:math' as math; 11 | 12 | import '../../colors.dart'; 13 | import '../../constants.dart'; 14 | import '../../model/shared_preference.dart'; 15 | import '../../model/user_model.dart'; 16 | import '../../services/auth_service.dart'; 17 | import '../../widget/connectivity_banner.dart'; 18 | 19 | class ForgotPasswordPage extends StatefulWidget { 20 | const ForgotPasswordPage({Key? key}) : super(key: key); 21 | 22 | @override 23 | State createState() => _ForgotPasswordPageState(); 24 | } 25 | 26 | class _ForgotPasswordPageState extends State { 27 | 28 | // controller 29 | final _phoneNumberCtrl = TextEditingController(); 30 | 31 | // errorMessages 32 | String _phoneNumberErrMsg = ''; 33 | 34 | // node 35 | final _phoneNumberNode = FocusNode(); 36 | 37 | // has internet 38 | late StreamSubscription internetSubscription; 39 | 40 | @override 41 | initState() { 42 | internetSubscription = InternetConnectionChecker().onStatusChange.listen((status) { 43 | final hasInternet = status == InternetConnectionStatus.connected; 44 | if(!hasInternet) { 45 | connectivityBanner(context, 'No internet connection.', 46 | () => ScaffoldMessenger.of(context).hideCurrentMaterialBanner() 47 | ); 48 | } else { 49 | ScaffoldMessenger.of(context).hideCurrentMaterialBanner(); 50 | } 51 | }); 52 | // 53 | super.initState(); 54 | } 55 | 56 | // check internet 57 | Future _hasInternetConnection() async { 58 | return await InternetConnectionChecker().hasConnection; 59 | } 60 | 61 | _validatePhoneNumber(String? val) { 62 | setState(() { 63 | if(val != null && val.isNotEmpty) { 64 | if(RegExp(r'^[0-9]*$').hasMatch(val)) { 65 | if(val.length == 10) { 66 | _phoneNumberErrMsg = ''; 67 | } else { 68 | _phoneNumberErrMsg = 'Invalid phone number length'; 69 | } 70 | } else { 71 | _phoneNumberErrMsg = 'Invalid phone number'; 72 | } 73 | } else { 74 | _phoneNumberErrMsg = 'Phone number is required'; 75 | } 76 | }); 77 | } 78 | 79 | @override 80 | void dispose() { 81 | _phoneNumberCtrl.dispose(); 82 | internetSubscription.cancel(); 83 | // TODO: implement dispose 84 | super.dispose(); 85 | } 86 | 87 | @override 88 | Widget build(BuildContext context) { 89 | Size size = MediaQuery.of(context).size; 90 | return Scaffold( 91 | backgroundColor: Colors.white, 92 | appBar: PreferredSize( 93 | preferredSize: Size.zero, 94 | child: AppBar( 95 | elevation: 0, 96 | backgroundColor: Colors.white, //ios status bar colors 97 | systemOverlayStyle: const SystemUiOverlayStyle( 98 | statusBarColor: Colors.white, //android status bar color 99 | statusBarBrightness: Brightness.light, // For iOS: (dark icons) 100 | statusBarIconBrightness: 101 | Brightness.dark, // For Android: (dark icons) 102 | ), 103 | ), 104 | ), 105 | body: GestureDetector( 106 | onTap: () { 107 | FocusScope.of(context).requestFocus(FocusNode()); 108 | }, 109 | child: SafeArea( 110 | child: ListView( 111 | children: [ 112 | Padding( 113 | padding: const EdgeInsets.fromLTRB(18, 20, 18, 0), 114 | child: Row( 115 | children: [ 116 | InkWell( 117 | onTap: () { 118 | Navigator.of(context).pushAndRemoveUntil( 119 | MaterialPageRoute( 120 | builder: (context) => const LoginPage(), 121 | ), (Route route) => false, 122 | ); 123 | }, 124 | child: const Icon( 125 | Icons.arrow_back_ios, 126 | size: 20, 127 | ), 128 | ), 129 | Expanded( 130 | child: Container( 131 | padding: EdgeInsets.only(right: size.width * 0.06), 132 | alignment: Alignment.center, 133 | child: const Text( 134 | 'Forgot Password', 135 | style: TextStyle( 136 | fontWeight: FontWeight.w500, 137 | fontSize: 18 138 | ), 139 | ), 140 | ), 141 | ), 142 | ], 143 | ), 144 | ), 145 | SizedBox(height: size.height * 0.05,), 146 | SizedBox( 147 | height: size.height * 0.75, 148 | width: double.infinity, 149 | child: Column( 150 | mainAxisAlignment: MainAxisAlignment.center, 151 | crossAxisAlignment: CrossAxisAlignment.center, 152 | children: [ 153 | Padding( 154 | padding: const EdgeInsets.symmetric(horizontal: 24), 155 | child: Stack( 156 | children: [ 157 | Container( 158 | height: 60, 159 | decoration: BoxDecoration( 160 | color: Colors.white, 161 | boxShadow: [ 162 | BoxShadow( 163 | color: kColorPrimary.withOpacity(0.2), 164 | blurRadius: 1, 165 | offset: const Offset( 166 | 0.0, 167 | 3 168 | ), 169 | ), 170 | ], 171 | borderRadius: BorderRadius.circular(5), 172 | ), 173 | ), 174 | Column( 175 | children: [ 176 | Container( 177 | alignment: Alignment.center, 178 | height: 60, 179 | width: double.infinity, 180 | decoration: BoxDecoration( 181 | borderRadius: BorderRadius.circular(5), 182 | border: Border.all(color: kColorPrimary, width: kInputBorderWidth, style: BorderStyle.solid,), 183 | ), 184 | child: Row( 185 | children: [ 186 | Padding( 187 | padding: const EdgeInsets.symmetric(horizontal: 10), 188 | child: GestureDetector( 189 | onTap: () { 190 | FocusScope.of(context).requestFocus(_phoneNumberNode); 191 | }, 192 | child: Row( 193 | children: [ 194 | Container( 195 | alignment: Alignment.center, 196 | padding: const EdgeInsets.only(bottom: 1), 197 | child: Text( 198 | '+', 199 | style: TextStyle( 200 | color: kColorPrimary, 201 | fontSize: size.width*0.038, 202 | fontWeight: FontWeight.bold, 203 | ), 204 | ), 205 | ), 206 | Text( 207 | '92', 208 | style: TextStyle( 209 | color: kColorPrimary, 210 | fontSize: size.width*0.035, 211 | fontWeight: FontWeight.bold, 212 | letterSpacing: 0.5, 213 | ), 214 | ), 215 | ], 216 | ), 217 | ), 218 | ), 219 | Container( 220 | width: 1, 221 | color: kColorPrimary.withOpacity(0.5), 222 | ), 223 | Expanded( 224 | child: Padding( 225 | padding: const EdgeInsets.symmetric(horizontal: 15), 226 | child: TextFormField( 227 | focusNode: _phoneNumberNode, 228 | controller: _phoneNumberCtrl, 229 | keyboardType: TextInputType.number, 230 | inputFormatters: [ 231 | LengthLimitingTextInputFormatter(10), 232 | ], 233 | cursorColor: Colors.black, 234 | decoration: const InputDecoration( 235 | border: InputBorder.none, 236 | hintText: 'Phone Number' 237 | ), 238 | autovalidateMode: AutovalidateMode.onUserInteraction, 239 | onChanged: _validatePhoneNumber, 240 | ), 241 | ), 242 | ), 243 | ], 244 | ), 245 | ), 246 | Container( 247 | height: _phoneNumberErrMsg.isEmpty ? 0: null, 248 | alignment: Alignment.centerLeft, 249 | child: Padding( 250 | padding: const EdgeInsets.only(top: 6, left: 16), 251 | child: Text( 252 | _phoneNumberErrMsg, 253 | style: const TextStyle( 254 | color: kColorPrimary, 255 | fontSize: 12, 256 | ), 257 | ), 258 | ), 259 | ), 260 | ], 261 | ), 262 | ], 263 | ), 264 | ), 265 | const SizedBox(height: 20,), 266 | Padding( 267 | padding: const EdgeInsets.symmetric(horizontal: 20), 268 | child: Material( 269 | color: kColorPrimary, 270 | borderRadius: BorderRadius.circular(2), 271 | child: InkWell( 272 | onTap: _onPressedForgotPass, 273 | child: Container( 274 | alignment: Alignment.center, 275 | width: double.infinity, 276 | height: size.height * 0.065, 277 | child: Text( 278 | 'Forgot Password', 279 | style: TextStyle( 280 | color: Colors.white, 281 | fontWeight: FontWeight.w600, 282 | fontSize: size.width * 0.045, 283 | ), 284 | ), 285 | ), 286 | ), 287 | ), 288 | ), 289 | ], 290 | ), 291 | ), 292 | ], 293 | ), 294 | ), 295 | ), 296 | ); 297 | } 298 | 299 | // custom validate form 300 | bool _customValidateForm() { 301 | // phone number 302 | if(_phoneNumberCtrl.value.text.isEmpty) { 303 | // required 304 | return false; 305 | } else { 306 | // not a number 307 | if(RegExp(r'^[a-z]+$').hasMatch(_phoneNumberCtrl.value.text)) { 308 | return false; 309 | } else { 310 | // invalid length 311 | if(_phoneNumberCtrl.value.text.length < 10) { 312 | return false; 313 | } 314 | } 315 | } 316 | return true; 317 | } 318 | 319 | _onPressedForgotPass() { 320 | // custom inputs error messages 321 | _validatePhoneNumber(_phoneNumberCtrl.value.text); 322 | // forms validate 323 | bool customValidation = _customValidateForm(); 324 | if(customValidation) { 325 | showDialogBox(context); 326 | _onVerifyUser().then((res) { 327 | final String message = res['message'] ?? 'Error occurred'; 328 | if (message == 'User found!') { 329 | _onSendVerificationCodeMessage().then((postRes) { 330 | final sendMessage = postRes['message']; 331 | if(sendMessage == 'Success!') { 332 | final randomPin = postRes['randomPin']; 333 | final phoneNumber = '92${_phoneNumberCtrl.value.text}'; 334 | SharedPreference().saveUserMobile(phoneNumber).then((_) { 335 | SharedPreference().saveRegistrationPin(randomPin).then((value) { 336 | Navigator.pop(context); 337 | Fluttertoast.showToast( 338 | msg: 'Check your text message on $phoneNumber', 339 | toastLength: Toast.LENGTH_LONG, 340 | ); 341 | Navigator.of(context).push( 342 | MaterialPageRoute( 343 | builder: (context) => const VerifyPinPage(), 344 | ), 345 | ); 346 | }); 347 | }); 348 | } else { 349 | Navigator.pop(context); 350 | Fluttertoast.showToast( 351 | msg: sendMessage, 352 | toastLength: Toast.LENGTH_SHORT, 353 | ); 354 | } 355 | }); 356 | } else { 357 | Navigator.pop(context); 358 | Fluttertoast.showToast( 359 | msg: message, 360 | toastLength: Toast.LENGTH_LONG, 361 | ); 362 | } 363 | }); 364 | } 365 | } 366 | 367 | Future> _onVerifyUser() async { 368 | // check internet connectivity 369 | final hasInternet = await _hasInternetConnection(); 370 | if(hasInternet) { 371 | final mobile = '92${_phoneNumberCtrl.value.text}'; 372 | final user = UserModel( 373 | userMobile: mobile, 374 | ); 375 | final res = await AuthService().onVerifyUser(user, true); 376 | return { 377 | 'message' : res, 378 | }; 379 | } 380 | return { 381 | 'message' : 'No internet connection', 382 | }; 383 | } 384 | 385 | Future> _onSendVerificationCodeMessage() async { 386 | // check internet connectivity 387 | final hasInternet = await _hasInternetConnection(); 388 | if(hasInternet) { 389 | final String randomPin = _getRandomPin().toString(); 390 | final String randomPinStr = 'Your%20PIN%20for%20HAMI%20App%20is%20$randomPin.'; 391 | final mobile = '92${_phoneNumberCtrl.value.text}'; 392 | String res = await AuthService().sendTextMessage(mobile, randomPinStr); 393 | return { 394 | 'message': res, 395 | 'randomPin': randomPin 396 | }; 397 | } 398 | return { 399 | 'message' : 'No internet connection', 400 | }; 401 | } 402 | 403 | // 404 | int _getRandomPin() { 405 | var rnd = math.Random(); 406 | return rnd.nextInt(1000) + 1000; 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /lib/screens/user/userMaterial/diet_chart_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:internet_connection_checker/internet_connection_checker.dart'; 7 | import 'package:photo_view/photo_view.dart'; 8 | 9 | import '../../../services/awareness_data_service.dart'; 10 | import '../../../widget/connectivity_banner.dart'; 11 | import '../awareness_material_page.dart'; 12 | 13 | const kColorBg = Colors.white; 14 | 15 | class DietChartPage extends StatefulWidget { 16 | final String mobile; 17 | const DietChartPage({required this.mobile, Key? key}) : super(key: key); 18 | 19 | @override 20 | State createState() => _DietChartPageState(); 21 | } 22 | 23 | class _DietChartPageState extends State { 24 | bool _isPageLoaded = false; 25 | bool _isGridview = true; 26 | List _imagesData = []; 27 | 28 | // has internet 29 | late StreamSubscription internetSubscription; 30 | 31 | @override 32 | void initState() { 33 | internetSubscription = 34 | InternetConnectionChecker().onStatusChange.listen((status) { 35 | final hasInternet = status == InternetConnectionStatus.connected; 36 | if (!hasInternet) { 37 | connectivityBanner(context, 'No internet connection.', 38 | () => ScaffoldMessenger.of(context).hideCurrentMaterialBanner()); 39 | } else { 40 | ScaffoldMessenger.of(context).hideCurrentMaterialBanner(); 41 | } 42 | }); 43 | // load data 44 | _initData(); 45 | super.initState(); 46 | } 47 | 48 | // check internet 49 | Future _hasInternetConnection() async { 50 | return await InternetConnectionChecker().hasConnection; 51 | } 52 | 53 | 54 | _initData() async { 55 | // check internet connectivity 56 | final hasInternet = await _hasInternetConnection(); 57 | if (hasInternet) { 58 | final data = await AwarenessDataService().loadData(widget.mobile); 59 | final message = data['message']; 60 | final materialData = data['data']; 61 | if (message == 'success') { 62 | final dietChartImages = 63 | materialData.where((e) => e['HAM_Type'] == 1).toList(); 64 | if (mounted) { 65 | setState(() { 66 | _isPageLoaded = true; 67 | _imagesData = dietChartImages; 68 | }); 69 | } 70 | } 71 | } else { 72 | setState(() { 73 | _isPageLoaded = true; 74 | }); 75 | } 76 | } 77 | 78 | @override 79 | void dispose() { 80 | internetSubscription.cancel(); 81 | // TODO: implement dispose 82 | super.dispose(); 83 | } 84 | 85 | @override 86 | Widget build(BuildContext context) { 87 | Size size = MediaQuery.of(context).size; 88 | return WillPopScope( 89 | onWillPop: () async { 90 | Navigator.pushReplacement( 91 | context, 92 | MaterialPageRoute( 93 | builder: (_) => AwarenessMaterialPage( 94 | mobile: widget.mobile, 95 | ), 96 | ), 97 | ); 98 | return Future.value(true); 99 | }, 100 | child: Scaffold( 101 | backgroundColor: kColorBg, 102 | appBar: PreferredSize( 103 | preferredSize: Size.zero, 104 | child: AppBar( 105 | elevation: 0, 106 | backgroundColor: kColorBg, //ios status bar colors 107 | systemOverlayStyle: const SystemUiOverlayStyle( 108 | statusBarColor: kColorBg, //android status bar color 109 | statusBarBrightness: Brightness.light, // For iOS: (dark icons) 110 | statusBarIconBrightness: 111 | Brightness.dark, // For Android: (dark icons) 112 | ), 113 | ), 114 | ), 115 | body: SizedBox( 116 | width: size.width, 117 | height: size.height, 118 | child: _isPageLoaded 119 | ? Column( 120 | children: [ 121 | Container( 122 | padding: const EdgeInsets.fromLTRB(18, 20, 18, 0), 123 | child: Row( 124 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 125 | children: [ 126 | InkWell( 127 | onTap: () { 128 | Navigator.pushReplacement( 129 | context, 130 | MaterialPageRoute( 131 | builder: (_) => AwarenessMaterialPage( 132 | mobile: widget.mobile, 133 | ), 134 | ), 135 | ); 136 | }, 137 | child: const Icon( 138 | Icons.arrow_back_ios, 139 | size: 18, 140 | ), 141 | ), 142 | Container( 143 | alignment: Alignment.center, 144 | child: const Text( 145 | 'Diet Chart', 146 | style: TextStyle( 147 | fontWeight: FontWeight.w500, 148 | fontSize: 18, 149 | ), 150 | ), 151 | ), 152 | InkWell( 153 | onTap: () { 154 | setState(() { 155 | _isGridview = !_isGridview; 156 | }); 157 | }, 158 | child: Icon( 159 | _isGridview ? Icons.grid_view : Icons.toc_outlined, 160 | ), 161 | ) 162 | ], 163 | ), 164 | ), 165 | const SizedBox( 166 | height: 15, 167 | ), 168 | _imagesData.isNotEmpty 169 | ? Container( 170 | width: size.width, 171 | padding: const EdgeInsets.only(left: 15, bottom: 10), 172 | child: Text( 173 | 'All images', 174 | style: TextStyle( 175 | fontSize: 15, 176 | color: Colors.black.withOpacity(0.8), 177 | fontWeight: FontWeight.w600, 178 | ), 179 | ), 180 | ) 181 | : Container(), 182 | Expanded( 183 | child: _imagesData.isNotEmpty 184 | ? _isGridview 185 | ? _buildGridView() 186 | : _buildListView() 187 | : const Center( 188 | child: Text('No data was found to show!'), 189 | ), 190 | ), 191 | ], 192 | ) 193 | : const Center( 194 | child: CircularProgressIndicator(), 195 | ), 196 | ), 197 | ), 198 | ); 199 | } 200 | 201 | // 202 | Widget _buildGridView() { 203 | Size size = MediaQuery.of(context).size; 204 | return ListView.builder( 205 | itemCount: (_imagesData.length / 4).ceil(), 206 | itemBuilder: (BuildContext context, int idx) { 207 | var a = idx * 4; 208 | var b = (idx * 4) + 1; 209 | var c = (idx * 4) + 2; 210 | var d = (idx * 4) + 3; 211 | return Container( 212 | margin: const EdgeInsets.only( 213 | left: 10, 214 | right: 10, 215 | bottom: 10, 216 | ), 217 | child: Row( 218 | children: [ 219 | Expanded( 220 | child: SizedBox( 221 | height: size.height * 0.12, 222 | child: _buildCachedImage(a), 223 | ), 224 | ), 225 | const SizedBox( 226 | width: 6, 227 | ), 228 | Expanded( 229 | child: SizedBox( 230 | height: size.height * 0.12, 231 | child: _buildCachedImage(b), 232 | ), 233 | ), 234 | const SizedBox( 235 | width: 6, 236 | ), 237 | Expanded( 238 | child: SizedBox( 239 | height: size.height * 0.12, 240 | child: _buildCachedImage(c), 241 | ), 242 | ), 243 | const SizedBox( 244 | width: 6, 245 | ), 246 | Expanded( 247 | child: SizedBox( 248 | height: size.height * 0.12, 249 | child: _buildCachedImage(d), 250 | ), 251 | ), 252 | ], 253 | ), 254 | ); 255 | }, 256 | ); 257 | } 258 | 259 | // 260 | _buildCachedImage(int index) { 261 | return (index <= (_imagesData.length - 1)) 262 | ? GestureDetector( 263 | onTap: () => Navigator.push( 264 | context, 265 | MaterialPageRoute( 266 | builder: (context) => ViewImage( 267 | url: _imagesData[index]['HAM_URL'], 268 | label: _imagesData[index]['HAM_Desc'], 269 | ), 270 | ), 271 | ), 272 | child: CachedNetworkImage( 273 | imageUrl: _imagesData[index]['HAM_URL'], 274 | imageBuilder: (context, imageProvider) => Container( 275 | decoration: BoxDecoration( 276 | borderRadius: BorderRadius.circular(7), 277 | border: Border.all( 278 | color: Colors.black12, 279 | ), 280 | image: DecorationImage( 281 | image: imageProvider, 282 | fit: BoxFit.cover, 283 | ), 284 | ), 285 | ), 286 | placeholder: (context, url) => Column( 287 | mainAxisAlignment: MainAxisAlignment.center, 288 | children: [ 289 | Container( 290 | height: 30.0, 291 | width: 30.0, 292 | decoration: const BoxDecoration( 293 | shape: BoxShape.circle, 294 | ), 295 | child: const CircularProgressIndicator( 296 | strokeWidth: 2, 297 | ), 298 | ), 299 | ], 300 | ), 301 | errorWidget: (context, url, error) => const Icon( 302 | Icons.error, 303 | ), 304 | ), 305 | ) 306 | : Container(); 307 | } 308 | 309 | // 310 | Widget _buildListView() { 311 | return ListView.builder( 312 | itemCount: _imagesData.length, 313 | itemBuilder: (BuildContext context, int idx) { 314 | return Container( 315 | margin: const EdgeInsets.only(bottom: 8, left: 5, right: 5), 316 | child: Material( 317 | color: Colors.white, 318 | elevation: 2, 319 | borderRadius: BorderRadius.circular(5), 320 | child: InkWell( 321 | borderRadius: BorderRadius.circular(5), 322 | onTap: () => Navigator.push( 323 | context, 324 | MaterialPageRoute( 325 | builder: (context) => ViewImage( 326 | url: _imagesData[idx]['HAM_URL'], 327 | label: _imagesData[idx]['HAM_Desc'], 328 | ), 329 | ), 330 | ), 331 | child: SizedBox( 332 | height: 90, 333 | child: Card( 334 | margin: EdgeInsets.zero, 335 | elevation: 0, 336 | shape: const RoundedRectangleBorder( 337 | borderRadius: BorderRadius.all( 338 | Radius.circular(15), 339 | ), 340 | ), 341 | child: Padding( 342 | padding: const EdgeInsets.symmetric(horizontal: 10), 343 | child: Row( 344 | children: [ 345 | CircleAvatar( 346 | radius: 35, 347 | backgroundColor: Colors.white, 348 | child: CachedNetworkImage( 349 | imageUrl: _imagesData[idx]['HAM_URL'], 350 | imageBuilder: (context, imageProvider) => Container( 351 | decoration: BoxDecoration( 352 | borderRadius: BorderRadius.circular(5), 353 | border: Border.all( 354 | color: Colors.black12, 355 | ), 356 | image: DecorationImage( 357 | image: imageProvider, 358 | fit: BoxFit.cover, 359 | ), 360 | ), 361 | ), 362 | placeholder: (context, url) => Column( 363 | mainAxisAlignment: MainAxisAlignment.center, 364 | children: [ 365 | Container( 366 | height: 20.0, 367 | width: 20.0, 368 | decoration: const BoxDecoration( 369 | shape: BoxShape.circle, 370 | ), 371 | child: const CircularProgressIndicator( 372 | strokeWidth: 2, 373 | ), 374 | ), 375 | ], 376 | ), 377 | errorWidget: (context, url, error) => const Icon( 378 | Icons.error, 379 | ), 380 | ), 381 | ), 382 | const SizedBox( 383 | width: 10, 384 | ), 385 | Expanded( 386 | child: Text( 387 | _imagesData[idx]['HAM_Desc'], 388 | style: const TextStyle( 389 | fontSize: 15, 390 | fontWeight: FontWeight.w500, 391 | ), 392 | ), 393 | ), 394 | ], 395 | ), 396 | ), 397 | ), 398 | ), 399 | ), 400 | ), 401 | ); 402 | }, 403 | ); 404 | } 405 | } 406 | 407 | class ViewImage extends StatelessWidget { 408 | final String url; 409 | final String label; 410 | const ViewImage({required this.url, required this.label, Key? key}) 411 | : super(key: key); 412 | 413 | @override 414 | Widget build(BuildContext context) { 415 | Size size = MediaQuery.of(context).size; 416 | return Scaffold( 417 | backgroundColor: Colors.black, 418 | appBar: PreferredSize( 419 | preferredSize: Size.zero, 420 | child: AppBar( 421 | elevation: 0, 422 | backgroundColor: Colors.black, //ios status bar colors 423 | systemOverlayStyle: const SystemUiOverlayStyle( 424 | statusBarColor: Colors.black, //android status bar color 425 | statusBarBrightness: Brightness.dark, // For iOS: (dark icons) 426 | statusBarIconBrightness: 427 | Brightness.light, // For Android: (dark icons) 428 | ), 429 | ), 430 | ), 431 | body: SizedBox( 432 | width: size.width, 433 | height: size.height, 434 | child: Stack( 435 | children: [ 436 | Center( 437 | child: PhotoView( 438 | imageProvider: CachedNetworkImageProvider(url), 439 | ), 440 | // child: CachedNetworkImage( 441 | // imageUrl: url, 442 | // ), 443 | ), 444 | Positioned( 445 | top: 20, 446 | left: 12, 447 | child: GestureDetector( 448 | onTap: () => Navigator.of(context).pop(), 449 | child: const Icon( 450 | Icons.arrow_back_ios, 451 | size: 20, 452 | color: Colors.white, 453 | ), 454 | ), 455 | ), 456 | Positioned( 457 | bottom: 30, 458 | left: 0, 459 | right: 0, 460 | child: Text( 461 | label, 462 | style: const TextStyle( 463 | fontSize: 15, 464 | fontWeight: FontWeight.w600, 465 | backgroundColor: Colors.black, 466 | color: Colors.white, 467 | ), 468 | textAlign: TextAlign.center, 469 | ), 470 | ), 471 | ], 472 | ), 473 | ), 474 | ); 475 | } 476 | } 477 | -------------------------------------------------------------------------------- /lib/screens/user/userMaterial/bp_ramazan_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:internet_connection_checker/internet_connection_checker.dart'; 7 | import 'package:photo_view/photo_view.dart'; 8 | 9 | import '../../../services/awareness_data_service.dart'; 10 | import '../../../widget/connectivity_banner.dart'; 11 | import '../awareness_material_page.dart'; 12 | 13 | const kColorBg = Colors.white; 14 | 15 | class BpRamazanPage extends StatefulWidget { 16 | final String mobile; 17 | const BpRamazanPage({required this.mobile, Key? key}) : super(key: key); 18 | 19 | @override 20 | State createState() => _BpRamazanState(); 21 | } 22 | 23 | class _BpRamazanState extends State { 24 | bool _isPageLoaded = false; 25 | bool _isGridview = true; 26 | List _imagesData = []; 27 | 28 | // has internet 29 | late StreamSubscription internetSubscription; 30 | 31 | @override 32 | void initState() { 33 | internetSubscription = 34 | InternetConnectionChecker().onStatusChange.listen((status) { 35 | final hasInternet = status == InternetConnectionStatus.connected; 36 | if (!hasInternet) { 37 | connectivityBanner(context, 'No internet connection.', 38 | () => ScaffoldMessenger.of(context).hideCurrentMaterialBanner()); 39 | } else { 40 | ScaffoldMessenger.of(context).hideCurrentMaterialBanner(); 41 | } 42 | }); 43 | // load data 44 | _initData(); 45 | super.initState(); 46 | } 47 | 48 | // check internet 49 | Future _hasInternetConnection() async { 50 | return await InternetConnectionChecker().hasConnection; 51 | } 52 | 53 | _initData() async { 54 | // check internet connectivity 55 | final hasInternet = await _hasInternetConnection(); 56 | if (hasInternet) { 57 | final data = await AwarenessDataService().loadData(widget.mobile); 58 | final message = data['message']; 59 | final materialData = data['data']; 60 | if (message == 'success') { 61 | final bpRamazanImages = 62 | materialData.where((e) => e['HAM_Type'] == 3).toList(); 63 | if (mounted) { 64 | setState(() { 65 | _isPageLoaded = true; 66 | _imagesData = bpRamazanImages; 67 | }); 68 | } 69 | } 70 | } else { 71 | setState(() { 72 | _isPageLoaded = true; 73 | }); 74 | } 75 | } 76 | 77 | @override 78 | void dispose() { 79 | internetSubscription.cancel(); 80 | // TODO: implement dispose 81 | super.dispose(); 82 | } 83 | 84 | 85 | @override 86 | Widget build(BuildContext context) { 87 | Size size = MediaQuery.of(context).size; 88 | return WillPopScope( 89 | onWillPop: () async { 90 | Navigator.pushReplacement( 91 | context, 92 | MaterialPageRoute( 93 | builder: (_) => AwarenessMaterialPage( 94 | mobile: widget.mobile, 95 | ), 96 | ), 97 | ); 98 | return Future.value(true); 99 | }, 100 | child: Scaffold( 101 | backgroundColor: kColorBg, 102 | appBar: PreferredSize( 103 | preferredSize: Size.zero, 104 | child: AppBar( 105 | elevation: 0, 106 | backgroundColor: kColorBg, //ios status bar colors 107 | systemOverlayStyle: const SystemUiOverlayStyle( 108 | statusBarColor: kColorBg, //android status bar color 109 | statusBarBrightness: Brightness.light, // For iOS: (dark icons) 110 | statusBarIconBrightness: 111 | Brightness.dark, // For Android: (dark icons) 112 | ), 113 | ), 114 | ), 115 | body: SizedBox( 116 | width: size.width, 117 | height: size.height, 118 | child: _isPageLoaded 119 | ? Column( 120 | children: [ 121 | Container( 122 | padding: const EdgeInsets.fromLTRB(18, 20, 18, 0), 123 | child: Row( 124 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 125 | children: [ 126 | InkWell( 127 | onTap: () { 128 | Navigator.pushReplacement( 129 | context, 130 | MaterialPageRoute( 131 | builder: (_) => AwarenessMaterialPage( 132 | mobile: widget.mobile, 133 | ), 134 | ), 135 | ); 136 | }, 137 | child: const Icon( 138 | Icons.arrow_back_ios, 139 | size: 18, 140 | ), 141 | ), 142 | Container( 143 | alignment: Alignment.center, 144 | child: const Text( 145 | 'Blood Pressure & Ramazan', 146 | style: TextStyle( 147 | fontWeight: FontWeight.w500, 148 | fontSize: 18, 149 | ), 150 | ), 151 | ), 152 | InkWell( 153 | onTap: () { 154 | setState(() { 155 | _isGridview = !_isGridview; 156 | }); 157 | }, 158 | child: Icon( 159 | _isGridview ? Icons.grid_view : Icons.toc_outlined, 160 | ), 161 | ) 162 | ], 163 | ), 164 | ), 165 | const SizedBox( 166 | height: 15, 167 | ), 168 | _imagesData.isNotEmpty 169 | ? Container( 170 | width: size.width, 171 | padding: const EdgeInsets.only(left: 15, bottom: 10), 172 | child: Text( 173 | 'All images', 174 | style: TextStyle( 175 | fontSize: 15, 176 | color: Colors.black.withOpacity(0.8), 177 | fontWeight: FontWeight.w600, 178 | ), 179 | ), 180 | ) 181 | : Container(), 182 | Expanded( 183 | child: _imagesData.isNotEmpty 184 | ? _isGridview 185 | ? _buildGridView() 186 | : _buildListView() 187 | : const Center( 188 | child: Text('No data was found to show!'), 189 | ), 190 | ), 191 | ], 192 | ) 193 | : const Center( 194 | child: CircularProgressIndicator(), 195 | ), 196 | ), 197 | ), 198 | ); 199 | } 200 | 201 | // 202 | Widget _buildGridView() { 203 | Size size = MediaQuery.of(context).size; 204 | return ListView.builder( 205 | itemCount: (_imagesData.length / 4).ceil(), 206 | itemBuilder: (BuildContext context, int idx) { 207 | var a = idx * 4; 208 | var b = (idx * 4) + 1; 209 | var c = (idx * 4) + 2; 210 | var d = (idx * 4) + 3; 211 | return Container( 212 | margin: const EdgeInsets.only( 213 | left: 10, 214 | right: 10, 215 | bottom: 10, 216 | ), 217 | child: Row( 218 | children: [ 219 | Expanded( 220 | child: SizedBox( 221 | height: size.height * 0.12, 222 | child: _buildCachedImage(a), 223 | ), 224 | ), 225 | const SizedBox( 226 | width: 6, 227 | ), 228 | Expanded( 229 | child: SizedBox( 230 | height: size.height * 0.12, 231 | child: _buildCachedImage(b), 232 | ), 233 | ), 234 | const SizedBox( 235 | width: 6, 236 | ), 237 | Expanded( 238 | child: SizedBox( 239 | height: size.height * 0.12, 240 | child: _buildCachedImage(c), 241 | ), 242 | ), 243 | const SizedBox( 244 | width: 6, 245 | ), 246 | Expanded( 247 | child: SizedBox( 248 | height: size.height * 0.12, 249 | child: _buildCachedImage(d), 250 | ), 251 | ), 252 | ], 253 | ), 254 | ); 255 | }, 256 | ); 257 | } 258 | 259 | // 260 | _buildCachedImage(int index) { 261 | return (index <= (_imagesData.length - 1)) 262 | ? GestureDetector( 263 | onTap: () => Navigator.push( 264 | context, 265 | MaterialPageRoute( 266 | builder: (context) => ViewImage( 267 | url: _imagesData[index]['HAM_URL'], 268 | label: _imagesData[index]['HAM_Desc'], 269 | ), 270 | ), 271 | ), 272 | child: CachedNetworkImage( 273 | imageUrl: _imagesData[index]['HAM_URL'], 274 | imageBuilder: (context, imageProvider) => Container( 275 | decoration: BoxDecoration( 276 | borderRadius: BorderRadius.circular(7), 277 | border: Border.all( 278 | color: Colors.black12, 279 | ), 280 | image: DecorationImage( 281 | image: imageProvider, 282 | fit: BoxFit.cover, 283 | ), 284 | ), 285 | ), 286 | placeholder: (context, url) => Column( 287 | mainAxisAlignment: MainAxisAlignment.center, 288 | children: [ 289 | Container( 290 | height: 30.0, 291 | width: 30.0, 292 | decoration: const BoxDecoration( 293 | shape: BoxShape.circle, 294 | ), 295 | child: const CircularProgressIndicator( 296 | strokeWidth: 2, 297 | ), 298 | ), 299 | ], 300 | ), 301 | errorWidget: (context, url, error) => const Icon( 302 | Icons.error, 303 | ), 304 | ), 305 | ) 306 | : Container(); 307 | } 308 | 309 | // 310 | Widget _buildListView() { 311 | return ListView.builder( 312 | itemCount: _imagesData.length, 313 | itemBuilder: (BuildContext context, int idx) { 314 | return Container( 315 | margin: const EdgeInsets.only(bottom: 8, left: 5, right: 5), 316 | child: Material( 317 | color: Colors.white, 318 | elevation: 2, 319 | borderRadius: BorderRadius.circular(5), 320 | child: InkWell( 321 | borderRadius: BorderRadius.circular(5), 322 | onTap: () => Navigator.push( 323 | context, 324 | MaterialPageRoute( 325 | builder: (context) => ViewImage( 326 | url: _imagesData[idx]['HAM_URL'], 327 | label: _imagesData[idx]['HAM_Desc'], 328 | ), 329 | ), 330 | ), 331 | child: SizedBox( 332 | height: 90, 333 | child: Card( 334 | margin: EdgeInsets.zero, 335 | elevation: 0, 336 | shape: const RoundedRectangleBorder( 337 | borderRadius: BorderRadius.all( 338 | Radius.circular(15), 339 | ), 340 | ), 341 | child: Padding( 342 | padding: const EdgeInsets.symmetric(horizontal: 10), 343 | child: Row( 344 | children: [ 345 | CircleAvatar( 346 | radius: 35, 347 | backgroundColor: Colors.white, 348 | child: CachedNetworkImage( 349 | imageUrl: _imagesData[idx]['HAM_URL'], 350 | imageBuilder: (context, imageProvider) => Container( 351 | decoration: BoxDecoration( 352 | borderRadius: BorderRadius.circular(5), 353 | border: Border.all( 354 | color: Colors.black12, 355 | ), 356 | image: DecorationImage( 357 | image: imageProvider, 358 | fit: BoxFit.cover, 359 | ), 360 | ), 361 | ), 362 | placeholder: (context, url) => Column( 363 | mainAxisAlignment: MainAxisAlignment.center, 364 | children: [ 365 | Container( 366 | height: 20.0, 367 | width: 20.0, 368 | decoration: const BoxDecoration( 369 | shape: BoxShape.circle, 370 | ), 371 | child: const CircularProgressIndicator( 372 | strokeWidth: 2, 373 | ), 374 | ), 375 | ], 376 | ), 377 | errorWidget: (context, url, error) => const Icon( 378 | Icons.error, 379 | ), 380 | ), 381 | ), 382 | const SizedBox( 383 | width: 10, 384 | ), 385 | Expanded( 386 | child: Text( 387 | _imagesData[idx]['HAM_Desc'], 388 | style: const TextStyle( 389 | fontSize: 15, 390 | fontWeight: FontWeight.w500, 391 | ), 392 | ), 393 | ), 394 | ], 395 | ), 396 | ), 397 | ), 398 | ), 399 | ), 400 | ), 401 | ); 402 | }, 403 | ); 404 | } 405 | } 406 | 407 | class ViewImage extends StatelessWidget { 408 | final String url; 409 | final String label; 410 | const ViewImage({required this.url, required this.label, Key? key}) 411 | : super(key: key); 412 | 413 | @override 414 | Widget build(BuildContext context) { 415 | Size size = MediaQuery.of(context).size; 416 | return Scaffold( 417 | backgroundColor: Colors.black, 418 | appBar: PreferredSize( 419 | preferredSize: Size.zero, 420 | child: AppBar( 421 | elevation: 0, 422 | backgroundColor: Colors.black, //ios status bar colors 423 | systemOverlayStyle: const SystemUiOverlayStyle( 424 | statusBarColor: Colors.black, //android status bar color 425 | statusBarBrightness: Brightness.dark, // For iOS: (dark icons) 426 | statusBarIconBrightness: 427 | Brightness.light, // For Android: (dark icons) 428 | ), 429 | ), 430 | ), 431 | body: SizedBox( 432 | width: size.width, 433 | height: size.height, 434 | child: Stack( 435 | children: [ 436 | Center( 437 | child: PhotoView( 438 | imageProvider: CachedNetworkImageProvider(url), 439 | ), 440 | // child: CachedNetworkImage( 441 | // imageUrl: url, 442 | // ), 443 | ), 444 | Positioned( 445 | top: 20, 446 | left: 12, 447 | child: GestureDetector( 448 | onTap: () => Navigator.of(context).pop(), 449 | child: const Icon( 450 | Icons.arrow_back_ios, 451 | size: 20, 452 | color: Colors.white, 453 | ), 454 | ), 455 | ), 456 | Positioned( 457 | bottom: 30, 458 | left: 0, 459 | right: 0, 460 | child: Text( 461 | label, 462 | style: const TextStyle( 463 | fontSize: 15, 464 | fontWeight: FontWeight.w600, 465 | backgroundColor: Colors.black, 466 | color: Colors.white, 467 | ), 468 | textAlign: TextAlign.center, 469 | ), 470 | ), 471 | ], 472 | ), 473 | ), 474 | ); 475 | } 476 | } 477 | -------------------------------------------------------------------------------- /lib/screens/user/userMaterial/bp_heart_disease_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:internet_connection_checker/internet_connection_checker.dart'; 7 | import 'package:photo_view/photo_view.dart'; 8 | 9 | import '../../../services/awareness_data_service.dart'; 10 | import '../../../widget/connectivity_banner.dart'; 11 | import '../awareness_material_page.dart'; 12 | 13 | const kColorBg = Colors.white; 14 | 15 | class BPHeartDiseasePage extends StatefulWidget { 16 | final String mobile; 17 | const BPHeartDiseasePage({required this.mobile, Key? key}) : super(key: key); 18 | 19 | @override 20 | State createState() => _BpRamazanState(); 21 | } 22 | 23 | class _BpRamazanState extends State { 24 | bool _isPageLoaded = false; 25 | bool _isGridview = true; 26 | List _imagesData = []; 27 | 28 | // has internet 29 | late StreamSubscription internetSubscription; 30 | 31 | @override 32 | void initState() { 33 | internetSubscription = 34 | InternetConnectionChecker().onStatusChange.listen((status) { 35 | final hasInternet = status == InternetConnectionStatus.connected; 36 | if (!hasInternet) { 37 | connectivityBanner(context, 'No internet connection.', 38 | () => ScaffoldMessenger.of(context).hideCurrentMaterialBanner()); 39 | } else { 40 | ScaffoldMessenger.of(context).hideCurrentMaterialBanner(); 41 | } 42 | }); 43 | // load data 44 | _initData(); 45 | super.initState(); 46 | } 47 | 48 | // check internet 49 | Future _hasInternetConnection() async { 50 | return await InternetConnectionChecker().hasConnection; 51 | } 52 | 53 | _initData() async { 54 | // check internet connectivity 55 | final hasInternet = await _hasInternetConnection(); 56 | if (hasInternet) { 57 | final data = await AwarenessDataService().loadData(widget.mobile); 58 | final message = data['message']; 59 | final materialData = data['data']; 60 | if (message == 'success') { 61 | final bpHeartDiseaseImages = 62 | materialData.where((e) => e['HAM_Type'] == 4).toList(); 63 | if (mounted) { 64 | setState(() { 65 | _isPageLoaded = true; 66 | _imagesData = bpHeartDiseaseImages; 67 | }); 68 | } 69 | } 70 | } else { 71 | setState(() { 72 | _isPageLoaded = true; 73 | }); 74 | } 75 | } 76 | 77 | @override 78 | void dispose() { 79 | internetSubscription.cancel(); 80 | // TODO: implement dispose 81 | super.dispose(); 82 | } 83 | 84 | @override 85 | Widget build(BuildContext context) { 86 | Size size = MediaQuery.of(context).size; 87 | return WillPopScope( 88 | onWillPop: () async { 89 | Navigator.pushReplacement( 90 | context, 91 | MaterialPageRoute( 92 | builder: (_) => AwarenessMaterialPage( 93 | mobile: widget.mobile, 94 | ), 95 | ), 96 | ); 97 | return Future.value(true); 98 | }, 99 | child: Scaffold( 100 | backgroundColor: kColorBg, 101 | appBar: PreferredSize( 102 | preferredSize: Size.zero, 103 | child: AppBar( 104 | elevation: 0, 105 | backgroundColor: kColorBg, //ios status bar colors 106 | systemOverlayStyle: const SystemUiOverlayStyle( 107 | statusBarColor: kColorBg, //android status bar color 108 | statusBarBrightness: Brightness.light, // For iOS: (dark icons) 109 | statusBarIconBrightness: 110 | Brightness.dark, // For Android: (dark icons) 111 | ), 112 | ), 113 | ), 114 | body: SizedBox( 115 | width: size.width, 116 | height: size.height, 117 | child: _isPageLoaded 118 | ? Column( 119 | children: [ 120 | Container( 121 | padding: const EdgeInsets.fromLTRB(18, 20, 18, 0), 122 | child: Row( 123 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 124 | children: [ 125 | InkWell( 126 | onTap: () { 127 | Navigator.pushReplacement( 128 | context, 129 | MaterialPageRoute( 130 | builder: (_) => AwarenessMaterialPage( 131 | mobile: widget.mobile, 132 | ), 133 | ), 134 | ); 135 | }, 136 | child: const Icon( 137 | Icons.arrow_back_ios, 138 | size: 18, 139 | ), 140 | ), 141 | Container( 142 | alignment: Alignment.center, 143 | child: const Text( 144 | 'Blood Pressure & Heart Disease', 145 | style: TextStyle( 146 | fontWeight: FontWeight.w500, 147 | fontSize: 18, 148 | ), 149 | ), 150 | ), 151 | InkWell( 152 | onTap: () { 153 | setState(() { 154 | _isGridview = !_isGridview; 155 | }); 156 | }, 157 | child: Icon( 158 | _isGridview ? Icons.grid_view : Icons.toc_outlined, 159 | ), 160 | ) 161 | ], 162 | ), 163 | ), 164 | const SizedBox( 165 | height: 15, 166 | ), 167 | _imagesData.isNotEmpty 168 | ? Container( 169 | width: size.width, 170 | padding: const EdgeInsets.only(left: 15, bottom: 10), 171 | child: Text( 172 | 'All images', 173 | style: TextStyle( 174 | fontSize: 15, 175 | color: Colors.black.withOpacity(0.8), 176 | fontWeight: FontWeight.w600, 177 | ), 178 | ), 179 | ) 180 | : Container(), 181 | Expanded( 182 | child: _imagesData.isNotEmpty 183 | ? _isGridview 184 | ? _buildGridView() 185 | : _buildListView() 186 | : const Center( 187 | child: Text('No data was found to show!'), 188 | ), 189 | ), 190 | ], 191 | ) 192 | : const Center( 193 | child: CircularProgressIndicator(), 194 | ), 195 | ), 196 | ), 197 | ); 198 | } 199 | 200 | // 201 | Widget _buildGridView() { 202 | Size size = MediaQuery.of(context).size; 203 | return ListView.builder( 204 | itemCount: (_imagesData.length / 4).ceil(), 205 | itemBuilder: (BuildContext context, int idx) { 206 | var a = idx * 4; 207 | var b = (idx * 4) + 1; 208 | var c = (idx * 4) + 2; 209 | var d = (idx * 4) + 3; 210 | return Container( 211 | margin: const EdgeInsets.only( 212 | left: 10, 213 | right: 10, 214 | bottom: 10, 215 | ), 216 | child: Row( 217 | children: [ 218 | Expanded( 219 | child: SizedBox( 220 | height: size.height * 0.12, 221 | child: _buildCachedImage(a), 222 | ), 223 | ), 224 | const SizedBox( 225 | width: 6, 226 | ), 227 | Expanded( 228 | child: SizedBox( 229 | height: size.height * 0.12, 230 | child: _buildCachedImage(b), 231 | ), 232 | ), 233 | const SizedBox( 234 | width: 6, 235 | ), 236 | Expanded( 237 | child: SizedBox( 238 | height: size.height * 0.12, 239 | child: _buildCachedImage(c), 240 | ), 241 | ), 242 | const SizedBox( 243 | width: 6, 244 | ), 245 | Expanded( 246 | child: SizedBox( 247 | height: size.height * 0.12, 248 | child: _buildCachedImage(d), 249 | ), 250 | ), 251 | ], 252 | ), 253 | ); 254 | }, 255 | ); 256 | } 257 | 258 | // 259 | _buildCachedImage(int index) { 260 | return (index <= (_imagesData.length - 1)) 261 | ? GestureDetector( 262 | onTap: () => Navigator.push( 263 | context, 264 | MaterialPageRoute( 265 | builder: (context) => ViewImage( 266 | url: _imagesData[index]['HAM_URL'], 267 | label: _imagesData[index]['HAM_Desc'], 268 | ), 269 | ), 270 | ), 271 | child: CachedNetworkImage( 272 | imageUrl: _imagesData[index]['HAM_URL'], 273 | imageBuilder: (context, imageProvider) => Container( 274 | decoration: BoxDecoration( 275 | borderRadius: BorderRadius.circular(7), 276 | border: Border.all( 277 | color: Colors.black12, 278 | ), 279 | image: DecorationImage( 280 | image: imageProvider, 281 | fit: BoxFit.cover, 282 | ), 283 | ), 284 | ), 285 | placeholder: (context, url) => Column( 286 | mainAxisAlignment: MainAxisAlignment.center, 287 | children: [ 288 | Container( 289 | height: 30.0, 290 | width: 30.0, 291 | decoration: const BoxDecoration( 292 | shape: BoxShape.circle, 293 | ), 294 | child: const CircularProgressIndicator( 295 | strokeWidth: 2, 296 | ), 297 | ), 298 | ], 299 | ), 300 | errorWidget: (context, url, error) => const Icon( 301 | Icons.error, 302 | ), 303 | ), 304 | ) 305 | : Container(); 306 | } 307 | 308 | // 309 | Widget _buildListView() { 310 | return ListView.builder( 311 | itemCount: _imagesData.length, 312 | itemBuilder: (BuildContext context, int idx) { 313 | return Container( 314 | margin: const EdgeInsets.only(bottom: 8, left: 5, right: 5), 315 | child: Material( 316 | color: Colors.white, 317 | elevation: 2, 318 | borderRadius: BorderRadius.circular(5), 319 | child: InkWell( 320 | borderRadius: BorderRadius.circular(5), 321 | onTap: () => Navigator.push( 322 | context, 323 | MaterialPageRoute( 324 | builder: (context) => ViewImage( 325 | url: _imagesData[idx]['HAM_URL'], 326 | label: _imagesData[idx]['HAM_Desc'], 327 | ), 328 | ), 329 | ), 330 | child: SizedBox( 331 | height: 90, 332 | child: Card( 333 | margin: EdgeInsets.zero, 334 | elevation: 0, 335 | shape: const RoundedRectangleBorder( 336 | borderRadius: BorderRadius.all( 337 | Radius.circular(15), 338 | ), 339 | ), 340 | child: Padding( 341 | padding: const EdgeInsets.symmetric(horizontal: 10), 342 | child: Row( 343 | children: [ 344 | CircleAvatar( 345 | radius: 35, 346 | backgroundColor: Colors.white, 347 | child: CachedNetworkImage( 348 | imageUrl: _imagesData[idx]['HAM_URL'], 349 | imageBuilder: (context, imageProvider) => Container( 350 | decoration: BoxDecoration( 351 | borderRadius: BorderRadius.circular(5), 352 | border: Border.all( 353 | color: Colors.black12, 354 | ), 355 | image: DecorationImage( 356 | image: imageProvider, 357 | fit: BoxFit.cover, 358 | ), 359 | ), 360 | ), 361 | placeholder: (context, url) => Column( 362 | mainAxisAlignment: MainAxisAlignment.center, 363 | children: [ 364 | Container( 365 | height: 20.0, 366 | width: 20.0, 367 | decoration: const BoxDecoration( 368 | shape: BoxShape.circle, 369 | ), 370 | child: const CircularProgressIndicator( 371 | strokeWidth: 2, 372 | ), 373 | ), 374 | ], 375 | ), 376 | errorWidget: (context, url, error) => const Icon( 377 | Icons.error, 378 | ), 379 | ), 380 | ), 381 | const SizedBox( 382 | width: 10, 383 | ), 384 | Expanded( 385 | child: Text( 386 | _imagesData[idx]['HAM_Desc'], 387 | style: const TextStyle( 388 | fontSize: 15, 389 | fontWeight: FontWeight.w500, 390 | ), 391 | ), 392 | ), 393 | ], 394 | ), 395 | ), 396 | ), 397 | ), 398 | ), 399 | ), 400 | ); 401 | }, 402 | ); 403 | } 404 | } 405 | 406 | class ViewImage extends StatelessWidget { 407 | final String url; 408 | final String label; 409 | const ViewImage({required this.url, required this.label, Key? key}) 410 | : super(key: key); 411 | 412 | @override 413 | Widget build(BuildContext context) { 414 | Size size = MediaQuery.of(context).size; 415 | return Scaffold( 416 | backgroundColor: Colors.black, 417 | appBar: PreferredSize( 418 | preferredSize: Size.zero, 419 | child: AppBar( 420 | elevation: 0, 421 | backgroundColor: Colors.black, //ios status bar colors 422 | systemOverlayStyle: const SystemUiOverlayStyle( 423 | statusBarColor: Colors.black, //android status bar color 424 | statusBarBrightness: Brightness.dark, // For iOS: (dark icons) 425 | statusBarIconBrightness: 426 | Brightness.light, // For Android: (dark icons) 427 | ), 428 | ), 429 | ), 430 | body: SizedBox( 431 | width: size.width, 432 | height: size.height, 433 | child: Stack( 434 | children: [ 435 | Center( 436 | child: PhotoView( 437 | imageProvider: CachedNetworkImageProvider(url), 438 | ), 439 | // child: CachedNetworkImage( 440 | // imageUrl: url, 441 | // ), 442 | ), 443 | Positioned( 444 | top: 20, 445 | left: 12, 446 | child: GestureDetector( 447 | onTap: () => Navigator.of(context).pop(), 448 | child: const Icon( 449 | Icons.arrow_back_ios, 450 | size: 20, 451 | color: Colors.white, 452 | ), 453 | ), 454 | ), 455 | Positioned( 456 | bottom: 30, 457 | left: 0, 458 | right: 0, 459 | child: Text( 460 | label, 461 | style: const TextStyle( 462 | fontSize: 15, 463 | fontWeight: FontWeight.w600, 464 | backgroundColor: Colors.black, 465 | color: Colors.white, 466 | ), 467 | textAlign: TextAlign.center, 468 | ), 469 | ), 470 | ], 471 | ), 472 | ), 473 | ); 474 | } 475 | } 476 | --------------------------------------------------------------------------------