diff --git a/.gitignore b/.gitignore
index 79c113f..3ada50c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,4 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
+/android/
diff --git a/assets/images/svg/cancel_circle.svg b/assets/images/svg/cancel_circle.svg
new file mode 100644
index 0000000..3ceda6f
--- /dev/null
+++ b/assets/images/svg/cancel_circle.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/svg/close_bottom_sheet_icon.svg b/assets/images/svg/close_bottom_sheet_icon.svg
new file mode 100644
index 0000000..599ce85
--- /dev/null
+++ b/assets/images/svg/close_bottom_sheet_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/insurance_history_icon.svg b/assets/images/svg/insurance_history_icon.svg
new file mode 100644
index 0000000..4ae2a1c
--- /dev/null
+++ b/assets/images/svg/insurance_history_icon.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/images/svg/update_insurance_card.svg b/assets/images/svg/update_insurance_card.svg
new file mode 100644
index 0000000..9b21aab
--- /dev/null
+++ b/assets/images/svg/update_insurance_card.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/json/countriesList.json b/assets/json/countriesList.json
new file mode 100644
index 0000000..25a65b3
--- /dev/null
+++ b/assets/json/countriesList.json
@@ -0,0 +1,1212 @@
+[
+ {
+ "ID": "AFG",
+ "Name": "Afghan",
+ "NameN": "أفغاني"
+ },
+ {
+ "ID": "CAF",
+ "Name": "African",
+ "NameN": "أفريقي"
+ },
+ {
+ "ID": "ALB",
+ "Name": "Albanian",
+ "NameN": "ألباني"
+ },
+ {
+ "ID": "DZA",
+ "Name": "Algerian",
+ "NameN": "جزائري"
+ },
+ {
+ "ID": "USA",
+ "Name": "American",
+ "NameN": "أمريكي"
+ },
+ {
+ "ID": "UMI",
+ "Name": "American (Minor Outlying Islands)",
+ "NameN": "أمريكي (الجزر الصغرى النائية)"
+ },
+ {
+ "ID": "ASM",
+ "Name": "American Samoa",
+ "NameN": "ساموا الأمريكية"
+ },
+ {
+ "ID": "AND",
+ "Name": "Andorrian",
+ "NameN": "أندوري"
+ },
+ {
+ "ID": "AGO",
+ "Name": "Angolan",
+ "NameN": "أنغولي"
+ },
+ {
+ "ID": "AIA",
+ "Name": "Anguillan",
+ "NameN": "أنغيلي"
+ },
+ {
+ "ID": "ATA",
+ "Name": "Antarctica",
+ "NameN": "أنتاركتيكا"
+ },
+ {
+ "ID": "ATG",
+ "Name": "Antiguans, Barbudans",
+ "NameN": "أنتيغوي، باربودي"
+ },
+ {
+ "ID": "ARG",
+ "Name": "Argentine",
+ "NameN": "أرجنتيني"
+ },
+ {
+ "ID": "ARM",
+ "Name": "Armenian",
+ "NameN": "أرمني"
+ },
+ {
+ "ID": "ABW",
+ "Name": "Arubian",
+ "NameN": "أروبي"
+ },
+ {
+ "ID": "AUS",
+ "Name": "Australian",
+ "NameN": "أسترالي"
+ },
+ {
+ "ID": "AUT",
+ "Name": "Austrian",
+ "NameN": "نمساوي"
+ },
+ {
+ "ID": "AZE",
+ "Name": "Azeri",
+ "NameN": "أذربيجاني"
+ },
+ {
+ "ID": "BHS",
+ "Name": "Bahamian",
+ "NameN": "باهامي"
+ },
+ {
+ "ID": "BHR",
+ "Name": "Bahraini",
+ "NameN": "بحريني"
+ },
+ {
+ "ID": "BRB",
+ "Name": "Barbadian or Bajuns",
+ "NameN": "باربادوسي"
+ },
+ {
+ "ID": "BLR",
+ "Name": "Belarusian",
+ "NameN": "بيلاروسي"
+ },
+ {
+ "ID": "BEL",
+ "Name": "Belgian",
+ "NameN": "بلجيكي"
+ },
+ {
+ "ID": "BLZ",
+ "Name": "Belizean",
+ "NameN": "بليزي"
+ },
+ {
+ "ID": "BGD",
+ "Name": "Bengali",
+ "NameN": "بنغلاديشي"
+ },
+ {
+ "ID": "BEN",
+ "Name": "Beninese",
+ "NameN": "بنيني"
+ },
+ {
+ "ID": "BMU",
+ "Name": "Bermudian",
+ "NameN": "برمودي"
+ },
+ {
+ "ID": "BTN",
+ "Name": "Bhutanese",
+ "NameN": "بوتاني"
+ },
+ {
+ "ID": "BOL",
+ "Name": "Bolivian",
+ "NameN": "بوليفي"
+ },
+ {
+ "ID": "BIH",
+ "Name": "Bosnian And Herzegovinian",
+ "NameN": "بوسني وهرسكي"
+ },
+ {
+ "ID": "BWA",
+ "Name": "Botswanian",
+ "NameN": "بوتسواني"
+ },
+ {
+ "ID": "BVT",
+ "Name": "Bouvet Island (Bouvetoya)",
+ "NameN": "جزيرة بوفيه"
+ },
+ {
+ "ID": "BRA",
+ "Name": "Brazilian",
+ "NameN": "برازيلي"
+ },
+ {
+ "ID": "GBR",
+ "Name": "British",
+ "NameN": "بريطاني"
+ },
+ {
+ "ID": "IOT",
+ "Name": "British (Chagos Archipelago)",
+ "NameN": "بريطاني (أرخبيل شاغوس)"
+ },
+ {
+ "ID": "VGB",
+ "Name": "British (Virgin Islands)",
+ "NameN": "بريطاني (الجزر العذراء)"
+ },
+ {
+ "ID": "BRN",
+ "Name": "Bruneian",
+ "NameN": "بروني"
+ },
+ {
+ "ID": "BGR",
+ "Name": "Bulgarian",
+ "NameN": "بلغاري"
+ },
+ {
+ "ID": "MMR",
+ "Name": "Burmese or Myanmarese",
+ "NameN": "بورمي أو ميانماري"
+ },
+ {
+ "ID": "BFA",
+ "Name": "Burnikabe",
+ "NameN": "بوركيني"
+ },
+ {
+ "ID": "BDI",
+ "Name": "Burundi",
+ "NameN": "بوروندي"
+ },
+ {
+ "ID": "KHM",
+ "Name": "Cambodian",
+ "NameN": "كمبودي"
+ },
+ {
+ "ID": "CMR",
+ "Name": "Cameroonian",
+ "NameN": "كاميروني"
+ },
+ {
+ "ID": "CAN",
+ "Name": "Canadian",
+ "NameN": "كندي"
+ },
+ {
+ "ID": "CPV",
+ "Name": "Cape Verdian or Cape Verdean",
+ "NameN": "رأس أخضري"
+ },
+ {
+ "ID": "CYM",
+ "Name": "Cayman Islander",
+ "NameN": "جزر كايماني"
+ },
+ {
+ "ID": "TCD",
+ "Name": "Chadian",
+ "NameN": "تشادي"
+ },
+ {
+ "ID": "CHL",
+ "Name": "Chilean",
+ "NameN": "تشيلي"
+ },
+ {
+ "ID": "HKG",
+ "Name": "Chinese (Hong Kong)",
+ "NameN": "صيني (هونغ كونغ)"
+ },
+ {
+ "ID": "MAC",
+ "Name": "Chinese (Macao)",
+ "NameN": "صيني (ماكاو)"
+ },
+ {
+ "ID": "CHN",
+ "Name": "Chinese",
+ "NameN": "صيني"
+ },
+ {
+ "ID": "CXR",
+ "Name": "Christmas Island",
+ "NameN": "جزيرة الكريسماس"
+ },
+ {
+ "ID": "CCK",
+ "Name": "Cocos (Keeling) Islands",
+ "NameN": "جزر كوكوس (كيلنغ)"
+ },
+ {
+ "ID": "COL",
+ "Name": "Colombian",
+ "NameN": "كولومبي"
+ },
+ {
+ "ID": "COM",
+ "Name": "Comoran",
+ "NameN": "قمري"
+ },
+ {
+ "ID": "COG",
+ "Name": "Congolese",
+ "NameN": "كونغولي"
+ },
+ {
+ "ID": "COD",
+ "Name": "Congolese (Zaire)",
+ "NameN": "كونغولي (زائير)"
+ },
+ {
+ "ID": "COK",
+ "Name": "Cook Islander",
+ "NameN": "جزر كوك"
+ },
+ {
+ "ID": "CRI",
+ "Name": "Costa Rican",
+ "NameN": "كوستاريكي"
+ },
+ {
+ "ID": "HRV",
+ "Name": "Croat",
+ "NameN": "كرواتي"
+ },
+ {
+ "ID": "CUB",
+ "Name": "Cuban",
+ "NameN": "كوبي"
+ },
+ {
+ "ID": "CYP",
+ "Name": "Cypriot, Greek",
+ "NameN": "قبرصي، يوناني"
+ },
+ {
+ "ID": "CZE",
+ "Name": "Czech",
+ "NameN": "تشيكي"
+ },
+ {
+ "ID": "DNK",
+ "Name": "Danish",
+ "NameN": "دنماركي"
+ },
+ {
+ "ID": "DJI",
+ "Name": "Djibouti",
+ "NameN": "جيبوتي"
+ },
+ {
+ "ID": "DOM",
+ "Name": "Dominican",
+ "NameN": "دومينيكاني"
+ },
+ {
+ "ID": "DMA",
+ "Name": "Dominican (Commonwealth)",
+ "NameN": "دومينيكي (الكومنولث)"
+ },
+ {
+ "ID": "ANT",
+ "Name": "Dutch (Antilles)",
+ "NameN": "هولندي (الأنتيل)"
+ },
+ {
+ "ID": "NLD",
+ "Name": "Dutch (Netherlands)",
+ "NameN": "هولندي (هولندا)"
+ },
+ {
+ "ID": "ECU",
+ "Name": "Ecuadorean",
+ "NameN": "إكوادوري"
+ },
+ {
+ "ID": "EGY",
+ "Name": "Egyptians",
+ "NameN": "مصري"
+ },
+ {
+ "ID": "ARE",
+ "Name": "Emirati",
+ "NameN": "إماراتي"
+ },
+ {
+ "ID": "GNQ",
+ "Name": "Equatorial Guinean or Equatoguinean",
+ "NameN": "غيني استوائي"
+ },
+ {
+ "ID": "ERI",
+ "Name": "Eritrean",
+ "NameN": "إريتري"
+ },
+ {
+ "ID": "EST",
+ "Name": "Estonian",
+ "NameN": "إستوني"
+ },
+ {
+ "ID": "ETH",
+ "Name": "Ethiopian",
+ "NameN": "إثيوبي"
+ },
+ {
+ "ID": "FRO",
+ "Name": "Faeroe Islander",
+ "NameN": "جزر فارو"
+ },
+ {
+ "ID": "FLK",
+ "Name": "Falkland Islander",
+ "NameN": "جزر فوكلاند"
+ },
+ {
+ "ID": "FJI",
+ "Name": "Fijian",
+ "NameN": "فيجي"
+ },
+ {
+ "ID": "PHL",
+ "Name": "Filipino",
+ "NameN": "فلبيني"
+ },
+ {
+ "ID": "FIN",
+ "Name": "Finnish",
+ "NameN": "فنلندي"
+ },
+ {
+ "ID": "FRA",
+ "Name": "French",
+ "NameN": "فرنسي"
+ },
+ {
+ "ID": "GUF",
+ "Name": "French Guianian",
+ "NameN": "غويانا الفرنسية"
+ },
+ {
+ "ID": "PYF",
+ "Name": "French Polynesian",
+ "NameN": "بولينيزيا الفرنسية"
+ },
+ {
+ "ID": "ATF",
+ "Name": "French Southern Territories",
+ "NameN": "الأقاليم الجنوبية الفرنسية"
+ },
+ {
+ "ID": "GAB",
+ "Name": "Gabonese",
+ "NameN": "غابوني"
+ },
+ {
+ "ID": "GMB",
+ "Name": "Gambian",
+ "NameN": "غامبي"
+ },
+ {
+ "ID": "GEO",
+ "Name": "Georgian",
+ "NameN": "جورجي"
+ },
+ {
+ "ID": "SGS",
+ "Name": "Georgian (South)",
+ "NameN": "جورجي (جنوبي)"
+ },
+ {
+ "ID": "DEU",
+ "Name": "German",
+ "NameN": "ألماني"
+ },
+ {
+ "ID": "GHA",
+ "Name": "Ghanaian",
+ "NameN": "غاني"
+ },
+ {
+ "ID": "GIB",
+ "Name": "Gibraltarian",
+ "NameN": "جبل طارقي"
+ },
+ {
+ "ID": "GRC",
+ "Name": "Greek",
+ "NameN": "يوناني"
+ },
+ {
+ "ID": "GRL",
+ "Name": "Greenlander",
+ "NameN": "غرينلاندي"
+ },
+ {
+ "ID": "GRD",
+ "Name": "Grenadian or Grenadan",
+ "NameN": "غرينادي"
+ },
+ {
+ "ID": "GLP",
+ "Name": "Guadeloupian",
+ "NameN": "غوادلوبي"
+ },
+ {
+ "ID": "GUM",
+ "Name": "Guamanian",
+ "NameN": "غوامي"
+ },
+ {
+ "ID": "GTM",
+ "Name": "Guatemalan",
+ "NameN": "غواتيمالي"
+ },
+ {
+ "ID": "GNB",
+ "Name": "Guinea-Bissauan",
+ "NameN": "غيني بيساوي"
+ },
+ {
+ "ID": "GIN",
+ "Name": "Guinean",
+ "NameN": "غيني"
+ },
+ {
+ "ID": "GUY",
+ "Name": "Guyanese",
+ "NameN": "غيانيزي"
+ },
+ {
+ "ID": "HTI",
+ "Name": "Haitian",
+ "NameN": "هايتيني"
+ },
+ {
+ "ID": "HMD",
+ "Name": "Heard and McDonald Islands",
+ "NameN": "جزر هيرد وماكدونالد"
+ },
+ {
+ "ID": "HND",
+ "Name": "Honduran",
+ "NameN": "هندوراسي"
+ },
+ {
+ "ID": "HUN",
+ "Name": "Hungarian",
+ "NameN": "مجري"
+ },
+ {
+ "ID": "ISL",
+ "Name": "Icelander",
+ "NameN": "آيسلندي"
+ },
+ {
+ "ID": "KIR",
+ "Name": "I-Kiribati",
+ "NameN": "كيريباتي"
+ },
+ {
+ "ID": "IND",
+ "Name": "Indian",
+ "NameN": "هندي"
+ },
+ {
+ "ID": "IDN",
+ "Name": "Indonesian",
+ "NameN": "إندونيسي"
+ },
+ {
+ "ID": "IRN",
+ "Name": "Iranian",
+ "NameN": "إيراني"
+ },
+ {
+ "ID": "IRQ",
+ "Name": "Iraqi",
+ "NameN": "عراقي"
+ },
+ {
+ "ID": "IRL",
+ "Name": "Irish",
+ "NameN": "إيرلندي"
+ },
+ {
+ "ID": "ITA",
+ "Name": "Italian",
+ "NameN": "إيطالي"
+ },
+ {
+ "ID": "CIV",
+ "Name": "Ivoirian",
+ "NameN": "إيفواري"
+ },
+ {
+ "ID": "JAM",
+ "Name": "Jamaican",
+ "NameN": "جامايكي"
+ },
+ {
+ "ID": "JPN",
+ "Name": "Japanese",
+ "NameN": "ياباني"
+ },
+ {
+ "ID": "JOR",
+ "Name": "Jordanian",
+ "NameN": "أردني"
+ },
+ {
+ "ID": "KAZ",
+ "Name": "Kazakhstani",
+ "NameN": "كازاخستاني"
+ },
+ {
+ "ID": "KEN",
+ "Name": "Kenyan",
+ "NameN": "كيني"
+ },
+ {
+ "ID": "KGZ",
+ "Name": "Kirghiz",
+ "NameN": "قيرغيزي"
+ },
+ {
+ "ID": "KOR",
+ "Name": "Korean",
+ "NameN": "كوري"
+ },
+ {
+ "ID": "PRK",
+ "Name": "Korean",
+ "NameN": "كوري"
+ },
+ {
+ "ID": "KWT",
+ "Name": "Kuwaiti",
+ "NameN": "كويتي"
+ },
+ {
+ "ID": "LAO",
+ "Name": "Laotian",
+ "NameN": "لاوسي"
+ },
+ {
+ "ID": "LVA",
+ "Name": "Latvian",
+ "NameN": "لاتفي"
+ },
+ {
+ "ID": "LBN",
+ "Name": "Lebanese",
+ "NameN": "لبناني"
+ },
+ {
+ "ID": "LBR",
+ "Name": "Liberian",
+ "NameN": "ليبيري"
+ },
+ {
+ "ID": "LBY",
+ "Name": "Libyan",
+ "NameN": "ليبي"
+ },
+ {
+ "ID": "LIE",
+ "Name": "Liechtensteiner",
+ "NameN": "ليختنشتايني"
+ },
+ {
+ "ID": "LTU",
+ "Name": "Lithuanian",
+ "NameN": "ليتواني"
+ },
+ {
+ "ID": "LUX",
+ "Name": "Luxembourger",
+ "NameN": "لوكسمبورغي"
+ },
+ {
+ "ID": "MKD",
+ "Name": "Macedonia, the former Yugoslav Republic of",
+ "NameN": "مقدوني، جمهورية يوغوسلافيا السابقة"
+ },
+ {
+ "ID": "MYT",
+ "Name": "Mahorais",
+ "NameN": "مايوتي"
+ },
+ {
+ "ID": "MDG",
+ "Name": "Malagasy",
+ "NameN": "مدغشقري"
+ },
+ {
+ "ID": "MWI",
+ "Name": "Malawian",
+ "NameN": "مالاوي"
+ },
+ {
+ "ID": "MYS",
+ "Name": "Malaysian",
+ "NameN": "ماليزي"
+ },
+ {
+ "ID": "MDV",
+ "Name": "Maldivan",
+ "NameN": "مالديفي"
+ },
+ {
+ "ID": "MLI",
+ "Name": "Malian",
+ "NameN": "مالي"
+ },
+ {
+ "ID": "MLT",
+ "Name": "Maltese",
+ "NameN": "مالطي"
+ },
+ {
+ "ID": "MHL",
+ "Name": "Marshallese",
+ "NameN": "مارشالي"
+ },
+ {
+ "ID": "MTQ",
+ "Name": "Martiniquais",
+ "NameN": "مارتينيكي"
+ },
+ {
+ "ID": "MRT",
+ "Name": "Mauritanian",
+ "NameN": "موريتاني"
+ },
+ {
+ "ID": "MUS",
+ "Name": "Mauritian",
+ "NameN": "موريشيوسي"
+ },
+ {
+ "ID": "MEX",
+ "Name": "Mexican",
+ "NameN": "مكسيكي"
+ },
+ {
+ "ID": "FSM",
+ "Name": "Micronesian",
+ "NameN": "ميكرونيزي"
+ },
+ {
+ "ID": "MDA",
+ "Name": "Moldovian",
+ "NameN": "مولدوفي"
+ },
+ {
+ "ID": "MCO",
+ "Name": "Monegasque or Monacan",
+ "NameN": "موناكي"
+ },
+ {
+ "ID": "MNG",
+ "Name": "Mongolian",
+ "NameN": "منغولي"
+ },
+ {
+ "ID": "MNE",
+ "Name": "Montenegrin",
+ "NameN": "جبل أسودي"
+ },
+ {
+ "ID": "MSR",
+ "Name": "Montserratian",
+ "NameN": "مونتسيراتي"
+ },
+ {
+ "ID": "MAR",
+ "Name": "Moroccan",
+ "NameN": "مغربي"
+ },
+ {
+ "ID": "LSO",
+ "Name": "Mosotho",
+ "NameN": "ليسوتي"
+ },
+ {
+ "ID": "MOZ",
+ "Name": "Mozambican",
+ "NameN": "موزمبيقي"
+ },
+ {
+ "ID": "NAM",
+ "Name": "Namibian",
+ "NameN": "ناميبي"
+ },
+ {
+ "ID": "NRU",
+ "Name": "Nauruan",
+ "NameN": "ناوروي"
+ },
+ {
+ "ID": "NPL",
+ "Name": "Nepalese",
+ "NameN": "نيبالي"
+ },
+ {
+ "ID": "NCL",
+ "Name": "New Caledonian",
+ "NameN": "كاليدوني جديد"
+ },
+ {
+ "ID": "NZL",
+ "Name": "New Zealand",
+ "NameN": "نيوزيلندي"
+ },
+ {
+ "ID": "VUT",
+ "Name": "Ni- Vanuatu",
+ "NameN": "فانواتي"
+ },
+ {
+ "ID": "NIC",
+ "Name": "Nicaraguan",
+ "NameN": "نيكاراغوي"
+ },
+ {
+ "ID": "NGA",
+ "Name": "Nigerian",
+ "NameN": "نيجيري"
+ },
+ {
+ "ID": "NER",
+ "Name": "Nigerien",
+ "NameN": "نيجري"
+ },
+ {
+ "ID": "NIU",
+ "Name": "Niuean",
+ "NameN": "نيوي"
+ },
+ {
+ "ID": "VAT",
+ "Name": "None (Vatican City)",
+ "NameN": "لا شيء (مدينة الفاتيكان)"
+ },
+ {
+ "ID": "NFK",
+ "Name": "Norfolk Islander",
+ "NameN": "جزيرة نورفولك"
+ },
+ {
+ "ID": "MNP",
+ "Name": "Northern Mariana Islander",
+ "NameN": "جزر ماريانا الشمالية"
+ },
+ {
+ "ID": "NOR",
+ "Name": "Norwegean",
+ "NameN": "نرويجي"
+ },
+ {
+ "ID": "OMN",
+ "Name": "Omani",
+ "NameN": "عماني"
+ },
+ {
+ "ID": "OTH",
+ "Name": "OTHERS",
+ "NameN": "آخرون"
+ },
+ {
+ "ID": "PAK",
+ "Name": "Pakistani",
+ "NameN": "باكستاني"
+ },
+ {
+ "ID": "PLW",
+ "Name": "Palauan",
+ "NameN": "بالاوي"
+ },
+ {
+ "ID": "PSE",
+ "Name": "Palestinian",
+ "NameN": "فلسطيني"
+ },
+ {
+ "ID": "PAN",
+ "Name": "Panamanian",
+ "NameN": "بنمي"
+ },
+ {
+ "ID": "PNG",
+ "Name": "Papua New Guinean",
+ "NameN": "بابوا غيني جديد"
+ },
+ {
+ "ID": "PRY",
+ "Name": "Paraguayan",
+ "NameN": "باراغوايي"
+ },
+ {
+ "ID": "PER",
+ "Name": "Peruvian",
+ "NameN": "بيروفي"
+ },
+ {
+ "ID": "PCN",
+ "Name": "Pitcairn Islander",
+ "NameN": "جزر بيتكايرن"
+ },
+ {
+ "ID": "POL",
+ "Name": "Polish",
+ "NameN": "بولندي"
+ },
+ {
+ "ID": "PRT",
+ "Name": "Portuguese",
+ "NameN": "برتغالي"
+ },
+ {
+ "ID": "PRI",
+ "Name": "Puerto Rican",
+ "NameN": "بورتوريكي"
+ },
+ {
+ "ID": "QAT",
+ "Name": "Qatari",
+ "NameN": "قطري"
+ },
+ {
+ "ID": "RKS",
+ "Name": "Republic of Kosovo",
+ "NameN": "جمهورية كوسوفو"
+ },
+ {
+ "ID": "REU",
+ "Name": "Reunionese",
+ "NameN": "ريونيوني"
+ },
+ {
+ "ID": "ROU",
+ "Name": "Romanian",
+ "NameN": "روماني"
+ },
+ {
+ "ID": "RUS",
+ "Name": "Russian",
+ "NameN": "روسي"
+ },
+ {
+ "ID": "RWA",
+ "Name": "Rwandan",
+ "NameN": "رواندي"
+ },
+ {
+ "ID": "ESH",
+ "Name": "Sahrawi",
+ "NameN": "صحراوي"
+ },
+ {
+ "ID": "SLV",
+ "Name": "Salvadoran",
+ "NameN": "سلفادوري"
+ },
+ {
+ "ID": "SMR",
+ "Name": "Sammarinese",
+ "NameN": "سان مارينو"
+ },
+ {
+ "ID": "WSM",
+ "Name": "Samoan",
+ "NameN": "ساموي"
+ },
+ {
+ "ID": "STP",
+ "Name": "Sao Tomean",
+ "NameN": "ساو تومي"
+ },
+ {
+ "ID": "SAU",
+ "Name": "Saudi",
+ "NameN": "سعودي"
+ },
+ {
+ "ID": "SCT",
+ "Name": "Scottish",
+ "NameN": "اسكتلندي"
+ },
+ {
+ "ID": "SEN",
+ "Name": "Senegalese",
+ "NameN": "سنغالي"
+ },
+ {
+ "ID": "SRB",
+ "Name": "Serbian",
+ "NameN": "صربي"
+ },
+ {
+ "ID": "SYC",
+ "Name": "Seychellois",
+ "NameN": "سيشيلي"
+ },
+ {
+ "ID": "SLE",
+ "Name": "Sierra Leonean",
+ "NameN": "سيراليوني"
+ },
+ {
+ "ID": "SGP",
+ "Name": "Singaporian",
+ "NameN": "سنغافوري"
+ },
+ {
+ "ID": "SVK",
+ "Name": "Slovak",
+ "NameN": "سلوفاكي"
+ },
+ {
+ "ID": "SVN",
+ "Name": "Slovene",
+ "NameN": "سلوفيني"
+ },
+ {
+ "ID": "SLB",
+ "Name": "Solomon Islander",
+ "NameN": "جزر سليمان"
+ },
+ {
+ "ID": "SOM",
+ "Name": "Somalian",
+ "NameN": "صومالي"
+ },
+ {
+ "ID": "ZAF",
+ "Name": "South African",
+ "NameN": "جنوب أفريقي"
+ },
+ {
+ "ID": "ESP",
+ "Name": "Spanish",
+ "NameN": "إسباني"
+ },
+ {
+ "ID": "LKA",
+ "Name": "Sri Lankan",
+ "NameN": "سريلانكي"
+ },
+ {
+ "ID": "SHN",
+ "Name": "St. Helena",
+ "NameN": "سانت هيلينا"
+ },
+ {
+ "ID": "KNA",
+ "Name": "St. Kitts and Nevis",
+ "NameN": "سانت كيتس ونيفيس"
+ },
+ {
+ "ID": "LCA",
+ "Name": "St. Lucia",
+ "NameN": "سانت لوسيا"
+ },
+ {
+ "ID": "SPM",
+ "Name": "St. Pierre and Miquelon",
+ "NameN": "سانت بيير وميكيلون"
+ },
+ {
+ "ID": "VCT",
+ "Name": "St. Vincent and the Grenadines",
+ "NameN": "سانت فنسنت والغرينادين"
+ },
+ {
+ "ID": "SDN",
+ "Name": "Sudanese",
+ "NameN": "سوداني"
+ },
+ {
+ "ID": "SUR",
+ "Name": "Surinamer",
+ "NameN": "سورينامي"
+ },
+ {
+ "ID": "SJM",
+ "Name": "Svalbard and Jan Mayen islander",
+ "NameN": "سفالبارد وجان ماين"
+ },
+ {
+ "ID": "SWZ",
+ "Name": "Swazi",
+ "NameN": "سوازي"
+ },
+ {
+ "ID": "SWE",
+ "Name": "Swedish",
+ "NameN": "سويدي"
+ },
+ {
+ "ID": "CHE",
+ "Name": "Swiss",
+ "NameN": "سويسري"
+ },
+ {
+ "ID": "SYR",
+ "Name": "Syrian",
+ "NameN": "سوري"
+ },
+ {
+ "ID": "TWN",
+ "Name": "Taiwan, Province of China",
+ "NameN": "تايوان، مقاطعة الصين"
+ },
+ {
+ "ID": "TJK",
+ "Name": "Tajik",
+ "NameN": "طاجيكي"
+ },
+ {
+ "ID": "TZA",
+ "Name": "Tanzania, United Republic o",
+ "NameN": "تنزاني، الجمهورية المتحدة"
+ },
+ {
+ "ID": "THA",
+ "Name": "Thai",
+ "NameN": "تايلندي"
+ },
+ {
+ "ID": "TLS",
+ "Name": "Timorese",
+ "NameN": "تيموري"
+ },
+ {
+ "ID": "TGO",
+ "Name": "Togolese",
+ "NameN": "توغولي"
+ },
+ {
+ "ID": "TKL",
+ "Name": "Tokelauan",
+ "NameN": "توكيلاوي"
+ },
+ {
+ "ID": "TON",
+ "Name": "Tongan",
+ "NameN": "تونغي"
+ },
+ {
+ "ID": "TTO",
+ "Name": "Trinidadian",
+ "NameN": "ترينيدادي"
+ },
+ {
+ "ID": "TUN",
+ "Name": "Tunisian",
+ "NameN": "تونسي"
+ },
+ {
+ "ID": "TCA",
+ "Name": "Turk",
+ "NameN": "تركي"
+ },
+ {
+ "ID": "TUR",
+ "Name": "Turkish",
+ "NameN": "تركي"
+ },
+ {
+ "ID": "TKM",
+ "Name": "Turkmen",
+ "NameN": "تركماني"
+ },
+ {
+ "ID": "TUV",
+ "Name": "Tuvaluan",
+ "NameN": "توفالوي"
+ },
+ {
+ "ID": "UGA",
+ "Name": "Ugandan",
+ "NameN": "أوغندي"
+ },
+ {
+ "ID": "UKR",
+ "Name": "Ukrainian",
+ "NameN": "أوكراني"
+ },
+ {
+ "ID": "URY",
+ "Name": "Uruguayan",
+ "NameN": "أوروغوايي"
+ },
+ {
+ "ID": "UZB",
+ "Name": "Uzbek",
+ "NameN": "أوزبكي"
+ },
+ {
+ "ID": "VEN",
+ "Name": "Venenzuelan",
+ "NameN": "فنزويلي"
+ },
+ {
+ "ID": "VNM",
+ "Name": "Vietnamese",
+ "NameN": "فيتنامي"
+ },
+ {
+ "ID": "VIR",
+ "Name": "Virgin Islander",
+ "NameN": "جزر عذراء"
+ },
+ {
+ "ID": "WLF",
+ "Name": "Wallis and Futuna Islander",
+ "NameN": "واليس وفوتونا"
+ },
+ {
+ "ID": "YEM",
+ "Name": "Yemeni",
+ "NameN": "يمني"
+ },
+ {
+ "ID": "ZMB",
+ "Name": "Zambian",
+ "NameN": "زامبي"
+ },
+ {
+ "ID": "ZWE",
+ "Name": "Zimbabwean",
+ "NameN": "زيمبابوي"
+ }
+]
\ No newline at end of file
diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json
index 91d5d4f..ede5e64 100644
--- a/assets/langs/ar-SA.json
+++ b/assets/langs/ar-SA.json
@@ -782,8 +782,29 @@
"resultsPending": "النتائج معلقة",
"resultsAvailable": "النتائج متاحة",
"viewReport": "عرض التقرير",
- "prescriptionDeliveryError": "هذه العيادة لا تدعم إعادة التعبئة والتسليم.",
-
"checkAvailability": "التحقق من التوفر",
- "readInstructions": "قراءة التعليمات"
+ "readInstructions": "قراءة التعليمات",
+ "searchLabReport" : "ابحث عن تقرير المختبر",
+ "prescriptionDeliveryError": "هذه العيادة لا تدعم إعادة التعبئة والتسليم.",
+ "receiveOtpToast": "أين تود تلقي رمز التحقق OTP؟",
+ "enterPhoneNumber": "أدخل رقم الهاتف",
+ "enterEmailDesc": "أدخل عنوان بريدك الإلكتروني لإكمال عملية إنشاء ملف طبي",
+ "enterPhoneDesc": "أدخل رقم هاتفك لتلقي رمز التحقق ",
+ "pleaseChooseOption": "الرجاء اختيار من الخيارات أدناه لتلقي رمز التحقق OTP",
+ "prepareToElevate": "هل أنت مستعد لتحسين صحتك ورفاهتك؟",
+ "iAcceptTermsConditions": "أوافق على الشروط والأحكام",
+ "alreadyHaveAccount": "هل لديك حساب بالفعل؟",
+ "loginNow": "تسجيل الدخول الآن",
+ "notice": "إشعار",
+ "oR": "أو",
+ "sendOTPWHATSAPP": "أرسل لي OTP عبر واتساب",
+ "sendOTPSMS": "أرسل لي OTP عبر الرسائل القصيرة",
+ "fullName": "الاسم الكامل",
+ "married": "متزوج",
+ "uae": "الإمارات العربية المتحدة",
+ "malE": "ذكر",
+ "loginBy": "تسجيل الدخول بواسطة",
+ "loginByOTP": "تسجيل الدخول بواسطة OTP",
+ "guest": "زائر",
+ "switchAccount": "تبديل الحساب"
}
\ No newline at end of file
diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json
index f91d98b..8ccd2c4 100644
--- a/assets/langs/en-US.json
+++ b/assets/langs/en-US.json
@@ -772,13 +772,35 @@
"aboutApp": "About the app",
"aboutPoints": "Online Appointment Booking & rescheduling, Insurance approval status, Find A doctor, Ask your doctor, Medical prescriptions, Lab results, Hospitals contact numbers, Doctor profiles, Hospitals locations, Pharmacies Locations, Hospital's Virtual Tour, Official Social Media, Vaccines Schedule, Health Calculators, Other Services",
"termsConditions": "These Online Services Terms of Use (Service Terms) govern certain online services provided by Dr Sulaiman Al Habib Medical Services Group Company (HMG, we, us, our)...",
+ "receiveOtpToast": "Where would you like to receive OTP?",
+ "enterPhoneNumber": "Enter Phone Number",
+ "enterEmailDesc": "Enter your email address to complete the process of creating a medical file",
+ "enterPhoneDesc": "Enter your phone number to receive OTP verification code",
+ "pleaseChooseOption": "Please select from the below options to receive OTP",
"dontHaveAccount": "Don't have an account?",
"loginOrRegister": "Login or Register",
"myFiles": "My Files",
"resultsPending": "Results Pending",
"resultsAvailable": "Results Available",
"viewReport": "View Report",
- "prescriptionDeliveryError": "This clinic doesn't support refill",
"checkAvailability": "Check Availability",
- "readInstructions": "Read Instructions"
+ "readInstructions": "Read Instructions",
+ "searchLabReport" : "Search Lab Report",
+ "prescriptionDeliveryError": "This clinic doesn't support refill",
+ "prepareToElevate": "Prepared to elevate your health and well-being?",
+ "iAcceptTermsConditions": "I Accept the Terms and Conditions",
+ "alreadyHaveAccount": "Already have an account?",
+ "loginNow": "Login Now",
+ "notice": "Notice",
+ "oR": "OR",
+ "sendOTPWHATSAPP": "Send me OTP on Whatsapp",
+ "sendOTPSMS": "Send me OTP on SMS",
+ "fullName": "Full Name",
+ "married": "Married",
+ "uae" : "United Arab Emirates",
+ "malE": "Male",
+ "loginBy": "Login By",
+ "loginByOTP": "Login By OTP",
+ "guest": "Guest",
+ "switchAccount": "Switch Account"
}
\ No newline at end of file
diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart
index b071112..ac3cfe6 100644
--- a/lib/core/app_assets.dart
+++ b/lib/core/app_assets.dart
@@ -68,17 +68,21 @@ class AppAssets {
static const String doctor_calendar_icon = '$svgBasePath/doctor_calendar_icon.svg';
static const String prescription_remarks_icon = '$svgBasePath/prescription_remarks_icon.svg';
static const String prescription_reminder_icon = '$svgBasePath/prescription_reminder_icon.svg';
+ static const String insurance_history_icon = '$svgBasePath/insurance_history_icon.svg';
+ static const String cancel_circle_icon = '$svgBasePath/cancel_circle.svg';
+ static const String update_insurance_card_icon = '$svgBasePath/update_insurance_card.svg';
+ static const String close_bottom_sheet_icon = '$svgBasePath/close_bottom_sheet_icon.svg';
static const String insurance = '$svgBasePath/insurance.svg';
static const String requests = '$svgBasePath/requests.svg';
static const String more = '$svgBasePath/more.svg';
//bottom navigation//
-
static const String homeBottom = '$svgBasePath/home_bottom.svg';
static const String bookAppoBottom = '$svgBasePath/book_appo_bottom.svg';
static const String myFilesBottom = '$svgBasePath/my_files_bottom.svg';
static const String toDoBottom = '$svgBasePath/todo_bottom.svg';
static const String servicesBottom = '$svgBasePath/services_bottom.svg';
+ static const String closeBottomNav = '$svgBasePath/close_bottom_nav.svg';
// PNGS //
static const String hmg_logo = '$pngBasePath/hmg_logo.png';
diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart
index 7c42042..eb9315c 100644
--- a/lib/core/app_state.dart
+++ b/lib/core/app_state.dart
@@ -22,13 +22,8 @@ class AppState {
set setUserLong(v) => userLong = v;
- final PostParamsModel _postParamsInitConfig = PostParamsModel(
- channel: 3,
- versionID: ApiConsts.VERSION_ID,
- ipAddress: '10.20.10.20',
- generalId: 'Cs2020@2016\$2958',
- deviceTypeID: "2",
- sessionID: 'TMRhVmkGhOsvamErw');
+ final PostParamsModel _postParamsInitConfig =
+ PostParamsModel(channel: 3, versionID: ApiConsts.VERSION_ID, ipAddress: '10.20.10.20', generalId: 'Cs2020@2016\$2958', deviceTypeID: "2", sessionID: 'TMRhVmkGhOsvamErw');
void setPostParamsInitConfig() {
isAuthenticated = false;
@@ -50,7 +45,9 @@ class AppState {
bool isArabic() => EasyLocalization.of(navigationService.navigatorKey.currentContext!)?.locale.languageCode == "ar";
- int getLanguageID(context) => EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2;
+ int getLanguageID() => EasyLocalization.of(navigationService.navigatorKey.currentContext!)?.locale.languageCode == "ar" ? 1 : 2;
+
+ String? getLanguageCode() => EasyLocalization.of(navigationService.navigatorKey.currentContext!)?.locale.languageCode;
AuthenticatedUser? _authenticatedUser;
diff --git a/lib/core/common_models/nationality_country_model.dart b/lib/core/common_models/nationality_country_model.dart
new file mode 100644
index 0000000..fb3961b
--- /dev/null
+++ b/lib/core/common_models/nationality_country_model.dart
@@ -0,0 +1,29 @@
+import 'dart:convert';
+
+class NationalityCountries {
+ String? id;
+ String? name;
+ String? nameN;
+
+ NationalityCountries({
+ this.id,
+ this.name,
+ this.nameN,
+ });
+
+ factory NationalityCountries.fromRawJson(String str) => NationalityCountries.fromJson(json.decode(str));
+
+ String toRawJson() => json.encode(toJson());
+
+ factory NationalityCountries.fromJson(Map json) => NationalityCountries(
+ id: json["ID"],
+ name: json["Name"],
+ nameN: json["NameN"],
+ );
+
+ Map toJson() => {
+ "ID": id,
+ "Name": name,
+ "NameN": nameN,
+ };
+}
diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart
index 65fd2c6..aa32fa6 100644
--- a/lib/core/dependencies.dart
+++ b/lib/core/dependencies.dart
@@ -6,6 +6,8 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_repo.
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_repo.dart';
import 'package:hmg_patient_app_new/features/common/common_repo.dart';
+import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart';
+import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/lab_repo.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart';
@@ -65,6 +67,7 @@ class AppDependencies {
getIt.registerLazySingleton(() => LabRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => RadiologyRepoImp(loggerService: getIt(), apiClient: getIt()));
getIt.registerLazySingleton(() => PrescriptionsRepoImp(loggerService: getIt(), apiClient: getIt()));
+ getIt.registerLazySingleton(() => InsuranceRepoImp(loggerService: getIt(), apiClient: getIt()));
// ViewModels
// Global/shared VMs → LazySingleton
@@ -90,6 +93,13 @@ class AppDependencies {
),
);
+ getIt.registerLazySingleton(
+ () => InsuranceViewModel(
+ insuranceRepo: getIt(),
+ errorHandlerService: getIt(),
+ ),
+ );
+
getIt.registerLazySingleton(
() => AuthenticationViewModel(
authenticationRepo: getIt(),
diff --git a/lib/core/enums.dart b/lib/core/enums.dart
index 2fd5867..34aa911 100644
--- a/lib/core/enums.dart
+++ b/lib/core/enums.dart
@@ -22,12 +22,6 @@ enum ViewStateEnum {
errorLocal,
}
-enum LoginTypeEnum {
- fromLogin,
- silentLogin,
- silentWithOTP,
-}
-
enum OTPTypeEnum { sms, whatsapp }
enum CountryEnum { saudiArabia, unitedArabEmirates }
@@ -40,6 +34,51 @@ enum MaritalStatusTypeEnum { single, married, divorced, widowed }
enum ChipTypeEnum { success, error, alert, info, warning }
+enum LoginTypeEnum { sms, whatsapp, face, fingerprint }
+
+extension LoginTypeExtension on LoginTypeEnum {
+ int get toInt {
+ switch (this) {
+ case LoginTypeEnum.sms:
+ return 1;
+ case LoginTypeEnum.whatsapp:
+ return 4;
+ case LoginTypeEnum.face:
+ return 3;
+ case LoginTypeEnum.fingerprint:
+ return 2;
+ }
+ }
+
+ String get displayName {
+ switch (this) {
+ case LoginTypeEnum.sms:
+ return 'SMS';
+ case LoginTypeEnum.whatsapp:
+ return 'WhatsApp';
+ case LoginTypeEnum.face:
+ return 'Face Recognition';
+ case LoginTypeEnum.fingerprint:
+ return 'Fingerprint';
+ }
+ }
+
+ static LoginTypeEnum? fromValue(int value) {
+ switch (value) {
+ case 1:
+ return LoginTypeEnum.sms;
+ case 2:
+ return LoginTypeEnum.fingerprint;
+ case 3:
+ return LoginTypeEnum.face;
+ case 4:
+ return LoginTypeEnum.whatsapp;
+ default:
+ return null;
+ }
+ }
+}
+
extension OTPTypeEnumExtension on OTPTypeEnum {
/// Convert enum to int
int toInt() {
diff --git a/lib/core/utils/size_utils.dart b/lib/core/utils/size_utils.dart
index 0b37082..6ad1835 100644
--- a/lib/core/utils/size_utils.dart
+++ b/lib/core/utils/size_utils.dart
@@ -11,6 +11,10 @@ extension ResponsiveExtension on num {
double get h => ((this * _width) / FIGMA_DESIGN_WIDTH);
double get fSize => ((this * _width) / FIGMA_DESIGN_WIDTH);
+ static double get screenHeight => SizeUtils.height;
+
+ /// Full screen width
+ static double get screenWidth => SizeUtils.width;
}
extension FormatExtension on double {
diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart
index bdca5ff..ed4770d 100644
--- a/lib/core/utils/utils.dart
+++ b/lib/core/utils/utils.dart
@@ -13,6 +13,7 @@ import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/dialogs/confirm_dialog.dart';
@@ -59,14 +60,13 @@ class Utils {
// ));
return !isAddHours
? DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US")
- .format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.toLocal())
+ .format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.toLocal())
: DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US")
- .format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.add(
- Duration(
- hours: isAddHours ? 3 : 0,
- ),
- ));
- ;
+ .format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.add(
+ Duration(
+ hours: isAddHours ? 3 : 0,
+ ),
+ ));
}
static String convertStringToDateTime(String dateTimeString) {
@@ -209,12 +209,12 @@ class Utils {
builder: (BuildContext context) => LoadingDialog(),
)
.then((value) {
- _isLoadingVisible = false;
- })
+ _isLoadingVisible = false;
+ })
.catchError((e) {})
.onError(
(error, stackTrace) {},
- );
+ );
}
static void hideLoading() {
@@ -236,11 +236,12 @@ class Utils {
showDialog(
barrierDismissible: false,
context: context,
- builder: (cxt) => ConfirmDialog(
- title: title!,
- message: message!,
- onTap: onTap,
- ),
+ builder: (cxt) =>
+ ConfirmDialog(
+ title: title!,
+ message: message!,
+ onTap: onTap,
+ ),
);
}
@@ -371,7 +372,9 @@ class Utils {
static String formatHijriDateToDisplay(String hijriDateString) {
try {
// Assuming hijriDateString is in the format yyyy-MM-dd
- final datePart = hijriDateString.split("T").first;
+ final datePart = hijriDateString
+ .split("T")
+ .first;
final parts = datePart.split('-');
if (parts.length != 3) return "";
@@ -429,8 +432,14 @@ class Utils {
void Function(LottieComposition)? onLoaded,
}) {
return Lottie.asset(assetPath,
- height: height ?? MediaQuery.of(context).size.height * 0.26,
- width: width ?? MediaQuery.of(context).size.width,
+ height: height ?? MediaQuery
+ .of(context)
+ .size
+ .height * 0.26,
+ width: width ?? MediaQuery
+ .of(context)
+ .size
+ .width,
fit: fit,
alignment: alignment,
repeat: repeat,
@@ -487,9 +496,13 @@ class Utils {
],
);
}
+
static Future isGoogleServicesAvailable() async {
GooglePlayServicesAvailability availability = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability();
- String status = availability.toString().split('.').last;
+ String status = availability
+ .toString()
+ .split('.')
+ .last;
if (status == "success") {
return true;
}
@@ -510,3 +523,26 @@ class Utils {
return crypto.md5.convert(utf8.encode(input)).toString();
}
}
+
+class ValidationUtils {
+ static DialogService dialogService = getIt.get();
+
+
+ static bool isValidatePhoneAndId({
+ String? nationalId,
+ String? phoneNumber
+ }) {
+ if (nationalId == null || nationalId.isEmpty) {
+ dialogService.showErrorDialog(message: "Please enter a valid national ID or file number", onOkPressed: () {});
+ return false;
+ }
+
+ if (phoneNumber == null || phoneNumber.isEmpty) {
+ dialogService.showErrorDialog(message: "Please enter a valid phone number", onOkPressed: () {});
+ return false;
+ }
+ return true;
+ }
+}
+
+
diff --git a/lib/extensions/context_extensions.dart b/lib/extensions/context_extensions.dart
index 5783ce3..4e9da0d 100644
--- a/lib/extensions/context_extensions.dart
+++ b/lib/extensions/context_extensions.dart
@@ -2,15 +2,32 @@ import 'package:flutter/material.dart';
extension ContextUtils on BuildContext {
double get screenHeight => MediaQuery.of(this).size.height;
+
double get screenWidth => MediaQuery.of(this).size.width;
+
ThemeData get theme => Theme.of(this);
+
TextTheme get textTheme => theme.textTheme;
- // TextStyle get headline1 => textTheme.headline1!;
- // TextStyle get headline2 => textTheme.headline2!;
- // TextStyle get headline3 => textTheme.headline3!;
- // TextStyle get headline4 => textTheme.headline4!;
- // TextStyle get headline5 => textTheme.headline5!;
- // TextStyle get headline6 => textTheme.headline6!;
- // TextStyle get bodyText1 => textTheme.bodyText1!;
- // TextStyle get bodyText2 => textTheme.bodyText2!;
+// TextStyle get headline1 => textTheme.headline1!;
+// TextStyle get headline2 => textTheme.headline2!;
+// TextStyle get headline3 => textTheme.headline3!;
+// TextStyle get headline4 => textTheme.headline4!;
+// TextStyle get headline5 => textTheme.headline5!;
+// TextStyle get headline6 => textTheme.headline6!;
+// TextStyle get bodyText1 => textTheme.bodyText1!;
+// TextStyle get bodyText2 => textTheme.bodyText2!;
+}
+
+extension ShowBottomSheet on BuildContext {
+ Future showBottomSheet({isScrollControlled = true, isDismissible = false, required Widget child, Color? backgroundColor, enableDra = false, useSafeArea = false}) {
+ return showModalBottomSheet(
+ context: this,
+ isScrollControlled: isScrollControlled,
+ isDismissible: isDismissible,
+ enableDrag: enableDra,
+ useSafeArea: useSafeArea,
+ backgroundColor: backgroundColor ?? Colors.transparent,
+ builder: (_) => child,
+ );
+ }
}
diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart
index a224526..d986de1 100644
--- a/lib/extensions/string_extensions.dart
+++ b/lib/extensions/string_extensions.dart
@@ -1,3 +1,5 @@
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/cupertino.dart';
@@ -11,16 +13,13 @@ extension CapExtension on String {
String get allInCaps => this.toUpperCase();
- String get capitalizeFirstofEach =>
- this.trim().length > 0 ? this.trim().toLowerCase().split(" ").map((str) => str.inCaps).join(" ") : "";
+ String get capitalizeFirstofEach => this.trim().length > 0 ? this.trim().toLowerCase().split(" ").map((str) => str.inCaps).join(" ") : "";
}
extension EmailValidator on String {
Widget get toWidget => Text(this);
- Widget toText8(
- {Color? color, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) =>
- Text(
+ Widget toText8({Color? color, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => Text(
this,
maxLines: maxlines,
overflow: textOverflow,
@@ -33,14 +32,7 @@ extension EmailValidator on String {
),
);
- Widget toText10(
- {Color? color,
- bool isBold = false,
- bool isUnderLine = false,
- int? maxlines,
- FontStyle? fontStyle,
- TextOverflow? textOverflow}) =>
- Text(
+ Widget toText10({Color? color, bool isBold = false, bool isUnderLine = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => Text(
this,
maxLines: maxlines,
overflow: textOverflow,
@@ -54,15 +46,7 @@ extension EmailValidator on String {
decorationColor: color ?? AppColors.blackColor),
);
- Widget toText11(
- {Color? color,
- FontWeight? weight,
- bool isUnderLine = false,
- bool isCenter = false,
- bool isBold = false,
- int maxLine = 0,
- double letterSpacing = 0.64}) =>
- Text(
+ Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = 0.64}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: (maxLine > 0) ? maxLine : null,
@@ -76,20 +60,11 @@ extension EmailValidator on String {
),
);
- Widget toText12(
- {Color? color,
- bool isUnderLine = false,
- bool isBold = false,
- FontWeight? fontWeight,
- bool isCenter = false,
- double? height,
- int maxLine = 0}) =>
- Text(
+ Widget toText12({Color? color, bool isUnderLine = false, bool isBold = false, FontWeight? fontWeight, bool isCenter = false, double? height, int maxLine = 0}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: (maxLine > 0) ? maxLine : null,
style: TextStyle(
-
fontSize: 12.fSize,
fontWeight: fontWeight ?? (isBold ? FontWeight.bold : FontWeight.normal),
color: color ?? AppColors.blackColor,
@@ -100,9 +75,7 @@ extension EmailValidator on String {
),
);
- Widget toText12Auto(
- {Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0}) =>
- AutoSizeText(
+ Widget toText12Auto({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0}) => AutoSizeText(
this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: (maxLine > 0) ? maxLine : null,
@@ -163,14 +136,7 @@ extension EmailValidator on String {
decoration: isUnderLine ? TextDecoration.underline : null),
);
- Widget toText14(
- {Color? color,
- bool isUnderLine = false,
- bool isBold = false,
- bool isCenter = false,
- FontWeight? weight,
- int? maxlines}) =>
- Text(
+ Widget toText14({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, FontWeight? weight, int? maxlines}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: maxlines,
@@ -182,14 +148,7 @@ extension EmailValidator on String {
decoration: isUnderLine ? TextDecoration.underline : null),
);
- Widget toText15(
- {Color? color,
- bool isUnderLine = false,
- bool isBold = false,
- bool isCenter = false,
- FontWeight? weight,
- int? maxlines}) =>
- Text(
+ Widget toText15({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, FontWeight? weight, int? maxlines}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
maxLines: maxlines,
@@ -227,93 +186,68 @@ extension EmailValidator on String {
Widget toText17({Color? color, bool isBold = false, bool isCenter = false}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
- style: TextStyle(
- color: color ?? AppColors.blackColor,
- fontSize: 17.fSize,
- letterSpacing: -0.4,
- fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
+ style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 17.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
Widget toText18({Color? color, bool isBold = false, bool isCenter = false, int? maxlines}) => Text(
maxLines: maxlines,
textAlign: isCenter ? TextAlign.center : null,
this,
- style: TextStyle(
- fontSize: 18.fSize,
- fontWeight: isBold ? FontWeight.bold : FontWeight.normal,
- color: color ?? AppColors.blackColor,
- letterSpacing: -0.4),
+ style: TextStyle(fontSize: 18.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4),
);
Widget toText19({Color? color, bool isBold = false}) => Text(
this,
- style: TextStyle(
- fontSize: 19.fSize,
- fontWeight: isBold ? FontWeight.bold : FontWeight.normal,
- color: color ?? AppColors.blackColor,
- letterSpacing: -0.4),
+ style: TextStyle(fontSize: 19.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4),
);
Widget toText20({Color? color, bool isBold = false}) => Text(
this,
- style: TextStyle(
- fontSize: 20.fSize,
- fontWeight: isBold ? FontWeight.bold : FontWeight.normal,
- color: color ?? AppColors.blackColor,
- letterSpacing: -0.4),
+ style: TextStyle(fontSize: 20.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4),
);
Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Text(
this,
maxLines: maxlines,
- style: TextStyle(
- color: color ?? AppColors.blackColor,
- fontSize: 21.fSize,
- letterSpacing: -0.4,
- fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
+ style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 21.fSize, letterSpacing: -0.4, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
);
Widget toText22({Color? color, bool isBold = false, bool isCenter = false}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
- style: TextStyle(
- height: 1,
- color: color ?? AppColors.blackColor,
- fontSize: 22.fSize,
- letterSpacing: -0.4,
- fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
+ style: TextStyle(height: 1, color: color ?? AppColors.blackColor, fontSize: 22.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
Widget toText24({Color? color, bool isBold = false, bool isCenter = false}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
- style: TextStyle(
- height: 23 / 24,
- color: color ?? AppColors.blackColor,
- fontSize: 24.fSize,
- letterSpacing: -0.4,
- fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
+ style: TextStyle(height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
+ Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false}) => Text(
+ this,
+ textAlign: isCenter ? TextAlign.center : null,
+ style: TextStyle(height: height ?? 23 / 26, color: color ?? AppColors.blackColor, fontSize: 26.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
+ );
+
+
+ Widget toText28({Color? color, bool isBold = false, double? height, bool isCenter = false}) => Text(
+ this,
+ textAlign: isCenter ? TextAlign.center : null,
+ style: TextStyle(height: height ?? 23 / 28, color: color ?? AppColors.blackColor, fontSize: 28.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
+ );
+
+
+
Widget toText32({Color? color, bool isBold = false, bool isCenter = false}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
- style: TextStyle(
- height: 32 / 32,
- color: color ?? AppColors.blackColor,
- fontSize: 32.fSize,
- letterSpacing: -0.4,
- fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
+ style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
Widget toText44({Color? color, bool isBold = false}) => Text(
this,
- style: TextStyle(
- height: 32 / 32,
- color: color ?? AppColors.blackColor,
- fontSize: 44.fSize,
- letterSpacing: -0.4,
- fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
+ style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 44.fSize, letterSpacing: -0.4, fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
Widget toSectionHeading({String upperHeading = "", String lowerHeading = ""}) {
@@ -349,9 +283,7 @@ extension EmailValidator on String {
}
bool isValidEmail() {
- return RegExp(
- r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$')
- .hasMatch(this);
+ return RegExp(r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$').hasMatch(this);
}
String toFormattedDate() {
@@ -453,3 +385,155 @@ class FontUtils {
return isArabic ? 'Cairo' : 'Poppins';
}
}
+
+extension CountryExtension on CountryEnum {
+ String get displayName {
+ switch (this) {
+ case CountryEnum.saudiArabia:
+ return "Kingdom Of Saudi Arabia";
+ case CountryEnum.unitedArabEmirates:
+ return "United Arab Emirates";
+ }
+ }
+
+ String get nameArabic {
+ switch (this) {
+ case CountryEnum.saudiArabia:
+ return "المملكة العربية السعودية";
+ case CountryEnum.unitedArabEmirates:
+ return "الإمارات العربية المتحدة";
+ }
+ }
+
+ String get iconPath {
+ switch (this) {
+ case CountryEnum.saudiArabia:
+ return AppAssets.ksa;
+ case CountryEnum.unitedArabEmirates:
+ return AppAssets.uae;
+ }
+ }
+
+ String get countryCode {
+ switch (this) {
+ case CountryEnum.saudiArabia:
+ return "966";
+ case CountryEnum.unitedArabEmirates:
+ return "971";
+ }
+ }
+
+ static CountryEnum fromDisplayName(String name) {
+ switch (name) {
+ case "Kingdom Of Saudi Arabia":
+ case "المملكة العربية السعودية":
+ return CountryEnum.saudiArabia;
+ case "United Arab Emirates":
+ case "الإمارات العربية المتحدة":
+ return CountryEnum.unitedArabEmirates;
+ default:
+ throw Exception("Invalid country name");
+ }
+ }
+}
+
+extension GenderTypeExtension on GenderTypeEnum {
+ String get value => this == GenderTypeEnum.male ? "M" : "F";
+
+ String get type => this == GenderTypeEnum.male ? "Male" : "Female";
+
+ String get typeAr => this == GenderTypeEnum.male ? "ذكر" : "أنثى";
+
+ static GenderTypeEnum? fromValue(String? value) {
+ switch (value) {
+ case "M":
+ return GenderTypeEnum.male;
+ case "F":
+ return GenderTypeEnum.female;
+ default:
+ return null;
+ }
+ }
+
+ static GenderTypeEnum? fromType(String? type) {
+ switch (type) {
+ case "Male":
+ return GenderTypeEnum.male;
+ case "Female":
+ return GenderTypeEnum.female;
+ default:
+ return null;
+ }
+ }
+}
+
+extension MaritalStatusTypeExtension on MaritalStatusTypeEnum {
+ String get value {
+ switch (this) {
+ case MaritalStatusTypeEnum.single:
+ return "U";
+ case MaritalStatusTypeEnum.married:
+ return "M";
+ case MaritalStatusTypeEnum.divorced:
+ return "D";
+ case MaritalStatusTypeEnum.widowed:
+ return "W";
+ }
+ }
+
+ String get type {
+ switch (this) {
+ case MaritalStatusTypeEnum.single:
+ return "Single";
+ case MaritalStatusTypeEnum.married:
+ return "Married";
+ case MaritalStatusTypeEnum.divorced:
+ return "Divorced";
+ case MaritalStatusTypeEnum.widowed:
+ return "Widowed";
+ }
+ }
+
+ String get typeAr {
+ switch (this) {
+ case MaritalStatusTypeEnum.single:
+ return "أعزب";
+ case MaritalStatusTypeEnum.married:
+ return "متزوج";
+ case MaritalStatusTypeEnum.divorced:
+ return "مطلق";
+ case MaritalStatusTypeEnum.widowed:
+ return "أرمل";
+ }
+ }
+
+ static MaritalStatusTypeEnum? fromValue(String? value) {
+ switch (value) {
+ case "U":
+ return MaritalStatusTypeEnum.single;
+ case "M":
+ return MaritalStatusTypeEnum.married;
+ case "D":
+ return MaritalStatusTypeEnum.divorced;
+ case "W":
+ return MaritalStatusTypeEnum.widowed;
+ default:
+ return null;
+ }
+ }
+
+ static MaritalStatusTypeEnum? fromType(String? type) {
+ switch (type) {
+ case "Single":
+ return MaritalStatusTypeEnum.single;
+ case "Married":
+ return MaritalStatusTypeEnum.married;
+ case "Divorced":
+ return MaritalStatusTypeEnum.divorced;
+ case "Widowed":
+ return MaritalStatusTypeEnum.widowed;
+ default:
+ return null;
+ }
+ }
+}
diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart
index 7307013..0be6eaf 100644
--- a/lib/features/authentication/authentication_repo.dart
+++ b/lib/features/authentication/authentication_repo.dart
@@ -20,8 +20,7 @@ abstract class AuthenticationRepo {
required CheckPatientAuthenticationReq checkPatientAuthenticationReq,
});
- Future>> sendActivationCodeRegister(
- {required CheckPatientAuthenticationReq checkPatientAuthenticationReq, String? languageID});
+ Future>> sendActivationCodeRegister({required CheckPatientAuthenticationReq checkPatientAuthenticationReq, String? languageID});
}
class AuthenticationRepoImp implements AuthenticationRepo {
@@ -114,8 +113,7 @@ class AuthenticationRepoImp implements AuthenticationRepo {
}
@override
- Future>> sendActivationCodeRegister(
- {required CheckPatientAuthenticationReq checkPatientAuthenticationReq, String? languageID}) async {
+ Future>> sendActivationCodeRegister({required CheckPatientAuthenticationReq checkPatientAuthenticationReq, String? languageID}) async {
int isOutKsa = (checkPatientAuthenticationReq.zipCode == '966' || checkPatientAuthenticationReq.zipCode == '+966') ? 0 : 1;
//TODO : We will use all these from AppState directly in the ApiClient
diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart
index ed77aca..c34c339 100644
--- a/lib/features/authentication/authentication_view_model.dart
+++ b/lib/features/authentication/authentication_view_model.dart
@@ -1,7 +1,12 @@
+import 'dart:convert';
+
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/request_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_repo.dart';
import 'package:hmg_patient_app_new/features/authentication/models/request_models/check_patient_authentication_request_model.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
@@ -20,12 +25,67 @@ class AuthenticationViewModel extends ChangeNotifier {
required this.dialogService,
});
- final TextEditingController nationalIdController = TextEditingController();
- final TextEditingController phoneNumberController = TextEditingController();
+ final TextEditingController nationalIdController = TextEditingController(), phoneNumberController = TextEditingController(), dobController = TextEditingController();
+ CountryEnum selectedCountrySignup = CountryEnum.saudiArabia;
+ MaritalStatusTypeEnum? maritalStatus;
+ GenderTypeEnum? genderType;
+ bool isTermsAccepted = false;
+ List? countriesList;
+ NationalityCountries? pickedCountryByUAEUser;
+
+ void login() {
+ if (ValidationUtils.isValidatePhoneAndId(nationalId: nationalIdController.text, phoneNumber: phoneNumberController.text)) {
+ } else {}
+ }
+
+ void clearDefaults() {
+ nationalIdController.clear();
+ phoneNumberController.clear();
+ dobController.clear();
+ maritalStatus = null;
+ genderType = null;
+ isTermsAccepted = false;
+ selectedCountrySignup = CountryEnum.saudiArabia;
+ pickedCountryByUAEUser = null;
+ }
+
+ void onCountryChange(CountryEnum country) {
+ selectedCountrySignup = country;
+ notifyListeners();
+ }
+
+ void loadCountriesData({required BuildContext context}) async {
+ final String response = await DefaultAssetBundle.of(context).loadString('assets/json/countriesList.json');
+ final List data = json.decode(response);
+ countriesList = data.map((e) => NationalityCountries.fromJson(e)).toList();
+ }
+
+ void onMaritalStatusChange(String? status) {
+ maritalStatus = MaritalStatusTypeExtension.fromType(status)!;
+ notifyListeners();
+ }
+
+ void onGenderChange(String? status) {
+ genderType = GenderTypeExtension.fromType(status)!;
+ notifyListeners();
+ }
+
+ void onUAEUserCountrySelection(String? value) {
+ pickedCountryByUAEUser = countriesList!.firstWhere((element) => element.name == value);
+ notifyListeners();
+ }
+
+ void onPhoneNumberChange(String? phoneNumber) {
+ phoneNumberController.text = phoneNumber!;
+ }
+
+ void onTermAccepted() {
+ isTermsAccepted = !isTermsAccepted;
+ notifyListeners();
+ }
Future selectDeviceImei({Function(dynamic)? onSuccess, Function(String)? onError}) async {
- String firebaseToken =
- "dOGRRszQQMGe_9wA5Hx3kO:APA91bFV5IcIJXvcCXXk0tc2ddtZgWwCPq7sGSuPr-YW7iiJpQZKgFGN9GAzCVOWL8MfheaP1slE8MdxB7lczdPBGdONQ7WbMmhgHcsUCUktq-hsapGXXqc";
+ String firebaseToken = "dOGRRszQQMGe_9wA5Hx3kO:APA91bFV5IcIJXvcCXXk0tc2ddtZgWwCPq7sGSuPr-YW7iiJpQZKgFGN9GAzCVOWL8MfheaP1slE8MdxB7lczdPBGdONQ7WbMmhgHcsUCUktq-hsapGXXqc";
final result = await authenticationRepo.selectDeviceByImei(firebaseToken: firebaseToken);
result.fold(
@@ -40,96 +100,96 @@ class AuthenticationViewModel extends ChangeNotifier {
);
}
- // Future checkUserAuthentication({Function(dynamic)? onSuccess, Function(String)? onError}) async {
- // CheckPatientAuthenticationReq checkPatientAuthenticationReq = RequestUtils.getCommonRequestWelcome(
- // phoneNumber: '0567184134',
- // otpTypeEnum: OTPTypeEnum.sms,
- // deviceToken: 'dummyDeviceToken123',
- // patientOutSA: true,
- // loginTokenID: 'dummyLoginToken456',
- // registeredData: null,
- // patientId: 12345,
- // nationIdText: '1234567890',
- // countryCode: 'SA',
- // );
- //
- // final result = await authenticationRepo.checkPatientAuthentication(checkPatientAuthenticationReq: checkPatientAuthenticationReq);
- // result.fold(
- // (failure) async => await errorHandlerService.handleError(failure: failure),
- // (apiResponse) {
- // if (apiResponse.data['isSMSSent']) {
- // // TODO: set this in AppState
- // // sharedPref.setString(LOGIN_TOKEN_ID, value['LogInTokenID']);
- // // loginTokenID = value['LogInTokenID'],
- // // sharedPref.setObject(REGISTER_DATA_FOR_LOGIIN, request),
- // sendActivationCode(type);
- // } else {
- // if (apiResponse.data['IsAuthenticated']) {
- // checkActivationCode(onWrongActivationCode: (String? message) {});
- // }
- // }
- // },
- // );
- // }
-
- // Future sendActivationCode({required OTPTypeEnum otpTypeEnum}) async {
- // var request = RequestUtils.getCommonRequestAuthProvider(
- // otpTypeEnum: otpTypeEnum,
- // registeredData: null,
- // deviceToken: "dummyLoginToken456",
- // mobileNumber: "0567184134",
- // zipCode: "SA",
- // patientOutSA: true,
- // loginTokenID: "dummyLoginToken456",
- // selectedOption: selectedOption,
- // patientId: 12345,
- // );
- //
- // request.sMSSignature = await SMSOTP.getSignature();
- // selectedOption = type;
- // // GifLoaderDialogUtils.showMyDialog(context);
- // if (healthId != null || isDubai) {
- // if (!isDubai) {
- // request.dob = dob; //isHijri == 1 ? dob : dateFormat2.format(dateFormat.parse(dob));
- // }
- // request.healthId = healthId;
- // request.isHijri = isHijri;
- // await this.apiClient.sendActivationCodeRegister(request).then((result) {
- // // GifLoaderDialogUtils.hideDialog(context);
- // if (result != null && result['isSMSSent'] == true) {
- // this.startSMSService(type);
- // }
- // }).catchError((r) {
- // GifLoaderDialogUtils.hideDialog(context);
- // context.showBottomSheet(
- // child: ExceptionBottomSheet(
- // message: r.toString(),
- // onOkPressed: () {
- // Navigator.of(context).pop();
- // },
- // ));
- // // AppToast.showErrorToast(message: r);
- // });
- // } else {
- // request.dob = "";
- // request.healthId = "";
- // request.isHijri = 0;
- // await this.authService.sendActivationCode(request).then((result) {
- // GifLoaderDialogUtils.hideDialog(context);
- // if (result != null && result['isSMSSent'] == true) {
- // this.startSMSService(type);
- // }
- // }).catchError((r) {
- // GifLoaderDialogUtils.hideDialog(context);
- // context.showBottomSheet(
- // child: ExceptionBottomSheet(
- // message: r.toString(),
- // onOkPressed: () {
- // Navigator.of(context).pop();
- // },
- // ));
- // // AppToast.showErrorToast(message: r.toString());
- // });
- // }
- // }
+// Future checkUserAuthentication({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+// CheckPatientAuthenticationReq checkPatientAuthenticationReq = RequestUtils.getCommonRequestWelcome(
+// phoneNumber: '0567184134',
+// otpTypeEnum: OTPTypeEnum.sms,
+// deviceToken: 'dummyDeviceToken123',
+// patientOutSA: true,
+// loginTokenID: 'dummyLoginToken456',
+// registeredData: null,
+// patientId: 12345,
+// nationIdText: '1234567890',
+// countryCode: 'SA',
+// );
+//
+// final result = await authenticationRepo.checkPatientAuthentication(checkPatientAuthenticationReq: checkPatientAuthenticationReq);
+// result.fold(
+// (failure) async => await errorHandlerService.handleError(failure: failure),
+// (apiResponse) {
+// if (apiResponse.data['isSMSSent']) {
+// // TODO: set this in AppState
+// // sharedPref.setString(LOGIN_TOKEN_ID, value['LogInTokenID']);
+// // loginTokenID = value['LogInTokenID'],
+// // sharedPref.setObject(REGISTER_DATA_FOR_LOGIIN, request),
+// sendActivationCode(type);
+// } else {
+// if (apiResponse.data['IsAuthenticated']) {
+// checkActivationCode(onWrongActivationCode: (String? message) {});
+// }
+// }
+// },
+// );
+// }
+
+// Future sendActivationCode({required OTPTypeEnum otpTypeEnum}) async {
+// var request = RequestUtils.getCommonRequestAuthProvider(
+// otpTypeEnum: otpTypeEnum,
+// registeredData: null,
+// deviceToken: "dummyLoginToken456",
+// mobileNumber: "0567184134",
+// zipCode: "SA",
+// patientOutSA: true,
+// loginTokenID: "dummyLoginToken456",
+// selectedOption: selectedOption,
+// patientId: 12345,
+// );
+//
+// request.sMSSignature = await SMSOTP.getSignature();
+// selectedOption = type;
+// // GifLoaderDialogUtils.showMyDialog(context);
+// if (healthId != null || isDubai) {
+// if (!isDubai) {
+// request.dob = dob; //isHijri == 1 ? dob : dateFormat2.format(dateFormat.parse(dob));
+// }
+// request.healthId = healthId;
+// request.isHijri = isHijri;
+// await this.apiClient.sendActivationCodeRegister(request).then((result) {
+// // GifLoaderDialogUtils.hideDialog(context);
+// if (result != null && result['isSMSSent'] == true) {
+// this.startSMSService(type);
+// }
+// }).catchError((r) {
+// GifLoaderDialogUtils.hideDialog(context);
+// context.showBottomSheet(
+// child: ExceptionBottomSheet(
+// message: r.toString(),
+// onOkPressed: () {
+// Navigator.of(context).pop();
+// },
+// ));
+// // AppToast.showErrorToast(message: r);
+// });
+// } else {
+// request.dob = "";
+// request.healthId = "";
+// request.isHijri = 0;
+// await this.authService.sendActivationCode(request).then((result) {
+// GifLoaderDialogUtils.hideDialog(context);
+// if (result != null && result['isSMSSent'] == true) {
+// this.startSMSService(type);
+// }
+// }).catchError((r) {
+// GifLoaderDialogUtils.hideDialog(context);
+// context.showBottomSheet(
+// child: ExceptionBottomSheet(
+// message: r.toString(),
+// onOkPressed: () {
+// Navigator.of(context).pop();
+// },
+// ));
+// // AppToast.showErrorToast(message: r.toString());
+// });
+// }
+// }
}
diff --git a/lib/features/insurance/insurance_repo.dart b/lib/features/insurance/insurance_repo.dart
new file mode 100644
index 0000000..a5c624f
--- /dev/null
+++ b/lib/features/insurance/insurance_repo.dart
@@ -0,0 +1,77 @@
+import 'package:dartz/dartz.dart';
+import 'package:hmg_patient_app_new/core/api/api_client.dart';
+import 'package:hmg_patient_app_new/core/api_consts.dart';
+import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
+import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
+import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart';
+import 'package:hmg_patient_app_new/services/logger_service.dart';
+
+abstract class InsuranceRepo {
+ Future>>> getPatientInsuranceDetails({required String patientId});
+}
+
+class InsuranceRepoImp implements InsuranceRepo {
+ final ApiClient apiClient;
+ final LoggerService loggerService;
+
+ InsuranceRepoImp({required this.loggerService, required this.apiClient});
+
+ @override
+ Future>>> getPatientInsuranceDetails({required String patientId}) async {
+ final mapDevice = {
+ "isDentalAllowedBackend": false,
+ "VersionID": 50.0,
+ "Channel": 3,
+ "LanguageID": 2,
+ "IPAdress": "10.20.10.20",
+ "generalid": "Cs2020@2016\$2958",
+ "Latitude": 0.0,
+ "Longitude": 0.0,
+ "DeviceTypeID": 1,
+ "PatientType": 1,
+ "PatientTypeID": 1,
+ "TokenID": "@dm!n",
+ "PatientID": "3628599",
+ "PatientOutSA": "0",
+ "SessionID": "03478TYC02N80874CTYN04883475!?"
+ };
+
+ try {
+ GenericApiModel>? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_PAtIENTS_INSURANCE,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus}) {
+ try {
+ final list = response['List_PatientInsuranceCard'];
+ if (list == null || list.isEmpty) {
+ throw Exception("insurance list is empty");
+ }
+
+ final labOrders = list.map((item) => PatientInsuranceDetailsResponseModel.fromJson(item as Map)).toList().cast();
+
+ apiResponse = GenericApiModel>(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: labOrders,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+
+ throw UnimplementedError();
+ }
+}
diff --git a/lib/features/insurance/insurance_view_model.dart b/lib/features/insurance/insurance_view_model.dart
new file mode 100644
index 0000000..fac09da
--- /dev/null
+++ b/lib/features/insurance/insurance_view_model.dart
@@ -0,0 +1,50 @@
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/features/insurance/insurance_repo.dart';
+import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart';
+import 'package:hmg_patient_app_new/features/lab/lab_repo.dart';
+import 'package:hmg_patient_app_new/services/error_handler_service.dart';
+
+class InsuranceViewModel extends ChangeNotifier {
+ bool isInsuranceLoading = false;
+ bool isInsuranceHistoryLoading = false;
+
+ InsuranceRepo insuranceRepo;
+ ErrorHandlerService errorHandlerService;
+
+ List patientInsuranceList = [];
+
+ InsuranceViewModel({required this.insuranceRepo, required this.errorHandlerService});
+
+ initInsuranceProvider() {
+ patientInsuranceList.clear();
+ isInsuranceLoading = true;
+ isInsuranceHistoryLoading = true;
+ getPatientInsuranceDetails();
+ notifyListeners();
+ }
+
+ setIsInsuranceHistoryLoading(bool val) {
+ isInsuranceHistoryLoading = val;
+ notifyListeners();
+ }
+
+ Future getPatientInsuranceDetails({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ final result = await insuranceRepo.getPatientInsuranceDetails(patientId: "1231755");
+
+ result.fold(
+ (failure) async => await errorHandlerService.handleError(failure: failure),
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
+ } else if (apiResponse.messageStatus == 1) {
+ patientInsuranceList = apiResponse.data!;
+ isInsuranceLoading = false;
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
+}
diff --git a/lib/features/insurance/models/resp_models/patient_insurance_details_response_model.dart b/lib/features/insurance/models/resp_models/patient_insurance_details_response_model.dart
new file mode 100644
index 0000000..b734abc
--- /dev/null
+++ b/lib/features/insurance/models/resp_models/patient_insurance_details_response_model.dart
@@ -0,0 +1,96 @@
+class PatientInsuranceDetailsResponseModel {
+ String? setupID;
+ int? projectID;
+ bool? isActive;
+ int? patientID;
+ int? companyID;
+ int? subCategoryID;
+ dynamic companyType;
+ String? patientCardID;
+ String? cardValidTo;
+ int? patientCreditLimit;
+ String? subPolicyNo;
+ String? companyName;
+ String? companyNameN;
+ String? subCategoryDesc;
+ dynamic subCategoryDescN;
+ bool? isElectronicClaim;
+ String? subCategoryValidTo;
+ dynamic groupID;
+ String? groupName;
+ dynamic groupNameN;
+ String? insurancePolicyNo;
+
+ PatientInsuranceDetailsResponseModel(
+ {this.setupID,
+ this.projectID,
+ this.isActive,
+ this.patientID,
+ this.companyID,
+ this.subCategoryID,
+ this.companyType,
+ this.patientCardID,
+ this.cardValidTo,
+ this.patientCreditLimit,
+ this.subPolicyNo,
+ this.companyName,
+ this.companyNameN,
+ this.subCategoryDesc,
+ this.subCategoryDescN,
+ this.isElectronicClaim,
+ this.subCategoryValidTo,
+ this.groupID,
+ this.groupName,
+ this.groupNameN,
+ this.insurancePolicyNo});
+
+ PatientInsuranceDetailsResponseModel.fromJson(Map json) {
+ setupID = json['SetupID'];
+ projectID = json['ProjectID'];
+ isActive = json['IsActive'];
+ patientID = json['PatientID'];
+ companyID = json['CompanyID'];
+ subCategoryID = json['SubCategoryID'];
+ companyType = json['CompanyType'];
+ patientCardID = json['PatientCardID'];
+ cardValidTo = json['CardValidTo'];
+ patientCreditLimit = json['PatientCreditLimit'];
+ subPolicyNo = json['SubPolicyNo'];
+ companyName = json['CompanyName'];
+ companyNameN = json['CompanyNameN'];
+ subCategoryDesc = json['SubCategoryDesc'];
+ subCategoryDescN = json['SubCategoryDescN'];
+ isElectronicClaim = json['IsElectronicClaim'];
+ subCategoryValidTo = json['SubCategoryValidTo'];
+ groupID = json['GroupID'];
+ groupName = json['GroupName'];
+ groupNameN = json['GroupNameN'];
+ insurancePolicyNo = json['InsurancePolicyNo'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['SetupID'] = this.setupID;
+ data['ProjectID'] = this.projectID;
+ data['IsActive'] = this.isActive;
+ data['PatientID'] = this.patientID;
+ data['CompanyID'] = this.companyID;
+ data['SubCategoryID'] = this.subCategoryID;
+ data['CompanyType'] = this.companyType;
+ data['PatientCardID'] = this.patientCardID;
+ data['CardValidTo'] = this.cardValidTo;
+ data['PatientCreditLimit'] = this.patientCreditLimit;
+ data['SubPolicyNo'] = this.subPolicyNo;
+ data['CompanyName'] = this.companyName;
+ data['CompanyNameN'] = this.companyNameN;
+ data['SubCategoryDesc'] = this.subCategoryDesc;
+ data['SubCategoryDescN'] = this.subCategoryDescN;
+ data['IsElectronicClaim'] = this.isElectronicClaim;
+ data['SubCategoryValidTo'] = this.subCategoryValidTo;
+ data['GroupID'] = this.groupID;
+ data['GroupName'] = this.groupName;
+ data['GroupNameN'] = this.groupNameN;
+ data['InsurancePolicyNo'] = this.insurancePolicyNo;
+ return data;
+ }
+}
diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart
index acd87eb..115b28d 100644
--- a/lib/features/lab/lab_view_model.dart
+++ b/lib/features/lab/lab_view_model.dart
@@ -12,6 +12,10 @@ class LabViewModel extends ChangeNotifier {
List patientLabOrders = [];
+ List labSuggestionsList =[];
+
+ get labSuggestions => labSuggestionsList;
+
LabViewModel({required this.labRepo, required this.errorHandlerService});
initLabProvider() {
@@ -42,4 +46,8 @@ class LabViewModel extends ChangeNotifier {
},
);
}
+
+ filterSuggestions(){
+
+ }
}
diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart
index cd59c15..7831616 100644
--- a/lib/generated/locale_keys.g.dart
+++ b/lib/generated/locale_keys.g.dart
@@ -781,7 +781,29 @@ abstract class LocaleKeys {
static const resultsAvailable = 'resultsAvailable';
static const viewReport = 'viewReport';
static const prescriptionDeliveryError = 'prescriptionDeliveryError';
+ static const receiveOtpToast = 'receiveOtpToast';
+ static const enterPhoneNumber = 'enterPhoneNumber';
+ static const enterEmailDesc = 'enterEmailDesc';
+ static const enterPhoneDesc = 'enterPhoneDesc';
+ static const pleaseChooseOption = 'pleaseChooseOption';
+ static const prepareToElevate = 'prepareToElevate';
+ static const iAcceptTermsConditions = 'iAcceptTermsConditions';
+ static const alreadyHaveAccount = 'alreadyHaveAccount';
+ static const loginNow = 'loginNow';
+ static const notice = 'notice';
+ static const oR = 'oR';
+ static const sendOTPWHATSAPP = 'sendOTPWHATSAPP';
+ static const sendOTPSMS = 'sendOTPSMS';
+ static const fullName = 'fullName';
+ static const married = 'married';
+ static const uae = 'uae';
+ static const malE = 'malE';
+ static const loginBy = 'loginBy';
+ static const loginByOTP = 'loginByOTP';
+ static const guest = 'guest';
+ static const switchAccount = 'switchAccount';
static const checkAvailability = 'checkAvailability';
static const readInstructions = 'readInstructions';
+ static const searchLabReport = 'searchLabReport';
}
diff --git a/lib/main.dart b/lib/main.dart
index 035c6bf..ce69f5d 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
+import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart';
@@ -71,6 +72,12 @@ void main() async {
errorHandlerService: getIt(),
),
),
+ ChangeNotifierProvider(
+ create: (_) => InsuranceViewModel(
+ insuranceRepo: getIt(),
+ errorHandlerService: getIt(),
+ ),
+ ),
ChangeNotifierProvider(
create: (_) => AuthenticationViewModel(
authenticationRepo: getIt(),
diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart
index fb88ba1..aef877f 100644
--- a/lib/presentation/authentication/login.dart
+++ b/lib/presentation/authentication/login.dart
@@ -1,17 +1,21 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/gestures.dart';
-import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/context_extensions.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/authentication/register.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
+import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
-import 'package:sizer/sizer.dart'; // Import sizer
+import 'package:provider/provider.dart';
class LoginScreen extends StatefulWidget {
@override
@@ -31,99 +35,165 @@ class _LoginScreen extends State {
@override
Widget build(BuildContext context) {
- return Sizer(// Wrap with Sizer
- builder: (context, orientation, deviceType) {
- return Scaffold(
- backgroundColor: AppColors.bgScaffoldColor,
- appBar: CustomAppBar(
- onBackPressed: () {
- },
- onLanguageChanged: (String value) {
- print(value);
- context.setLocale(value == 'en' ? Locale('ar', 'SA') : Locale('en', 'US'));
- },
+ AuthenticationViewModel authVm = context.read();
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ appBar: CustomAppBar(
+ onBackPressed: () {
+ Navigator.of(context).pop();
+ },
+ onLanguageChanged: (String value) {
+ // context.setLocale(value == 'en' ? Locale('ar', 'SA') : Locale('en', 'US'));
+ },
+ ),
+ body: GestureDetector(
+ onTap: () {
+ FocusScope.of(context).unfocus(); // Dismiss the keyboard when tapping outside
+ },
+ child: SingleChildScrollView(
+ child: Padding(
+ padding: EdgeInsets.symmetric(horizontal: 24.h),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Utils.showLottie(context: context, assetPath: AppAnimations.login, width: 200.h, height: 200.h, repeat: true, fit: BoxFit.cover),
+ SizedBox(height: 130.h), // Adjusted to sizer unit
+ LocaleKeys.welcomeToDrSulaiman.tr().toText32(isBold: true, color: AppColors.textColor),
+ SizedBox(height: 32.h),
+ TextInputWidget(
+ labelText: "${LocaleKeys.nationalId.tr()} / ${LocaleKeys.fileNo.tr()}",
+ hintText: "xxxxxxxxx",
+ controller: authVm.nationalIdController,
+ keyboardType: TextInputType.number,
+ isEnable: true,
+ prefix: null,
+ autoFocus: true,
+ isAllowRadius: true,
+ isBorderAllowed: false,
+ isAllowLeadingIcon: true,
+ padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 10.h),
+ leadingIcon: AppAssets.student_card,
+ errorMessage: "Please enter a valid national ID or file number",
+ hasError: true,
+ ),
+ SizedBox(height: 16.h), // Adjusted to sizer unit (approx 16px)
+ CustomButton(
+ text: LocaleKeys.login.tr(),
+ icon: AppAssets.login1,
+ iconColor: Colors.white,
+ onPressed: () {
+ showLoginModel(context: context, authVM: authVm);
+ // if (nationIdController.text.isNotEmpty) {
+
+ // } else {
+ // showBottomSheet(
+ // child: ExceptionBottomSheet(
+ // message: TranslationBase.of(context).pleaseEnterNationalIdOrFileNo,
+ // showCancel: false,
+ // onOkPressed: () {
+ // Navigator.of(context).pop();
+ // },
+ // ),
+ // );
+ // }
+ },
+ ),
+ SizedBox(height: 10.h), // Adjusted to sizer unit (approx 14px)
+ Center(
+ child: RichText(
+ textAlign: TextAlign.center,
+ text: TextSpan(
+ style: context.dynamicTextStyle(
+ color: Colors.black,
+ fontSize: 14.fSize, // Adjusted to sizer unit
+ height: 26 / 16, // This height is a ratio, may need re-evaluation
+ fontWeight: FontWeight.w500,
+ ),
+ children: [
+ TextSpan(text: LocaleKeys.dontHaveAccount.tr(), style: context.dynamicTextStyle()),
+ TextSpan(text: " "),
+ TextSpan(
+ text: LocaleKeys.registernow.tr(),
+ style: context.dynamicTextStyle(
+ color: AppColors.primaryRedColor,
+ fontSize: 14.fSize, // Adjusted to sizer unit
+ height: 26 / 16, // Ratio
+ fontWeight: FontWeight.w500,
+ ),
+ recognizer: TapGestureRecognizer()
+ ..onTap = () {
+ Navigator.of(context).push(
+ MaterialPageRoute(builder: (BuildContext context) => RegisterNew()),
+ );
+ },
+ ),
+ ],
+ ),
+ ).withVerticalPadding(2), // Adjusted to sizer unit
+ ),
+ ],
+ ),
+ ),
),
- body: GestureDetector(
- onTap: () {
- FocusScope.of(context).unfocus(); // Dismiss the keyboard when tapping outside
- },
- child: SingleChildScrollView(
- child: Padding(
- padding: EdgeInsets.only(left: 6.w, right: 6.w),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Utils.showLottie(context: context, assetPath: AppAnimations.login, width: 45.w, height: 22.h, repeat: true, fit: BoxFit.cover),
- SizedBox(height: 19.h), // Adjusted to sizer unit
- LocaleKeys.welcomeToDrSulaiman.tr().toText22(isBold: true, color: AppColors.textColor),
- // Text(
- // LocaleKeys.welcomeToDrSulaiman.tr(),
- // style: context.dynamicTextStyle(
- // fontSize: 22,
- // fontWeight: FontWeight.w600,
- // color: AppColors.textColor,
- // letterSpacing: -0.4,
- // height: 40 / 28,
- // ),
- // ),
- SizedBox(height: 4.h), // Adjusted to sizer unit (approx 32px)
- TextInputWidget(
- labelText: "${LocaleKeys.nationalId.tr()} / ${LocaleKeys.fileNo.tr()}",
- hintText: "xxxxxxxxx",
- controller: TextEditingController(),
- keyboardType: TextInputType.number,
- isEnable: true,
- prefix: null,
- autoFocus: true,
- isBorderAllowed: false,
- isAllowLeadingIcon: true,
- padding: EdgeInsets.symmetric(vertical: 1.h, horizontal: 2.w),
- leadingIcon: AppAssets.student_card,
- errorMessage: "Please enter a valid national ID or file number",
- hasError: true,
- ),
- SizedBox(height: 2.h), // Adjusted to sizer unit (approx 16px)
- CustomButton(
- text: LocaleKeys.login.tr(),
- icon: AppAssets.login1,
- iconColor: Colors.white,
- onPressed: () {},
+ ),
+ );
+ }
+
+ void showLoginModel({required BuildContext context, required AuthenticationViewModel authVM}) {
+ context.showBottomSheet(
+ isScrollControlled: true,
+ isDismissible: false,
+ useSafeArea: true,
+ backgroundColor: Colors.transparent,
+ child: StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) {
+ return Padding(
+ padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
+ child: SingleChildScrollView(
+ child: GenericBottomSheet(
+ countryCode: authVM.selectedCountrySignup.countryCode,
+ initialPhoneNumber: "",
+ textController: authVM.phoneNumberController,
+ isEnableCountryDropdown: true,
+ onCountryChange: authVM.onCountryChange,
+ onChange: authVM.onPhoneNumberChange,
+ buttons: [
+ Padding(
+ padding: EdgeInsets.only(bottom: 10.h),
+ child: CustomButton(
+ text: LocaleKeys.sendOTPSMS.tr(),
+ onPressed: () {},
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedBorderColor,
+ textColor: AppColors.whiteColor,
+ icon: AppAssets.message,
+ ),
),
- SizedBox(height: 1.8.h), // Adjusted to sizer unit (approx 14px)
- Center(
- child: RichText(
- textAlign: TextAlign.center,
- text: TextSpan(
- style: context.dynamicTextStyle(
- color: Colors.black,
- fontSize: 14.sp, // Adjusted to sizer unit
- height: 26 / 16, // This height is a ratio, may need re-evaluation
- fontWeight: FontWeight.w500,
- ),
- children: [
- TextSpan(text: LocaleKeys.dontHaveAccount.tr(), style: context.dynamicTextStyle()),
- TextSpan(text: " "),
- TextSpan(
- text: LocaleKeys.registernow.tr(),
- style: context.dynamicTextStyle(
- color: AppColors.primaryRedColor,
- fontSize: 14.sp, // Adjusted to sizer unit
- height: 26 / 16, // Ratio
- fontWeight: FontWeight.w500,
- ),
- recognizer: TapGestureRecognizer()..onTap = () {},
- ),
- ],
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Padding(
+ padding: EdgeInsets.symmetric(horizontal: 8.h),
+ child: LocaleKeys.oR.tr().toText16(color: AppColors.textColor),
),
- ).withVerticalPadding(2.h), // Adjusted to sizer unit
+ ],
+ ),
+ Padding(
+ padding: EdgeInsets.only(bottom: 10.h, top: 10.h),
+ child: CustomButton(
+ text: LocaleKeys.sendOTPWHATSAPP.tr(),
+ onPressed: () {},
+ backgroundColor: Colors.white,
+ borderColor: AppColors.borderOnlyColor,
+ textColor: AppColors.textColor,
+ icon: AppAssets.whatsapp,
+ ),
),
],
),
),
- ),
- ),
- );
- });
+ );
+ }));
}
}
diff --git a/lib/presentation/authentication/register.dart b/lib/presentation/authentication/register.dart
new file mode 100644
index 0000000..4756cba
--- /dev/null
+++ b/lib/presentation/authentication/register.dart
@@ -0,0 +1,316 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/enums.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
+import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart' show CustomButton;
+import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart';
+import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart';
+import 'package:hmg_patient_app_new/widgets/input_widget.dart';
+import 'package:hmg_patient_app_new/widgets/otp/otp.dart';
+import 'package:provider/provider.dart';
+
+class RegisterNew extends StatefulWidget {
+ @override
+ _RegisterNew createState() => _RegisterNew();
+}
+
+class _RegisterNew extends State {
+ @override
+ void initState() {
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ AppState appState = getIt.get();
+ AuthenticationViewModel authVm = context.read();
+
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ appBar: CustomAppBar(
+ onBackPressed: () {
+ Navigator.of(context).pop();
+ },
+ onLanguageChanged: (String value) {
+ // context.setLocale(value == 'en' ? Locale('ar', 'SA') : Locale('en', 'US'));
+ },
+ ),
+ body: GestureDetector(
+ onTap: () {
+ FocusScope.of(context).unfocus();
+ },
+ child: ScrollConfiguration(
+ behavior: ScrollConfiguration.of(context).copyWith(overscroll: false, physics: const ClampingScrollPhysics()),
+ child: NotificationListener(
+ onNotification: (notification) {
+ notification.disallowIndicator();
+ return true;
+ },
+ child: SingleChildScrollView(
+ physics: ClampingScrollPhysics(),
+ padding: EdgeInsets.symmetric(horizontal: 24.h),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Utils.showLottie(context: context, assetPath: 'assets/animations/lottie/register.json', width: 200.h, height: 200.h, fit: BoxFit.cover, repeat: true),
+ SizedBox(height: 16.h),
+ LocaleKeys.prepareToElevate.tr().toText32(isBold: true),
+ SizedBox(height: 24.h),
+ Directionality(
+ textDirection: Directionality.of(context),
+ child: Container(
+ decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(24)),
+ padding: EdgeInsets.symmetric(horizontal: 16.h),
+ child: Column(
+ children: [
+ CustomCountryDropdown(
+ countryList: CountryEnum.values,
+ onCountryChange: authVm.onCountryChange,
+ isRtl: Directionality.of(context) == TextDirection.LTR,
+ ).withVerticalPadding(8.h),
+ Divider(height: 1.h),
+ TextInputWidget(
+ labelText: LocaleKeys.nationalIdNumber.tr(),
+ hintText: "xxxxxxxxx",
+ controller: authVm.nationalIdController,
+ isEnable: true,
+ prefix: null,
+ isAllowRadius: true,
+ isBorderAllowed: false,
+ isAllowLeadingIcon: true,
+ autoFocus: true,
+ padding: EdgeInsets.symmetric(vertical: 8.h),
+ leadingIcon: AppAssets.student_card,
+ // onChange: (value) {
+ // print(value);
+ // }
+ ).withVerticalPadding(8),
+ Divider(height: 1),
+ TextInputWidget(
+ labelText: LocaleKeys.dob.tr(),
+ hintText: "11 July, 1994",
+ controller: authVm.dobController,
+ isEnable: true,
+ prefix: null,
+ isAllowRadius: true,
+ isBorderAllowed: false,
+ isAllowLeadingIcon: true,
+ padding: EdgeInsets.symmetric(vertical: 8.h),
+ leadingIcon: AppAssets.birthday_cake,
+ selectionType: SelectionTypeEnum.calendar,
+ ).withVerticalPadding(8),
+ ],
+ ),
+ ),
+ ),
+ SizedBox(height: 25.h),
+ GestureDetector(
+ onTap: authVm.onTermAccepted,
+ child: Row(
+ children: [
+ Selector(
+ selector: (_, viewModel) => viewModel.isTermsAccepted,
+ shouldRebuild: (previous, next) => previous != next,
+ builder: (context, isTermsAccepted, child) {
+ return AnimatedContainer(
+ duration: const Duration(milliseconds: 200),
+ height: 24.h,
+ width: 24.h,
+ decoration: BoxDecoration(
+ color: isTermsAccepted ? AppColors.primaryRedColor : Colors.transparent,
+ borderRadius: BorderRadius.circular(6),
+ border: Border.all(color: isTermsAccepted ? AppColors.primaryRedBorderColor : AppColors.greyColor, width: 2.h),
+ ),
+ child: isTermsAccepted ? Icon(Icons.check, size: 16.fSize, color: Colors.white) : null,
+ );
+ },
+ ),
+ SizedBox(width: 12.h),
+ Expanded(
+ child: Text(
+ LocaleKeys.iAcceptTermsConditions.tr(),
+ style: context.dynamicTextStyle(fontSize: 14.fSize, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)),
+ ),
+ ),
+ ],
+ ),
+ ),
+ SizedBox(height: 25.h),
+ CustomButton(
+ text: "Register",
+ icon: AppAssets.note_edit,
+ onPressed: () {
+ showRegisterModel(context: context, authVM: authVm);
+ },
+ ),
+ SizedBox(height: 14),
+ Center(
+ child: RichText(
+ textAlign: TextAlign.center,
+ text: TextSpan(
+ style: context.dynamicTextStyle(
+ color: Colors.black,
+ fontSize: 16.fSize,
+ height: 26 / 16,
+ fontWeight: FontWeight.w500,
+ ),
+ children: [
+ TextSpan(text: LocaleKeys.alreadyHaveAccount.tr(), style: context.dynamicTextStyle()),
+ TextSpan(text: " "),
+ TextSpan(
+ text: LocaleKeys.loginNow.tr(),
+ style: context.dynamicTextStyle(
+ color: AppColors.primaryRedColor,
+ fontSize: 16.fSize,
+ height: 26 / 16,
+ fontWeight: FontWeight.w500,
+ ),
+ recognizer: TapGestureRecognizer()
+ ..onTap = () {
+ Navigator.of(context).pop();
+ },
+ ),
+ ],
+ ),
+ ),
+ ),
+ SizedBox(height: 30),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ));
+ }
+
+ void showRegisterModel({required BuildContext context, required AuthenticationViewModel authVM}) {
+ showModalBottomSheet(
+ context: context,
+ isScrollControlled: true,
+ isDismissible: false,
+ useSafeArea: true,
+ backgroundColor: Colors.transparent,
+ builder: (bottomSheetContext) => Padding(
+ padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom),
+ child: SingleChildScrollView(
+ child: GenericBottomSheet(
+ countryCode: authVM.selectedCountrySignup.countryCode,
+ initialPhoneNumber: "",
+ textController: authVM.phoneNumberController,
+ isEnableCountryDropdown: true,
+ onCountryChange: authVM.onCountryChange,
+ onChange: authVM.onPhoneNumberChange,
+ buttons: [
+ Padding(
+ padding: const EdgeInsets.only(bottom: 10),
+ child: CustomButton(
+ text: LocaleKeys.sendOTPSMS.tr(),
+ onPressed: () {
+ Navigator.of(context).push(MaterialPageRoute(
+ builder: (BuildContext context) => OTPVerificationPage(
+ phoneNumber: '12234567',
+ )));
+
+ // if (mobileNo.isEmpty) {
+ // context.showBottomSheet(
+ // child: ExceptionBottomSheet(
+ // message: TranslationBase.of(context).pleaseEnterMobile,
+ // showCancel: false,
+ // onOkPressed: () {
+ // Navigator.of(context).pop();
+ // },
+ // ),
+ // );
+ // } else if (!Utils.validateMobileNumber(mobileNo)) {
+ // context.showBottomSheet(
+ // child: ExceptionBottomSheet(
+ // message: TranslationBase.of(context).pleaseEnterValidMobile,
+ // showCancel: false,
+ // onOkPressed: () {
+ // Navigator.of(context).pop();
+ // },
+ // ),
+ // );
+ // } else {
+ // registerUser(1);
+ // }
+ Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => OTPVerificationPage(phoneNumber: '12234567')));
+ },
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedBorderColor,
+ textColor: AppColors.whiteColor,
+ icon: AppAssets.message,
+ ),
+ ),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Padding(
+ padding: EdgeInsets.symmetric(horizontal: 8.h),
+ child: LocaleKeys.oR.tr().toText16(color: AppColors.textColor),
+ ),
+ ],
+ ),
+ Padding(
+ padding: EdgeInsets.only(bottom: 10.h, top: 10.h),
+ child: CustomButton(
+ text: LocaleKeys.sendOTPWHATSAPP.tr(),
+ onPressed: () {
+ // if (mobileNo.isEmpty) {
+ // context.showBottomSheet(
+ // child: ExceptionBottomSheet(
+ // message: TranslationBase.of(context).pleaseEnterMobile,
+ // showCancel: false,
+ // onOkPressed: () {
+ // Navigator.of(context).pop();
+ // },
+ // ),
+ // );
+ // } else if (!Utils.validateMobileNumber(mobileNo)) {
+ // context.showBottomSheet(
+ // child: ExceptionBottomSheet(
+ // message: TranslationBase.of(context).pleaseEnterValidMobile,
+ // showCancel: false,
+ // onOkPressed: () {
+ // Navigator.of(context).pop();
+ // },
+ // ),
+ // );
+ // } else {
+ // registerUser(4);
+ // }
+ // int? val = Utils.onOtpBtnPressed(OTPType.whatsapp, mobileNo, context);
+ // registerUser(val);
+ },
+ backgroundColor: AppColors.whiteColor,
+ borderColor: AppColors.borderOnlyColor,
+ textColor: AppColors.textColor,
+ icon: AppAssets.whatsapp,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/authentication/register_step2.dart b/lib/presentation/authentication/register_step2.dart
new file mode 100644
index 0000000..483ce5c
--- /dev/null
+++ b/lib/presentation/authentication/register_step2.dart
@@ -0,0 +1,353 @@
+import 'dart:convert';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:get_it/get_it.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/enums.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/extensions/context_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
+import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart';
+import 'package:hmg_patient_app_new/widgets/input_widget.dart';
+import 'package:provider/provider.dart';
+
+class RegisterNewStep2 extends StatefulWidget {
+ var nHICData;
+ var payload;
+
+ RegisterNewStep2(this.nHICData, this.payload, {Key? key}) : super(key: key);
+
+ @override
+ _RegisterNew createState() => _RegisterNew();
+}
+
+class _RegisterNew extends State {
+ bool isFromDubai = true;
+ AuthenticationViewModel? authVM;
+
+ @override
+ void initState() {
+ super.initState();
+ authVM = context.read();
+ authVM!.loadCountriesData(context: context);
+ // isFromDubai = widget.payload.zipCode!.contains("971") || widget.payload.zipCode!.contains("+971");
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ authVM!.clearDefaults();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ AppState appState = getIt.get();
+ return Scaffold(
+ appBar: CustomAppBar(
+ onBackPressed: () {
+ Navigator.of(context).pop();
+ authVM!.clearDefaults();
+ },
+ onLanguageChanged: (lang) {},
+ hideLogoAndLang: true,
+ ),
+ body: SingleChildScrollView(
+ reverse: false,
+ padding: EdgeInsets.only(left: 24.h, right: 24.h, top: 24.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Directionality(
+ textDirection: Directionality.of(context),
+ child: Container(
+ decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(24)),
+ padding: EdgeInsets.only(left: 16.h, right: 16.h),
+ child: Column(
+ children: [
+ TextInputWidget(
+ labelText: isFromDubai ? LocaleKeys.fullName.tr() : LocaleKeys.name.tr(),
+ hintText: isFromDubai ? "name" ?? "" : (widget.nHICData!.firstNameEn!.toUpperCase() + " " + widget.nHICData!.lastNameEn!.toUpperCase()),
+ controller: null,
+ isEnable: true,
+ prefix: null,
+ isAllowRadius: false,
+ isBorderAllowed: false,
+ keyboardType: TextInputType.text,
+ isAllowLeadingIcon: true,
+ isReadOnly: isFromDubai ? false : true,
+ leadingIcon: AppAssets.user_circle)
+ .paddingSymmetrical(0.h, 16.h),
+ Divider(height: 1, color: AppColors.greyColor),
+ TextInputWidget(
+ labelText: LocaleKeys.nationalIdNumber.tr(),
+ hintText: isFromDubai ? "widget.payload.nationalID!" : (widget.nHICData!.idNumber ?? ""),
+ controller: null,
+ isEnable: true,
+ prefix: null,
+ isAllowRadius: false,
+ isBorderAllowed: false,
+ isAllowLeadingIcon: true,
+ isReadOnly: true,
+ leadingIcon: AppAssets.student_card)
+ .paddingSymmetrical(0.h, 16.h),
+ Divider(height: 1, color: AppColors.greyColor),
+ isFromDubai
+ ? Selector(
+ selector: (_, authViewModel) => authViewModel.genderType,
+ shouldRebuild: (previous, next) => previous != next,
+ builder: (context, genderType, child) {
+ final authVM = context.read();
+ return DropdownWidget(
+ labelText: LocaleKeys.gender.tr(),
+ hintText: LocaleKeys.malE.tr(),
+ isEnable: true,
+ dropdownItems: GenderTypeEnum.values.map((e) => appState!.isArabic() ? e.typeAr : e.type).toList(),
+ selectedValue: genderType != null ? (appState!.isArabic() ? genderType!.typeAr : genderType!.type) : "",
+ onChange: authVM.onGenderChange,
+ isBorderAllowed: false,
+ hasSelectionCustomIcon: true,
+ isAllowRadius: false,
+ padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
+ selectionCustomIcon: AppAssets.arrow_down,
+ leadingIcon: AppAssets.user_full,
+ ).withVerticalPadding(8);
+ })
+ : TextInputWidget(
+ labelText: LocaleKeys.gender.tr(),
+ hintText: (widget.nHICData!.gender ?? ""),
+ controller: null,
+ isEnable: true,
+ prefix: null,
+ isAllowRadius: false,
+ isBorderAllowed: false,
+ isAllowLeadingIcon: true,
+ isReadOnly: isFromDubai ? false : true,
+ leadingIcon: AppAssets.user_full,
+ onChange: (value) {})
+ .paddingSymmetrical(0.h, 16.h),
+ Divider(height: 1, color: AppColors.greyColor),
+ isFromDubai
+ ? Selector(
+ selector: (_, authViewModel) => authViewModel.maritalStatus,
+ shouldRebuild: (previous, next) => previous != next,
+ builder: (context, maritalStatus, child) {
+ final authVM = context.read(); // For onChange
+ return DropdownWidget(
+ labelText: LocaleKeys.maritalStatus.tr(),
+ hintText: LocaleKeys.married.tr(),
+ isEnable: true,
+ dropdownItems: MaritalStatusTypeEnum.values.map((e) => appState!.isArabic() ? e.typeAr : e.type).toList(),
+ selectedValue: maritalStatus != null ? (appState!.isArabic() ? maritalStatus.typeAr : maritalStatus.type) : "",
+ onChange: authVM.onMaritalStatusChange,
+ isBorderAllowed: false,
+ hasSelectionCustomIcon: true,
+ isAllowRadius: false,
+ padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
+ selectionCustomIcon: AppAssets.arrow_down,
+ leadingIcon: AppAssets.smart_phone,
+ ).withVerticalPadding(8);
+ },
+ )
+ : TextInputWidget(
+ labelText: LocaleKeys.maritalStatus.tr(),
+ hintText: appState!.isArabic()
+ ? (MaritalStatusTypeExtension.fromValue(widget.nHICData!.maritalStatusCode)!.typeAr)
+ : (MaritalStatusTypeExtension.fromValue(widget.nHICData!.maritalStatusCode)!.type),
+ isEnable: true,
+ prefix: null,
+ isAllowRadius: false,
+ isBorderAllowed: false,
+ isAllowLeadingIcon: true,
+ isReadOnly: true,
+ leadingIcon: AppAssets.smart_phone,
+ onChange: (value) {})
+ .paddingSymmetrical(0.h, 16.h),
+ Divider(height: 1, color: AppColors.greyColor),
+ isFromDubai
+ ? Selector? countriesList, NationalityCountries? selectedCountry, bool isArabic})>(
+ selector: (context, authViewModel) {
+ final appState = getIt.get();
+ return (countriesList: authViewModel.countriesList, selectedCountry: authViewModel.pickedCountryByUAEUser, isArabic: appState.isArabic());
+ },
+ shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic,
+ builder: (context, data, child) {
+ final authVM = context.read();
+ return DropdownWidget(
+ labelText: LocaleKeys.country.tr(),
+ hintText: LocaleKeys.uae.tr(),
+ isEnable: true,
+ dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(),
+ selectedValue: data.selectedCountry != null
+ ? data.isArabic
+ ? data.selectedCountry!.nameN ?? ""
+ : data.selectedCountry!.name ?? ""
+ : "",
+ onChange: authVM.onUAEUserCountrySelection,
+ isBorderAllowed: false,
+ hasSelectionCustomIcon: true,
+ isAllowRadius: false,
+ padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
+ selectionCustomIcon: AppAssets.arrow_down,
+ leadingIcon: AppAssets.globe,
+ ).withVerticalPadding(8);
+ },
+ )
+ : TextInputWidget(
+ labelText: LocaleKeys.nationality.tr(),
+ hintText: appState.isArabic()
+ ? (authVM!.countriesList!.firstWhere((e) => e.id == (widget.nHICData!.nationalityCode ?? ""), orElse: () => NationalityCountries()).nameN ?? "")
+ : (authVM!.countriesList!.firstWhere((e) => e.id == (widget.nHICData!.nationalityCode ?? ""), orElse: () => NationalityCountries()).name ?? ""),
+ isEnable: true,
+ prefix: null,
+ isAllowRadius: false,
+ isBorderAllowed: false,
+ isAllowLeadingIcon: true,
+ isReadOnly: true,
+ leadingIcon: AppAssets.globe,
+ onChange: (value) {})
+ .paddingSymmetrical(0.h, 16.h),
+ Divider(
+ height: 1,
+ color: AppColors.greyColor,
+ ),
+ TextInputWidget(
+ labelText: LocaleKeys.mobileNumber.tr(),
+ hintText: ("widget.payload.mobileNo" ?? ""),
+ controller: authVM!.phoneNumberController,
+ isEnable: true,
+ prefix: null,
+ isAllowRadius: false,
+ isBorderAllowed: false,
+ isAllowLeadingIcon: true,
+ isReadOnly: true,
+ leadingIcon: AppAssets.call,
+ onChange: (value) {})
+ .paddingSymmetrical(0.h, 16.h),
+ Divider(
+ height: 1,
+ color: AppColors.greyColor,
+ ),
+ TextInputWidget(
+ labelText: LocaleKeys.dob.tr(),
+ hintText: isFromDubai ? "widget.payload.dob!" : (widget.nHICData!.dateOfBirth ?? ""),
+ controller: authVM!.dobController,
+ isEnable: true,
+ prefix: null,
+ isBorderAllowed: false,
+ isAllowLeadingIcon: true,
+ isReadOnly: true,
+ leadingIcon: AppAssets.birthday_cake,
+ selectionType: SelectionTypeEnum.calendar,
+ ).paddingSymmetrical(0.h, 16.h),
+ ],
+ ),
+ ),
+ ),
+ SizedBox(height: 50.h),
+ Row(
+ children: [
+ Expanded(
+ child: CustomButton(
+ text: LocaleKeys.cancel.tr(),
+ icon: AppAssets.cancel,
+ onPressed: () {
+ Navigator.of(context).pop();
+ authVM!.clearDefaults();
+ },
+ backgroundColor: AppColors.secondaryLightRedColor,
+ borderColor: AppColors.secondaryLightRedColor,
+ textColor: AppColors.primaryRedColor,
+ iconColor: AppColors.primaryRedColor,
+ ),
+ ),
+ SizedBox(
+ width: 16,
+ ),
+ Expanded(
+ child: CustomButton(
+ backgroundColor: AppColors.lightGreenColor,
+ borderColor: AppColors.lightGreenColor,
+ textColor: AppColors.textGreenColor,
+ text: LocaleKeys.confirm.tr(),
+ icon: AppAssets.confirm,
+ iconColor: AppColors.textGreenColor,
+ onPressed: () {
+ // if (isFromDubai) {
+ // if (name == null) {
+ // AppToast.showErrorToast(message: LocaleKeys.enterFullName);
+ // return;
+ // }
+ // if (!name!.contains(" ")) if (selectedGenderType == null) {
+ // AppToast.showErrorToast(message: TranslationBase.of(context).enterFullName);
+ // return;
+ // }
+ // if (selectedMaritalStatusType == null) {
+ // AppToast.showErrorToast(message: TranslationBase.of(context).chooseMaritalStatus);
+ // return;
+ // }
+ // if (selectedCountry == null) {
+ // AppToast.showErrorToast(message: TranslationBase.of(context).chooseCountry);
+ // return;
+ // }
+ // }
+
+ showModel(context: context);
+ },
+ ),
+ )
+ ],
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ void showModel({required BuildContext context}) {
+ showModalBottomSheet(
+ context: context,
+ isScrollControlled: true,
+ isDismissible: false,
+ backgroundColor: Colors.transparent,
+ builder: (bottomSheetContext) => Padding(
+ padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom),
+ child: SingleChildScrollView(
+ child: GenericBottomSheet(
+ textController: TextEditingController(),
+ isForEmail: true,
+ buttons: [
+ Padding(
+ padding: const EdgeInsets.only(bottom: 10),
+ child: CustomButton(
+ text: LocaleKeys.submit,
+ onPressed: () {
+ // if (emailAddress.text.isEmpty) {
+ // Utils.showErrorToast(TranslationBase.of(context).enterEmailAddress);
+ // return;
+ // } else {
+ // Navigator.of(context).pop();
+ // registerNow();
+ // }
+ },
+ backgroundColor: AppColors.bgGreenColor,
+ borderColor: AppColors.bgGreenColor,
+ textColor: AppColors.whiteColor),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/authentication/saved_login_screen.dart b/lib/presentation/authentication/saved_login_screen.dart
new file mode 100644
index 0000000..da04c40
--- /dev/null
+++ b/lib/presentation/authentication/saved_login_screen.dart
@@ -0,0 +1,289 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/enums.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
+import 'package:hmg_patient_app_new/widgets/bottomsheet/generic_bottom_sheet.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+
+class SavedLogin extends StatefulWidget {
+ // final SelectDeviceIMEIRES savedLoginData;
+
+ // const SavedLogin(this.savedLoginData, {Key? key}) : super(key: key);
+ const SavedLogin();
+
+ @override
+ _SavedLogin createState() => _SavedLogin();
+}
+
+class _SavedLogin extends State {
+ LoginTypeEnum loginType = LoginTypeEnum.sms;
+
+ @override
+ void initState() {
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: CustomAppBar(
+ onBackPressed: () {},
+ onLanguageChanged: (lang) {},
+ ),
+ body: SafeArea(
+ child: Padding(
+ padding: EdgeInsets.symmetric(horizontal: 24.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ const Spacer(flex: 2),
+ // Welcome back text
+ LocaleKeys.welcomeBack.tr().toText16(color: AppColors.inputLabelTextColor),
+ SizedBox(height: 16.h),
+ ("widget.savedLoginData.name!.toLowerCase().capitalizeFirstofEach").toText26(isBold: true, height: 26 / 36, color: AppColors.textColor),
+ SizedBox(height: 24.h),
+ Container(
+ padding: EdgeInsets.all(16.h),
+ decoration: BoxDecoration(
+ color: AppColors.whiteColor,
+ border: Border.all(color: AppColors.whiteColor),
+ borderRadius: BorderRadius.circular(24.h),
+ boxShadow: [
+ BoxShadow(color: Color(0x0D000000), blurRadius: 16, offset: Offset(0, 0), spreadRadius: 5),
+ ],
+ ),
+ child: Column(
+ children: [
+ // Last login info
+ ('LocaleKeys.lastloginBy.tr()' + ' {getType(widget.savedLoginData.logInType!, context)}').toText14(isBold: true, color: AppColors.greyTextColor),
+ ('widget.savedLoginData.createdOn != null ? DateUtil.getFormattedDate(DateUtil.convertStringToDate(widget.savedLoginData.createdOn!), "d MMMM, y at HH:mm") : --')
+ .toText16(isBold: true, color: AppColors.textColor),
+
+ Container(margin: EdgeInsets.all(16.h), child: Utils.buildSvgWithAssets(icon: getTypeIcons(loginType.toInt), iconColor: loginType.toInt == 4 ? null : AppColors.primaryRedColor)),
+ // Face ID login button
+ Container(
+ height: 45,
+ child: CustomButton(
+ text: "${LocaleKeys.loginBy.tr()} ${loginType.displayName}",
+ onPressed: () {
+ if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) {
+ // loginWithFingerPrintFace(loginType.toInt, widget.savedLoginData.iMEI!);
+ } else {
+ int? val = loginType.toInt;
+ //checkUserAuthentication(val);
+ }
+ },
+ backgroundColor: Color(0xffED1C2B),
+ borderColor: Color(0xffFEE9EA),
+ textColor: Colors.white,
+ fontSize: 12,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
+ icon: AppAssets.apple_finder,
+ ),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 24),
+ Padding(
+ padding: EdgeInsets.symmetric(horizontal: 16.0),
+ child: Text(
+ LocaleKeys.oR.tr(),
+ style: context.dynamicTextStyle(fontSize: 16, fontWeight: FontWeight.w500),
+ ),
+ ),
+ const SizedBox(height: 24),
+ // OTP login button
+ loginType != null && loginType.toInt != 1
+ ? Column(
+ children: [
+ loginType.toInt != 1
+ ? CustomButton(
+ text: LocaleKeys.loginByOTP.tr(),
+ onPressed: () {
+ showModalBottomSheet(
+ context: context,
+ isScrollControlled: true,
+ isDismissible: false,
+ useSafeArea: true,
+ backgroundColor: Colors.transparent,
+ enableDrag: false,
+ // Prevent dragging to avoid focus conflicts
+ builder: (bottomSheetContext) => StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) {
+ return Padding(
+ padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom),
+ child: SingleChildScrollView(
+ child: GenericBottomSheet(
+ countryCode: "966",
+ initialPhoneNumber: "",
+ textController: TextEditingController(),
+ isFromSavedLogin: true,
+ isEnableCountryDropdown: true,
+ onCountryChange: (value) {},
+ onChange: (String? value) {},
+ buttons: [
+ Padding(
+ padding: EdgeInsets.only(bottom: 10.h),
+ child: CustomButton(
+ text: LocaleKeys.sendOTPSMS.tr(),
+ onPressed: () {
+ Navigator.of(context).pop();
+ loginType = LoginTypeEnum.sms;
+ int? val = loginType.toInt;
+ // checkUserAuthentication(val);
+ },
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedBorderColor,
+ textColor: AppColors.whiteColor,
+ icon: AppAssets.message,
+ ),
+ ),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Padding(padding: EdgeInsets.symmetric(horizontal: 8.h), child: (LocaleKeys.oR.tr()).toText16(color: AppColors.textColor)),
+ ],
+ ),
+ Padding(
+ padding: const EdgeInsets.only(bottom: 10, top: 10),
+ child: CustomButton(
+ text: LocaleKeys.sendOTPWHATSAPP.tr(),
+ onPressed: () {
+ Navigator.of(context).pop();
+ loginType = LoginTypeEnum.whatsapp;
+ int? val = loginType.toInt;
+ // checkUserAuthentication(val);
+ },
+ backgroundColor: AppColors.transparent,
+ borderColor: AppColors.textColor,
+ textColor: AppColors.textColor,
+ icon: AppAssets.whatsapp,
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }),
+ );
+ },
+ backgroundColor: AppColors.whiteColor,
+ borderColor: AppColors.borderOnlyColor,
+ textColor: AppColors.textColor,
+ borderWidth: 2,
+ padding: EdgeInsets.fromLTRB(0, 14, 0, 14),
+ icon: AppAssets.password_validation,
+ )
+ : Container(),
+ SizedBox(
+ height: 20,
+ ),
+ ],
+ )
+ : CustomButton(
+ text: "${LocaleKeys.loginBy.tr()} ${LoginTypeEnum.whatsapp.displayName}",
+ onPressed: () {
+ if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) {
+ // loginWithFingerPrintFace(loginType.toInt, "iMEI");
+ } else {
+ loginType = LoginTypeEnum.whatsapp;
+ int? val = loginType.toInt;
+ // checkUserAuthentication(val);
+ }
+ },
+ backgroundColor: AppColors.whiteColor,
+ borderColor: Color(0xFF2E3039),
+ textColor: Color(0xFF2E3039),
+ borderWidth: 2,
+ padding: EdgeInsets.fromLTRB(0, 14, 0, 14),
+ icon: AppAssets.whatsapp,
+ ),
+ const Spacer(flex: 2),
+ // OR divider
+
+ const SizedBox(height: 24),
+ // Guest and Switch account
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Container(
+ height: 56,
+ child: CustomButton(
+ text: LocaleKeys.guest.tr(),
+ onPressed: () {
+ Navigator.of(context).pushReplacement(
+ MaterialPageRoute(builder: (BuildContext context) => LoginScreen()),
+ );
+ },
+ backgroundColor: Color(0xffFEE9EA),
+ borderColor: Color(0xffFEE9EA),
+ textColor: Color(0xffED1C2B),
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
+ // icon: "assets/images/svg/apple-finder.svg",
+ ),
+ ),
+ ),
+ SizedBox(
+ width: MediaQuery.of(context).size.width * 0.05,
+ ),
+ Expanded(
+ child: Container(
+ height: 56,
+ child: CustomButton(
+ text: LocaleKeys.switchAccount.tr(),
+ onPressed: () {
+ Navigator.of(context).pushReplacement(
+ MaterialPageRoute(builder: (BuildContext context) => LoginScreen()),
+ );
+ },
+ backgroundColor: Color(0xffFEE9EA),
+ borderColor: Color(0xffFEE9EA),
+ textColor: Color(0xffED1C2B),
+ fontSize: 15,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
+ // icon: "assets/images/svg/apple-finder.svg",
+ ),
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 20),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ String getTypeIcons(int type) {
+ final types = {
+ 1: AppAssets.sms,
+ 2: AppAssets.fingerprint,
+ 3: AppAssets.fingerprint,
+ 4: AppAssets.whatsapp,
+ };
+
+ if (types.containsKey(type)) {
+ return types[type]!;
+ } else {
+ throw Exception('Invalid login type: $type');
+ }
+ }
+}
diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart
index 301e8bb..e863dab 100644
--- a/lib/presentation/home/landing_page.dart
+++ b/lib/presentation/home/landing_page.dart
@@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/habib_wallet_card.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart';
@@ -50,7 +51,8 @@ class _LandingPageState extends State {
CustomButton(
text: LocaleKeys.loginOrRegister.tr(context: context),
onPressed: () async {
- await authenticationViewModel.selectDeviceImei();
+ // await authenticationViewModel.selectDeviceImei();
+ Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => LoginScreen()));
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
@@ -179,10 +181,7 @@ class _LandingPageState extends State {
)
: Container(
height: 127.h,
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 24,
- ),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
diff --git a/lib/presentation/home/navigation_screen.dart b/lib/presentation/home/navigation_screen.dart
index f3e088b..152bbd2 100644
--- a/lib/presentation/home/navigation_screen.dart
+++ b/lib/presentation/home/navigation_screen.dart
@@ -20,22 +20,19 @@ class _LandingNavigationState extends State {
body: PageView(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
- children: const [
- LandingPage(),
+ children: [
+ const LandingPage(),
MedicalFilePage(),
- LandingPage(),
- LandingPage(),
- LandingPage(),
+ const LandingPage(),
+ const LandingPage(),
+ const LandingPage(),
],
),
bottomNavigationBar: BottomNavigation(
currentIndex: _currentIndex,
onTap: (index) {
setState(() => _currentIndex = index);
- _pageController.animateToPage(
- index,
- duration: const Duration(milliseconds: 300),
- curve: Curves.easeInOut);
+ _pageController.animateToPage(index, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
},
),
);
diff --git a/lib/presentation/home/widgets/small_service_card.dart b/lib/presentation/home/widgets/small_service_card.dart
index 297ed2b..f988e18 100644
--- a/lib/presentation/home/widgets/small_service_card.dart
+++ b/lib/presentation/home/widgets/small_service_card.dart
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
@@ -78,6 +79,11 @@ class SmallServiceCard extends StatelessWidget {
);
break;
case "insurance_update":
+ Navigator.of(context).push(
+ FadePage(
+ page: InsuranceHomePage(),
+ ),
+ );
break;
default:
// Handle unknown service
diff --git a/lib/presentation/insurance/insurance_home_page.dart b/lib/presentation/insurance/insurance_home_page.dart
new file mode 100644
index 0000000..9f9271a
--- /dev/null
+++ b/lib/presentation/insurance/insurance_home_page.dart
@@ -0,0 +1,90 @@
+import 'dart:async';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/insurance/widgets/patient_insurance_card.dart';
+import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
+import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
+import 'package:provider/provider.dart';
+
+import 'widgets/insurance_history.dart';
+
+class InsuranceHomePage extends StatefulWidget {
+ const InsuranceHomePage({super.key});
+
+ @override
+ State createState() => _InsuranceHomePageState();
+}
+
+class _InsuranceHomePageState extends State {
+ late InsuranceViewModel insuranceViewModel;
+
+ @override
+ void initState() {
+ scheduleMicrotask(() {
+ insuranceViewModel.initInsuranceProvider();
+ });
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ insuranceViewModel = Provider.of(context);
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ appBar: AppBar(
+ title: LocaleKeys.insurance.tr(context: context).toText18(),
+ backgroundColor: AppColors.bgScaffoldColor,
+ ),
+ body: SingleChildScrollView(
+ child: Consumer(builder: (context, insuranceVM, child) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ "${LocaleKeys.insurance.tr(context: context)} ${LocaleKeys.updateInsurance.tr(context: context)}".toText24(isBold: true),
+ CustomButton(
+ icon: AppAssets.insurance_history_icon,
+ iconColor: AppColors.primaryRedColor,
+ iconSize: 21.h,
+ text: LocaleKeys.history.tr(context: context),
+ onPressed: () {
+ insuranceVM.setIsInsuranceHistoryLoading(true);
+ showCommonBottomSheet(context,
+ child: InsuranceHistory(), callBackFunc: () {}, title: "", height: ResponsiveExtension.screenHeight * 0.5, isCloseButtonVisible: false, isFullScreen: false);
+ },
+ backgroundColor: AppColors.primaryRedColor.withOpacity(0.1),
+ borderColor: AppColors.primaryRedColor.withOpacity(0.0),
+ textColor: AppColors.primaryRedColor,
+ fontSize: 14,
+ fontWeight: FontWeight.w600,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 40.h,
+ ),
+ ],
+ ).paddingSymmetrical(24.h, 24.h),
+ insuranceVM.isInsuranceLoading
+ ? const MoviesShimmerWidget().paddingSymmetrical(24.h, 0)
+ : PatientInsuranceCard(
+ insuranceCardDetailsModel: insuranceVM.patientInsuranceList.first,
+ isInsuranceExpired: DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo))),
+ ],
+ );
+ }),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/insurance/widgets/insurance_history.dart b/lib/presentation/insurance/widgets/insurance_history.dart
new file mode 100644
index 0000000..0708c7c
--- /dev/null
+++ b/lib/presentation/insurance/widgets/insurance_history.dart
@@ -0,0 +1,39 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
+import 'package:provider/provider.dart';
+
+class InsuranceHistory extends StatelessWidget {
+ InsuranceHistory({super.key});
+
+ late InsuranceViewModel insuranceViewModel;
+
+ @override
+ Widget build(BuildContext context) {
+ insuranceViewModel = Provider.of(context);
+ return Consumer(builder: (context, insuranceVM, child) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ LocaleKeys.history.tr(context: context).toText24(isBold: true),
+ Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon).onPress(() {
+ Navigator.of(context).pop();
+ }),
+ ],
+ ).paddingSymmetrical(24.h, 24.h),
+ insuranceVM.isInsuranceHistoryLoading ? const MoviesShimmerWidget().paddingSymmetrical(24.h, 24.h) : Container()
+ ],
+ );
+ });
+ }
+}
diff --git a/lib/presentation/insurance/widgets/patient_insurance_card.dart b/lib/presentation/insurance/widgets/patient_insurance_card.dart
new file mode 100644
index 0000000..2701233
--- /dev/null
+++ b/lib/presentation/insurance/widgets/patient_insurance_card.dart
@@ -0,0 +1,133 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
+import 'package:hmg_patient_app_new/features/insurance/models/resp_models/patient_insurance_details_response_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:provider/provider.dart';
+
+class PatientInsuranceCard extends StatelessWidget {
+ PatientInsuranceCard({super.key, required this.insuranceCardDetailsModel, required this.isInsuranceExpired});
+
+ PatientInsuranceDetailsResponseModel insuranceCardDetailsModel;
+ bool isInsuranceExpired = false;
+
+ late InsuranceViewModel insuranceViewModel;
+
+ @override
+ Widget build(BuildContext context) {
+ insuranceViewModel = Provider.of(context);
+ return Container(
+ width: double.infinity,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "Haroon Amjad".toText18(isBold: true),
+ "Policy: ${insuranceCardDetailsModel.insurancePolicyNo}".toText12(isBold: true, color: AppColors.lightGrayColor),
+ ],
+ ),
+ CustomButton(
+ icon: isInsuranceExpired ? AppAssets.cancel_circle_icon : AppAssets.insurance_active_icon,
+ iconColor: isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor,
+ iconSize: 13.h,
+ text: isInsuranceExpired ? "Insurance Expired" : "Insurance Active",
+ onPressed: () {},
+ backgroundColor: isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.15) : AppColors.successColor.withOpacity(0.15),
+ borderColor: isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.01) : AppColors.successColor.withOpacity(0.01),
+ textColor: isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor,
+ fontSize: 10,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 30.h,
+ ),
+ ],
+ ),
+ SizedBox(height: 12.h),
+ insuranceCardDetailsModel.groupName!.toText12(isBold: true),
+ insuranceCardDetailsModel.companyName!.toText12(isBold: true),
+ SizedBox(height: 8.h),
+ Wrap(
+ direction: Axis.horizontal,
+ spacing: 6.h,
+ runSpacing: 6.h,
+ children: [
+ Row(
+ children: [
+ CustomButton(
+ icon: AppAssets.doctor_calendar_icon,
+ iconColor: AppColors.blackColor,
+ iconSize: 13.h,
+ text: "${LocaleKeys.expiryDate.tr(context: context)} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceCardDetailsModel.cardValidTo), false)}",
+ onPressed: () {},
+ backgroundColor: AppColors.greyColor,
+ borderColor: AppColors.greyColor,
+ textColor: AppColors.blackColor,
+ fontSize: 10,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 30.h,
+ ),
+ ],
+ ),
+ Row(
+ children: [
+ CustomButton(
+ text: "Patient Card ID: ${insuranceCardDetailsModel.patientCardID}",
+ onPressed: () {},
+ backgroundColor: AppColors.greyColor,
+ borderColor: AppColors.greyColor,
+ textColor: AppColors.blackColor,
+ fontSize: 10,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 30.h,
+ ),
+ ],
+ ),
+ ],
+ ),
+ SizedBox(height: 10.h),
+ isInsuranceExpired
+ ? CustomButton(
+ icon: AppAssets.update_insurance_card_icon,
+ iconColor: AppColors.successColor,
+ iconSize: 15.h,
+ text: "${LocaleKeys.updateInsurance.tr(context: context)} ${LocaleKeys.updateInsuranceSubtitle.tr(context: context)}",
+ onPressed: () {},
+ backgroundColor: AppColors.bgGreenColor.withOpacity(0.20),
+ borderColor: AppColors.bgGreenColor.withOpacity(0.0),
+ textColor: AppColors.bgGreenColor,
+ fontSize: 14,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 40.h,
+ )
+ : Container(),
+ ],
+ ),
+ ),
+ ).paddingSymmetrical(24.h, 0.h);
+ }
+}
diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart
index 8a0ce26..e829c93 100644
--- a/lib/presentation/lab/lab_orders_page.dart
+++ b/lib/presentation/lab/lab_orders_page.dart
@@ -5,14 +5,19 @@ import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+import 'package:hmg_patient_app_new/core/utils/size_config.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
+import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
import 'package:provider/provider.dart';
@@ -25,7 +30,7 @@ class LabOrdersPage extends StatefulWidget {
class _LabOrdersPageState extends State {
late LabViewModel labProvider;
-
+ List?> labSuggestions = [];
int? expandedIndex;
@override
@@ -57,7 +62,17 @@ class _LabOrdersPageState extends State {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.labResults.tr(context: context).toText24(isBold: true),
- Utils.buildSvgWithAssets(icon: AppAssets.search_icon),
+ Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(() {
+ if(model.isLabOrdersLoading){
+ return;
+ } else {
+ labSuggestions = getLabSuggestions(model);
+ showCommonBottomSheet(context, child: SearchLabResultsContent(),
+ callBackFunc: () {},
+ title: LocaleKeys.searchLabReport.tr(),
+ height: ResponsiveExtension.screenHeight,
+ isCloseButtonVisible: true);
+ } }),
],
),
SizedBox(height: 16.h),
@@ -262,4 +277,13 @@ class _LabOrdersPageState extends State {
return "";
}
}
+ getLabSuggestions(LabViewModel model) {
+ if(model.patientLabOrders.isEmpty){
+ return [];
+ }
+ return model.patientLabOrders.map((m) => m.testDetails).toList();
+
+
+ }
+
}
diff --git a/lib/presentation/lab/search_lab_report.dart b/lib/presentation/lab/search_lab_report.dart
new file mode 100644
index 0000000..1c05174
--- /dev/null
+++ b/lib/presentation/lab/search_lab_report.dart
@@ -0,0 +1,116 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_export.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:hmg_patient_app_new/widgets/input_widget.dart';
+import 'package:sizer/sizer.dart';
+
+class SearchLabResultsContent extends StatelessWidget {
+ const SearchLabResultsContent({super.key});
+
+ final List _chipLabels = const [
+ "Blood Test",
+ "X-Ray",
+ "MRI Scan",
+ "CT Scan",
+ "Ultrasound",
+ "Urine Test",
+ "Allergy Test",
+ "Cholesterol Test",
+ "Diabetes Test",
+ "Thyroid Test",
+ ];
+
+ @override
+ Widget build(BuildContext context) {
+
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ TextInputWidget(
+ labelText:"Search lab results",
+ hintText: "Type test name",
+ controller: TextEditingController(),
+ isEnable: true,
+ prefix: null,
+ autoFocus: true,
+ isBorderAllowed: false,
+ padding: EdgeInsets.symmetric(vertical:ResponsiveExtension(10).h, horizontal: ResponsiveExtension(15).h),
+
+ ),
+ SizedBox(height: ResponsiveExtension(20).h),
+ "Suggestions".toText16(isBold: true),
+ const SizedBox(height: 12),
+ ],
+ ),
+ ),
+ Expanded(
+ child: SingleChildScrollView(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: Wrap(
+ alignment: WrapAlignment.start,
+ spacing: 10,
+ runSpacing: 10,
+ children: _chipLabels
+ .map((label) => SuggestionChip(
+ label: label,
+ onTap: () {},
+ ))
+ .toList(),
+ ),
+ ),
+ ),
+ Container(
+ color: Colors.white,
+ padding: EdgeInsets.all(ResponsiveExtension(20).h),
+ child: CustomButton(
+ text: LocaleKeys.search.tr(),
+ icon: AppAssets.search_icon,
+ iconColor: Colors.white,
+ onPressed: () => Navigator.pop(context),
+ ),
+ ),
+ ],
+ );
+ }
+}
+
+class SuggestionChip extends StatelessWidget {
+ final String label;
+ final bool isSelected;
+ final VoidCallback? onTap;
+
+ const SuggestionChip({
+ super.key,
+ required this.label,
+ this.isSelected = false,
+ this.onTap,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ onTap: onTap, // optional tap callback
+ child: Container(
+ padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
+ decoration: BoxDecoration(
+ color: isSelected ? AppColors.primaryRedColor : AppColors.whiteColor,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: label.toText12(
+ color: isSelected ? Colors.white : Colors.black87,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart
index 703e0b7..36e4892 100644
--- a/lib/presentation/medical_file/medical_file_page.dart
+++ b/lib/presentation/medical_file/medical_file_page.dart
@@ -1,21 +1,29 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/insurance/widgets/patient_insurance_card.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
+import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
+import 'package:provider/provider.dart';
class MedicalFilePage extends StatelessWidget {
- const MedicalFilePage({super.key});
+ MedicalFilePage({super.key});
+
+ late InsuranceViewModel insuranceViewModel;
@override
Widget build(BuildContext context) {
+ insuranceViewModel = Provider.of(context);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
appBar: AppBar(
@@ -42,7 +50,6 @@ class MedicalFilePage extends StatelessWidget {
isAllowLeadingIcon: true,
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
leadingIcon: AppAssets.student_card,
- hasError: true,
),
SizedBox(height: 16.h),
Container(
@@ -175,101 +182,13 @@ class MedicalFilePage extends StatelessWidget {
),
SizedBox(height: 16.h),
//Insurance Tab Data
- Container(
- // height: 150.h,
- width: double.infinity,
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 24,
- ),
- child: Padding(
- padding: EdgeInsets.all(16.h),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- "Haroon Amjad".toText18(isBold: true),
- "Policy: 223123345".toText12(isBold: true, color: AppColors.lightGrayColor),
- ],
- ),
- CustomButton(
- icon: AppAssets.cross_circle,
- iconColor: AppColors.primaryRedColor,
- iconSize: 13.h,
- text: "Insurance Expired",
- onPressed: () {},
- backgroundColor: AppColors.primaryRedColor.withOpacity(0.1),
- borderColor: AppColors.primaryRedColor.withOpacity(0.0),
- textColor: AppColors.primaryRedColor,
- fontSize: 10,
- fontWeight: FontWeight.w500,
- borderRadius: 12,
- padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
- height: 30.h,
- ),
- ],
- ),
- SizedBox(height: 12.h),
- "NCCI".toText12(isBold: true),
- "NC_Dr Sulaiman Al Habib Medical Group".toText12(isBold: true),
- SizedBox(height: 8.h),
- Row(
- children: [
- CustomButton(
- icon: AppAssets.cross_circle,
- iconColor: AppColors.primaryRedColor,
- iconSize: 13.h,
- text: "Expiry: 18 Mar, 2025",
- onPressed: () {},
- backgroundColor: AppColors.primaryRedColor.withOpacity(0.1),
- borderColor: AppColors.primaryRedColor.withOpacity(0.0),
- textColor: AppColors.primaryRedColor,
- fontSize: 10,
- fontWeight: FontWeight.w500,
- borderRadius: 12,
- padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
- height: 30.h,
- ),
- SizedBox(width: 5.h),
- CustomButton(
- text: "Patient Card ID: 3628599",
- onPressed: () {},
- backgroundColor: AppColors.greyColor,
- borderColor: AppColors.greyColor,
- textColor: AppColors.blackColor,
- fontSize: 10,
- fontWeight: FontWeight.normal,
- borderRadius: 12,
- padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
- height: 30.h,
- ),
- ],
- ),
- SizedBox(height: 10.h),
- CustomButton(
- icon: AppAssets.cross_circle,
- iconColor: AppColors.primaryRedColor,
- iconSize: 13.h,
- text: "${LocaleKeys.updateInsurance.tr(context: context)} ${LocaleKeys.updateInsuranceSubtitle.tr(context: context)}",
- onPressed: () {},
- backgroundColor: AppColors.bgGreenColor.withOpacity(0.20),
- borderColor: AppColors.bgGreenColor.withOpacity(0.0),
- textColor: AppColors.bgGreenColor,
- fontSize: 14,
- fontWeight: FontWeight.w500,
- borderRadius: 12,
- padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
- height: 40.h,
- ),
- ],
- ),
- ),
- ),
+ Consumer(builder: (context, insuranceVM, child) {
+ return insuranceVM.isInsuranceLoading
+ ? const MoviesShimmerWidget()
+ : PatientInsuranceCard(
+ insuranceCardDetailsModel: insuranceVM.patientInsuranceList.first,
+ isInsuranceExpired: DateTime.now().isBefore(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo)));
+ }),
SizedBox(height: 10.h),
GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, crossAxisSpacing: 13, mainAxisSpacing: 13),
diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart
index 61938b0..5cb9ced 100644
--- a/lib/theme/colors.dart
+++ b/lib/theme/colors.dart
@@ -9,7 +9,6 @@ class AppColors {
static const buttonColor = Color(0xFF6A46F5);
static const splashBgColor = Color(0xFF3C355D);
- static const blackColor = Color(0xFF000000);
static const lightGray = Color(0xFFF4F5F7);
static const lightPurple = Color(0xFFB7A3E6);
static const scaffoldBgColor = Color(0xFFF8F8F8);
@@ -33,15 +32,23 @@ class AppColors {
static const Color borderOnlyColor = Color(0xFF2E3039);
static const Color dividerColor = Color(0xFFD2D2D2);
static const Color warningColorYellow = Color(0xFFF4A308);
+ static const Color blackBgColor = Color(0xFF2E3039);
+ static const blackColor = textColor;
+ static const inputLabelTextColor = Color(0xff898A8D);
+ static const greyTextColor = Color(0xFF8F9AA3);
+
+
+ static const lightGreenColor = Color(0xFF0ccedde);
+ static const textGreenColor = Color(0xFF18C273);
static const Color ratingColorYellow = Color(0xFFFFAF15);
//Chips
-static const Color successColor = Color(0xff18C273);
-static const Color errorColor = Color(0xFFED1C2B);
-static const Color alertColor = Color(0xFFD48D05);
-static const Color infoColor = Color(0xFF0B85F7);
-static const Color warningColor = Color(0xFFFFCC00);
-static const Color greyColor = Color(0xFFEFEFF0);
+ static const Color successColor = Color(0xff18C273);
+ static const Color errorColor = Color(0xFFED1C2B);
+ static const Color alertColor = Color(0xFFD48D05);
+ static const Color infoColor = Color(0xFF0B85F7);
+ static const Color warningColor = Color(0xFFFFCC00);
+ static const Color greyColor = Color(0xFFEFEFF0);
static const Color successLightColor = Color(0xFF18C273);
static const Color errorLightColor = Color(0xFFED1C2B);
diff --git a/lib/widgets/appbar/app_bar_widget.dart b/lib/widgets/appbar/app_bar_widget.dart
index dde02ed..4bc08a9 100644
--- a/lib/widgets/appbar/app_bar_widget.dart
+++ b/lib/widgets/appbar/app_bar_widget.dart
@@ -1,7 +1,9 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/widgets/language_switcher.dart';
import '../../generated/locale_keys.g.dart';
@@ -9,11 +11,13 @@ import '../../generated/locale_keys.g.dart';
class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
final VoidCallback onBackPressed;
final ValueChanged onLanguageChanged;
+ bool hideLogoAndLang;
- const CustomAppBar({
+ CustomAppBar({
Key? key,
required this.onBackPressed,
required this.onLanguageChanged,
+ this.hideLogoAndLang = false,
}) : super(key: key);
@override
@@ -24,45 +28,45 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
return AppBar(
backgroundColor: Colors.transparent,
leading: null,
- title: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- // Arrow Back with click handler
- Expanded(
- child: Align(
- alignment: Alignment.centerLeft,
- child: GestureDetector(
- onTap: onBackPressed,
- child: Utils.buildSvgWithAssets(
- icon: AppAssets.arrow_back,
- width: 32,
- height: 32,
+ automaticallyImplyLeading: false,
+ title: Padding(
+ padding: EdgeInsets.symmetric(horizontal: 10.h),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ Expanded(
+ child: Align(
+ alignment: Alignment.centerLeft,
+ child: GestureDetector(
+ onTap: onBackPressed,
+ child: Utils.buildSvgWithAssets(icon: AppAssets.arrow_back, width: 32.h, height: 32.h),
),
),
),
- ),
- // Logo
- Utils.buildSvgWithAssets(
- icon: AppAssets.habiblogo,
- ),
+ // Logo
+ if (!hideLogoAndLang)
+ Utils.buildSvgWithAssets(
+ icon: AppAssets.habiblogo,
+ ),
- // Language Selector
- Expanded(
- child: Align(
- alignment: Alignment.centerRight,
- child: LanguageSelector(
- currentLanguage: context.locale.languageCode,
- showOnlyIcon: false,
- onLanguageChanged: onLanguageChanged,
- languages: [
- {'code': 'ar', 'name': LocaleKeys.arabic.tr()},
- {'code': 'en', 'name': LocaleKeys.english.tr()}
- ],
+ if (!hideLogoAndLang)
+ Expanded(
+ child: Align(
+ alignment: Alignment.centerRight,
+ child: LanguageSelector(
+ currentLanguage: context.locale.languageCode,
+ showOnlyIcon: false,
+ onLanguageChanged: onLanguageChanged,
+ languages: [
+ {'code': 'ar', 'name': LocaleKeys.arabic.tr()},
+ {'code': 'en', 'name': LocaleKeys.english.tr()}
+ ],
+ ),
+ ),
),
- ),
- ),
- ],
+ ],
+ ),
),
centerTitle: true,
);
diff --git a/lib/widgets/bottomsheet/exception_bottom_sheet.dart b/lib/widgets/bottomsheet/exception_bottom_sheet.dart
new file mode 100644
index 0000000..f0ea651
--- /dev/null
+++ b/lib/widgets/bottomsheet/exception_bottom_sheet.dart
@@ -0,0 +1,111 @@
+import 'dart:io';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+
+class ExceptionBottomSheet extends StatelessWidget {
+ String message;
+ bool showOKButton;
+ bool showCancel;
+ Function() onOkPressed;
+ Function()? onCancelPressed;
+
+ ExceptionBottomSheet({Key? key, required this.message, this.showOKButton = true, this.showCancel = false, required this.onOkPressed, this.onCancelPressed}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return SafeArea(
+ bottom: Platform.isIOS ? false : true, // Adjust for iOS to avoid bottom padding
+ child: GestureDetector(
+ onTap: () {
+ FocusScope.of(context).unfocus(); // Dismiss the keyboard when tapping outside
+ },
+ child: Builder(builder: (context) {
+ return Directionality(
+ textDirection: Directionality.of(context),
+ child: Container(
+ padding: EdgeInsets.all(24.h),
+ decoration: BoxDecoration(
+ color: Color(0xFFF8F8FA),
+ borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ LocaleKeys.notice.tr().toText28(),
+ InkWell(
+ onTap: () {
+ Navigator.of(context).pop();
+ },
+ child: Utils.buildSvgWithAssets(icon: AppAssets.cross_circle),
+ )
+ ],
+ ),
+ SizedBox(height: 10.h),
+ (message ?? "").toText16(isBold: false, color: AppColors.textColor),
+ SizedBox(height: 10.h),
+ SizedBox(height: 24.h),
+ if (showOKButton && showCancel)
+ Row(
+ children: [
+ Expanded(
+ child: CustomButton(
+ text: LocaleKeys.cancel.tr(),
+ onPressed: onCancelPressed != null
+ ? onCancelPressed!
+ : () {
+ Navigator.of(context).pop();
+ },
+ backgroundColor: AppColors.secondaryLightRedColor,
+ borderColor: AppColors.secondaryLightRedColor,
+ textColor: AppColors.primaryRedColor,
+ icon: AppAssets.cancel,
+ iconColor: AppColors.primaryRedColor,
+ ),
+ ),
+ SizedBox(width: 5.h),
+ Expanded(
+ child: CustomButton(
+ text: showCancel ? LocaleKeys.confirm.tr() : LocaleKeys.ok.tr(),
+ onPressed: onOkPressed,
+ backgroundColor: AppColors.bgGreenColor,
+ borderColor: AppColors.bgGreenColor,
+ textColor: Colors.white,
+ icon: AppAssets.confirm,
+ ),
+ ),
+ ],
+ ),
+ if (showOKButton && !showCancel)
+ Padding(
+ padding: EdgeInsets.only(bottom: 10.h),
+ child: CustomButton(
+ text: LocaleKeys.ok.tr(),
+ onPressed: onOkPressed,
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedBorderColor,
+ textColor: Colors.white,
+ icon: AppAssets.confirm,
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }),
+ ),
+ );
+ }
+}
diff --git a/lib/widgets/bottomsheet/generic_bottom_sheet.dart b/lib/widgets/bottomsheet/generic_bottom_sheet.dart
new file mode 100644
index 0000000..08bf849
--- /dev/null
+++ b/lib/widgets/bottomsheet/generic_bottom_sheet.dart
@@ -0,0 +1,148 @@
+import 'dart:io';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/enums.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/input_widget.dart';
+
+class GenericBottomSheet extends StatefulWidget {
+ String? countryCode;
+ String? initialPhoneNumber;
+ final List buttons;
+ TextEditingController? textController;
+ final bool isForEmail;
+ Function(CountryEnum)? onCountryChange;
+ final bool isEnableCountryDropdown;
+ final bool isFromSavedLogin;
+ Function(String?)? onChange;
+
+ // FocusNode myFocusNode;
+
+ GenericBottomSheet({
+ this.countryCode = "",
+ this.initialPhoneNumber = "",
+ required this.buttons,
+ this.textController,
+ this.isForEmail = false,
+ this.onCountryChange,
+ this.isEnableCountryDropdown = false,
+ this.isFromSavedLogin = false,
+ this.onChange,
+ // required this.myFocusNode
+ });
+
+ @override
+ _GenericBottomSheetState createState() => _GenericBottomSheetState();
+}
+
+class _GenericBottomSheetState extends State {
+ @override
+ void initState() {
+ super.initState();
+
+ if (!widget.isForEmail && widget.textController != null) {
+ widget.textController!.text = widget.initialPhoneNumber!;
+ }
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return SafeArea(
+ top: false,
+ bottom: Platform.isIOS ? false : true,
+ child: GestureDetector(
+ onTap: () {
+ FocusScope.of(context).unfocus();
+ },
+ child: Directionality(
+ textDirection: Directionality.of(context),
+ child: Container(
+ padding: EdgeInsets.all(24.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.bgScaffoldColor, borderRadius: 16),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ // Title
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Flexible(
+ child: widget.isFromSavedLogin
+ ? LocaleKeys.receiveOtpToast.tr().toText24()
+ : widget.isForEmail
+ ? LocaleKeys.enterEmail.tr().toText24()
+ : LocaleKeys.enterPhoneNumber.tr().toText24()),
+ InkWell(
+ onTap: () {
+ Navigator.of(context).pop();
+ },
+ child: Padding(
+ padding: EdgeInsets.only(top: 10.h),
+ child: Utils.buildSvgWithAssets(icon: AppAssets.cross_circle),
+ ),
+ ),
+ ],
+ ),
+ SizedBox(height: 8.h),
+ // Subtitle
+ widget.isFromSavedLogin
+ ? LocaleKeys.pleaseChooseOption.tr().toText16()
+ : widget.isForEmail
+ ? LocaleKeys.enterEmailDesc.tr().toText16()
+ : LocaleKeys.enterPhoneDesc.tr().toText16(),
+
+ if (widget.isFromSavedLogin)
+ ...[]
+ else ...[
+ widget.textController != null
+ ? TextInputWidget(
+ labelText: widget.isForEmail ? LocaleKeys.email : LocaleKeys.phoneNumber,
+ hintText: widget.isForEmail ? "demo@gmail.com" : "5xxxxxxxx",
+ controller: widget.textController!,
+ padding: EdgeInsets.all(8.h),
+ keyboardType: widget.isForEmail ? TextInputType.emailAddress : TextInputType.number,
+ onChange: (value) {
+ if (widget.onChange != null) {
+ widget.onChange!(value);
+ }
+ },
+ onCountryChange: (value) {
+ if (widget.onCountryChange != null) {
+ widget.onCountryChange!(value);
+ }
+ },
+ isEnable: true,
+ isReadOnly: widget.isFromSavedLogin,
+ prefix: widget.isForEmail ? null : widget.countryCode,
+ isBorderAllowed: false,
+ isAllowLeadingIcon: true,
+ isCountryDropDown: widget.isEnableCountryDropdown,
+ leadingIcon: widget.isForEmail ? AppAssets.email : AppAssets.smart_phone,
+ )
+ : SizedBox(),
+ ],
+
+ SizedBox(height: 24.h),
+ ...widget.buttons,
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/widgets/common_bottom_sheet.dart b/lib/widgets/common_bottom_sheet.dart
new file mode 100644
index 0000000..f6039eb
--- /dev/null
+++ b/lib/widgets/common_bottom_sheet.dart
@@ -0,0 +1,88 @@
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_export.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+
+void showCommonBottomSheet(BuildContext context,
+ {required Widget child, required VoidCallback callBackFunc, String? title, required double height, bool isCloseButtonVisible = true, bool isFullScreen = true}) {
+ showModalBottomSheet(
+ sheetAnimationStyle: AnimationStyle(
+ duration: Duration(milliseconds: 500), // Custom animation duration
+ reverseDuration: Duration(milliseconds: 300), // Custom reverse animation duration
+ ),
+ context: context,
+ isScrollControlled: true,
+ showDragHandle: false,
+ backgroundColor: AppColors.scaffoldBgColor,
+ builder: (BuildContext context) {
+ return Container(
+ height: height,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.scaffoldBgColor, borderRadius: 24.h),
+ child: ButtonSheetContent(
+ title: title!,
+ isCloseButtonVisible: isCloseButtonVisible,
+ isFullScreen: isFullScreen,
+ child: child,
+ ),
+ );
+ }).then((value) {
+ callBackFunc();
+ });
+}
+
+class ButtonSheetContent extends StatelessWidget {
+ final Widget child;
+ final String title;
+ final bool isCloseButtonVisible;
+ final bool isFullScreen;
+
+ const ButtonSheetContent({super.key, required this.child, required this.isCloseButtonVisible, required this.title, required this.isFullScreen});
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // SizedBox(
+ // height: 20.h,
+ // ),
+ // Center(
+ // child: Container(
+ // margin: const EdgeInsets.only(top: 18, bottom: 12),
+ // height: 4,
+ // width: 40.h,
+ // decoration: BoxDecoration(
+ // color: Colors.grey[400],
+ // borderRadius: BorderRadius.circular(2),
+ // ),
+ // ),
+ // ),
+
+ // Close button
+ isCloseButtonVisible && isFullScreen
+ ? Padding(
+ padding: EdgeInsets.symmetric(horizontal: 16),
+ child: Utils.buildSvgWithAssets(icon: AppAssets.closeBottomNav, width: 32, height: 32).onPress(() {
+ Navigator.of(context).pop();
+ }),
+ )
+ : SizedBox(),
+
+ isFullScreen
+ ? Column(
+ children: [
+ SizedBox(height: 20.h),
+ Padding(padding: EdgeInsets.symmetric(horizontal: 16.h), child: title.toText24(isBold: true)),
+ SizedBox(height: 16.h),
+ ],
+ )
+ : SizedBox(),
+
+ Expanded(child: child)
+ ],
+ );
+ }
+}
diff --git a/lib/widgets/dropdown/country_dropdown_widget.dart b/lib/widgets/dropdown/country_dropdown_widget.dart
new file mode 100644
index 0000000..3d28e23
--- /dev/null
+++ b/lib/widgets/dropdown/country_dropdown_widget.dart
@@ -0,0 +1,210 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/enums.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+
+class CustomCountryDropdown extends StatefulWidget {
+ final List countryList;
+ final Function(CountryEnum)? onCountryChange;
+ final Function(String)? onPhoneNumberChanged;
+ final bool isRtl;
+ final bool isFromBottomSheet;
+ final bool isEnableTextField;
+ Widget? textField;
+
+ CustomCountryDropdown({
+ Key? key,
+ required this.countryList,
+ this.onCountryChange,
+ this.onPhoneNumberChanged,
+ required this.isRtl,
+ this.isFromBottomSheet = false,
+ this.isEnableTextField = false,
+ this.textField,
+ }) : super(key: key);
+
+ @override
+ _CustomCountryDropdownState createState() => _CustomCountryDropdownState();
+}
+
+class _CustomCountryDropdownState extends State {
+ CountryEnum? selectedCountry;
+ late OverlayEntry _overlayEntry;
+ bool _isDropdownOpen = false;
+ FocusNode textFocusNode = new FocusNode();
+
+ @override
+ void initState() {
+ super.initState();
+ selectedCountry = CountryEnum.saudiArabia;
+
+ if (widget.isEnableTextField && widget.isFromBottomSheet) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (mounted && textFocusNode.canRequestFocus) {
+ FocusScope.of(context).requestFocus(textFocusNode);
+ }
+ });
+ }
+ }
+
+ @override
+ void dispose() {
+ textFocusNode.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ height: 40.h,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h),
+ child: Row(
+ children: [
+ GestureDetector(
+ onTap: () {
+ if (_isDropdownOpen) {
+ _closeDropdown();
+ } else {
+ _openDropdown();
+ }
+ },
+ child: Utils.buildSvgWithAssets(icon: selectedCountry != null ? selectedCountry!.iconPath : AppAssets.ksa, width: 40.h, height: 40.h)),
+ SizedBox(width: 8.h),
+ Utils.buildSvgWithAssets(icon: _isDropdownOpen ? AppAssets.dropdow_icon : AppAssets.dropdow_icon),
+ SizedBox(width: 4.h),
+ if (widget.isFromBottomSheet)
+ GestureDetector(
+ onTap: () {
+ if (widget.isEnableTextField && textFocusNode.canRequestFocus) {
+ FocusScope.of(context).requestFocus(textFocusNode);
+ }
+ },
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ Text(
+ LocaleKeys.phoneNumber.tr(),
+ style: TextStyle(fontSize: 12.fSize, height: 21 / 12, fontWeight: FontWeight.w500, letterSpacing: -1),
+ ),
+ ],
+ ),
+ Row(
+ children: [
+ Text(
+ selectedCountry!.countryCode,
+ style: TextStyle(fontSize: 12.fSize, height: 21 / 18, fontWeight: FontWeight.w600, letterSpacing: -0.2),
+ ),
+ SizedBox(width: 4.h),
+ if (widget.isEnableTextField)
+ SizedBox(
+ height: 20,
+ width: 200,
+ child: TextField(
+ focusNode: textFocusNode,
+ decoration: InputDecoration(hintText: "", isDense: true, border: InputBorder.none),
+ keyboardType: TextInputType.phone,
+ onChanged: widget.onPhoneNumberChanged),
+ ),
+ ],
+ )
+ ],
+ ),
+ ),
+ if (!widget.isFromBottomSheet)
+ Text(
+ selectedCountry != null ? selectedCountry!.displayName : "Select Country",
+ style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2),
+ ),
+ ],
+ ),
+ );
+ }
+
+ void _openDropdown() {
+ if (textFocusNode.hasFocus) {
+ textFocusNode.unfocus();
+ }
+
+ RenderBox renderBox = context.findRenderObject() as RenderBox;
+ Offset offset = renderBox.localToGlobal(Offset.zero);
+
+ _overlayEntry = OverlayEntry(
+ builder: (context) => Stack(
+ children: [
+ // Dismiss dropdown when tapping outside
+ Positioned.fill(
+ child: GestureDetector(
+ onTap: _closeDropdown,
+ behavior: HitTestBehavior.translucent,
+ child: Container(),
+ ),
+ ),
+ Positioned(
+ top: offset.dy + renderBox.size.height,
+ left: widget.isRtl ? offset.dx + 6.h : offset.dx - 6.h,
+ width: renderBox.size.width,
+ child: Material(
+ child: Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.white, borderRadius: 12),
+ child: Column(
+ children: widget.countryList
+ .map(
+ (country) => GestureDetector(
+ onTap: () {
+ setState(() {
+ selectedCountry = country;
+ });
+ widget.onCountryChange?.call(country);
+ _closeDropdown();
+ },
+ child: Container(
+ padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 16.h),
+ child: Row(
+ children: [
+ Utils.buildSvgWithAssets(icon: country.iconPath, width: 38.h, height: 38.h),
+ if (!widget.isFromBottomSheet) SizedBox(width: 12.h),
+ if (!widget.isFromBottomSheet) Text(country.displayName, style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2)),
+ ],
+ ),
+ ),
+ ),
+ )
+ .toList(),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+
+ Overlay.of(context)?.insert(_overlayEntry);
+ setState(() {
+ _isDropdownOpen = true;
+ });
+ }
+
+ void _closeDropdown() {
+ _overlayEntry.remove();
+ setState(() {
+ _isDropdownOpen = false;
+ });
+
+ if (widget.isEnableTextField && widget.isFromBottomSheet) {
+ Future.delayed(Duration(milliseconds: 100), () {
+ if (mounted && textFocusNode.canRequestFocus) {
+ FocusScope.of(context).requestFocus(textFocusNode);
+ }
+ });
+ }
+ }
+}
diff --git a/lib/widgets/dropdown/dropdown_widget.dart b/lib/widgets/dropdown/dropdown_widget.dart
new file mode 100644
index 0000000..2a0cad3
--- /dev/null
+++ b/lib/widgets/dropdown/dropdown_widget.dart
@@ -0,0 +1,154 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart' show Icons, PopupMenuItem, showMenu, Colors;
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+
+class DropdownWidget extends StatelessWidget {
+ final String labelText;
+ final String hintText;
+ final List dropdownItems;
+ final String? selectedValue;
+ final Function(String?)? onChange;
+ final bool isEnable;
+ final bool isBorderAllowed;
+ final bool isAllowRadius;
+ final EdgeInsetsGeometry? padding;
+ final bool hasSelectionCustomIcon;
+ final String? selectionCustomIcon;
+ final String? leadingIcon;
+
+ const DropdownWidget({
+ Key? key,
+ required this.labelText,
+ required this.hintText,
+ required this.dropdownItems,
+ this.selectedValue,
+ this.onChange,
+ this.isEnable = true,
+ this.isBorderAllowed = true,
+ this.isAllowRadius = true,
+ this.padding,
+ this.hasSelectionCustomIcon = false,
+ this.selectionCustomIcon,
+ this.leadingIcon,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ Widget content = Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [_buildLabelText(), _buildDropdown(context)],
+ );
+
+ return Container(
+ padding: padding,
+ alignment: Alignment.center, // This might need adjustment based on layout
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: Colors.white,
+ borderRadius: isAllowRadius ? 15.h : null,
+ side: isBorderAllowed ? BorderSide(color: const Color(0xffefefef), width: 1) : null,
+ ),
+ child: Row(
+ // Wrap with a Row
+ crossAxisAlignment: CrossAxisAlignment.center, // Align items vertically in the center
+ children: [
+ if (leadingIcon != null) ...[
+ _buildLeadingIcon(),
+ SizedBox(width: 3.h),
+ ],
+ Expanded(child: content),
+ ],
+ ),
+ );
+ }
+
+ Widget _buildLeadingIcon() {
+ return Container(
+ height: 40.h,
+ width: 40.h,
+ margin: EdgeInsets.only(right: 10.h),
+ padding: EdgeInsets.all(8.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h, color: AppColors.greyColor),
+ child: Utils.buildSvgWithAssets(icon: leadingIcon!));
+ }
+
+ Widget _buildLabelText() {
+ return Text(
+ labelText,
+ style: TextStyle(
+ fontSize: 12.fSize,
+ fontWeight: FontWeight.w500,
+ color: Color(0xff898A8D),
+ letterSpacing: -0.2,
+ height: 18 / 12,
+ ),
+ );
+ }
+
+ Widget _buildDropdown(BuildContext context) {
+ return GestureDetector(
+ onTap: isEnable
+ ? () async {
+ final renderBox = context.findRenderObject() as RenderBox;
+ final offset = renderBox.localToGlobal(Offset.zero);
+ final selected = await showMenu(
+ context: context,
+ position: RelativeRect.fromLTRB(
+ offset.dx,
+ offset.dy + renderBox.size.height,
+ offset.dx + renderBox.size.width,
+ 0,
+ ),
+ items: dropdownItems
+ .map(
+ (e) => PopupMenuItem(
+ value: e,
+ child: Text(
+ e,
+ style: TextStyle(
+ fontSize: 14.fSize,
+ height: 21 / 14,
+ fontWeight: FontWeight.w500,
+ letterSpacing: -0.2,
+ ),
+ ),
+ ),
+ )
+ .toList(),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(12),
+ ),
+ );
+
+ if (selected != null && onChange != null) {
+ onChange!(selected);
+ }
+ }
+ : null,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Expanded(
+ child: Text(
+ (selectedValue == null || selectedValue!.isEmpty) ? hintText : selectedValue!,
+ textAlign: TextAlign.left,
+ textDirection: TextDirection.ltr,
+ style: TextStyle(
+ fontSize: 14.fSize,
+ height: 21 / 14,
+ fontWeight: FontWeight.w500,
+ color: (selectedValue != null && selectedValue!.isNotEmpty) ? const Color(0xff2E3039) : const Color(0xffB0B0B0),
+ letterSpacing: -0.2,
+ ),
+ ),
+ ),
+ if (hasSelectionCustomIcon && selectionCustomIcon != null) Utils.buildSvgWithAssets(icon: selectionCustomIcon!) else const Icon(Icons.keyboard_arrow_down_outlined),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/widgets/dropdown_widget.dart b/lib/widgets/dropdown_widget.dart
deleted file mode 100644
index a778f2e..0000000
--- a/lib/widgets/dropdown_widget.dart
+++ /dev/null
@@ -1,132 +0,0 @@
-import 'package:flutter/cupertino.dart';
-import 'package:flutter/material.dart' show Icons, PopupMenuItem, showMenu, Colors;
-import 'package:hmg_patient_app_new/core/utils/utils.dart';
-import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
-
-class DropdownWidget extends StatelessWidget {
- final String labelText;
- final String hintText;
- final List dropdownItems;
- final String? selectedValue;
- final Function(String?)? onChange;
- final bool isEnable;
- final bool isBorderAllowed;
- final bool isAllowRadius;
- final EdgeInsetsGeometry? padding;
- final bool hasSelectionCustomIcon;
- final String? selectionCustomIcon;
-
- const DropdownWidget({
- Key? key,
- required this.labelText,
- required this.hintText,
- required this.dropdownItems,
- this.selectedValue,
- this.onChange,
- this.isEnable = true,
- this.isBorderAllowed = true,
- this.isAllowRadius = true,
- this.padding,
- this.hasSelectionCustomIcon = false,
- this.selectionCustomIcon,
- }) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- return Container(
- padding: padding,
- alignment: Alignment.center,
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: Colors.white,
- borderRadius: isAllowRadius ? 15 : null,
- side: isBorderAllowed
- ? BorderSide(color: const Color(0xffefefef), width: 1)
- : null,
- ),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- _buildLabelText(),
- _buildDropdown(context),
- ],
- ),
- );
- }
-
- Widget _buildLabelText() {
- return Text(
- labelText,
- style: const TextStyle(
- fontSize: 12,
- fontWeight: FontWeight.w500,
- color: Color(0xff898A8D),
- letterSpacing: -0.2,
- height: 18 / 12,
- ),
- );
- }
-
- Widget _buildDropdown(BuildContext context) {
- return GestureDetector(
- onTap: isEnable
- ? () async {
- final renderBox = context.findRenderObject() as RenderBox;
- final offset = renderBox.localToGlobal(Offset.zero);
- final selected = await showMenu(
- context: context,
- position: RelativeRect.fromLTRB(
- offset.dx,
- offset.dy + renderBox.size.height,
- offset.dx + renderBox.size.width,
- 0,
- ),
- items: dropdownItems
- .map(
- (e) => PopupMenuItem(
- value: e,
- child: Text(e),
- ),
- )
- .toList(),
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(12),
- ),
- );
-
- if (selected != null && onChange != null) {
- onChange!(selected);
- }
- }
- : null,
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Expanded(
- child: Text(
- (selectedValue == null || selectedValue!.isEmpty)
- ? hintText
- : selectedValue!,
- textAlign: TextAlign.left,
- textDirection: TextDirection.ltr,
- style: TextStyle(
- fontSize: 14,
- height: 21 / 14,
- fontWeight: FontWeight.w500,
- color: (selectedValue != null && selectedValue!.isNotEmpty)
- ? const Color(0xff2E3039)
- : const Color(0xffB0B0B0),
- letterSpacing: -0.2,
- ),
- ),
- ),
- if (hasSelectionCustomIcon && selectionCustomIcon != null)
- Utils.buildSvgWithAssets(icon: selectionCustomIcon!)
- else
- const Icon(Icons.keyboard_arrow_down_outlined),
- ],
- ),
- );
- }
-}
\ No newline at end of file
diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart
index f2446a7..0f520c6 100644
--- a/lib/widgets/input_widget.dart
+++ b/lib/widgets/input_widget.dart
@@ -1,6 +1,15 @@
import 'package:flutter/material.dart';
+import 'package:hijri_gregorian_calendar/hijri_gregorian_calendar.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_export.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/dropdown/country_dropdown_widget.dart';
+
+import '../core/dependencies.dart';
// TODO: Import AppColors if bgRedColor is defined there
// import 'package:hmg_patient_app_new/core/ui_utils/app_colors.dart';
@@ -24,11 +33,13 @@ class TextInputWidget extends StatelessWidget {
final bool isCountryDropDown;
final bool hasError;
final String? errorMessage;
+ Function(CountryEnum)? onCountryChange;
+ SelectionTypeEnum? selectionType;
// final List countryList;
// final Function(Country)? onCountryChange;
- const TextInputWidget({
+ TextInputWidget({
Key? key,
required this.labelText,
required this.hintText,
@@ -48,15 +59,15 @@ class TextInputWidget extends StatelessWidget {
this.isCountryDropDown = false,
this.hasError = false,
this.errorMessage,
+ this.onCountryChange,
+ this.selectionType,
// this.countryList = const [],
// this.onCountryChange,
}) : super(key: key);
@override
Widget build(BuildContext context) {
- // Assuming AppColors.bgRedColor exists, otherwise using Colors.red
- final errorColor = Colors.red; // Replace with AppColors.bgRedColor if available
-
+ final errorColor = AppColors.primaryRedColor;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
@@ -66,34 +77,45 @@ class TextInputWidget extends StatelessWidget {
alignment: Alignment.center,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
- borderRadius: isAllowRadius ? 15 : null,
+ borderRadius: isAllowRadius ? 12 : null,
side: isBorderAllowed ? BorderSide(color: hasError ? errorColor : const Color(0xffefefef), width: 1) : null,
),
child: Row(
textDirection: Directionality.of(context),
children: [
- if (isAllowLeadingIcon && leadingIcon != null) _buildLeadingIcon(context),
- Expanded(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- _buildLabelText(),
- _buildTextField(context),
- ],
- ),
- ),
+ if (isAllowLeadingIcon && leadingIcon != null && !isCountryDropDown) _buildLeadingIcon(context),
+ isCountryDropDown
+ ? CustomCountryDropdown(
+ countryList: CountryEnum.values,
+ onCountryChange: onCountryChange,
+ isRtl: Directionality.of(context) == TextDirection.rtl,
+ isFromBottomSheet: isCountryDropDown,
+ isEnableTextField: true,
+ onPhoneNumberChanged: onChange,
+ // textField: _buildTextField(context),
+ )
+ : Expanded(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ _buildLabelText(),
+ _buildTextField(context),
+ ],
+ ),
+ ),
+ if (selectionType == SelectionTypeEnum.calendar) _buildTrailingIcon(context),
],
),
),
if (hasError && errorMessage != null)
Padding(
- padding: const EdgeInsets.only(top: 4.0, left: 12.0), // Adjust padding as needed
+ padding: EdgeInsets.only(top: 4.h, left: 12.h), // Adjust padding as needed
child: Text(
errorMessage!,
style: TextStyle(
color: errorColor,
- fontSize: 12,
+ fontSize: 12.fSize,
),
),
),
@@ -103,21 +125,56 @@ class TextInputWidget extends StatelessWidget {
Widget _buildLeadingIcon(BuildContext context) {
return Container(
- height: 40,
- width: 40,
- margin: const EdgeInsets.only(right: 10),
- padding: const EdgeInsets.all(8),
- decoration: const BoxDecoration(color: Color(0xFFEFEFF0), borderRadius: BorderRadius.all(Radius.circular(10))),
+ height: 40.h,
+ width: 40.h,
+ margin: EdgeInsets.only(right: 10.h),
+ padding: EdgeInsets.all(8.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ borderRadius: 10.h,
+ color: AppColors.greyColor,
+ ),
child: Utils.buildSvgWithAssets(icon: leadingIcon!));
}
+ Widget _buildTrailingIcon(BuildContext context) {
+ final AppState appState = getIt.get();
+ return Container(
+ height: 40.h,
+ width: 40.h,
+ margin: EdgeInsets.zero,
+ padding: EdgeInsets.all(8.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 10.h, color: AppColors.whiteColor),
+ child: GestureDetector(
+ onTap: () async {
+ bool isGregorian = true;
+ final picked = await showHijriGregBottomSheet(context,
+ switcherIcon: Utils.buildSvgWithAssets(icon: AppAssets.language, width: 24.h, height: 24.h),
+ language: appState.getLanguageCode()!,
+ initialDate: DateTime.now(),
+ okWidget: Padding(padding: EdgeInsets.only(right: 8.h), child: Utils.buildSvgWithAssets(icon: AppAssets.confirm, width: 24.h, height: 24.h)),
+ cancelWidget: Padding(padding: EdgeInsets.only(right: 8.h), child: Utils.buildSvgWithAssets(icon: AppAssets.cancel, iconColor: Colors.white, width: 24.h, height: 24.h)),
+ onCalendarTypeChanged: (bool value) {
+ isGregorian = value;
+ });
+ if (picked != null && onChange != null) {
+ // if (onCalendarTypeChanged != null) {
+ // onCalendarTypeChanged.call(isGregorian);
+ // }
+ onChange!(picked.toIso8601String());
+ }
+ },
+ child: Utils.buildSvgWithAssets(icon: AppAssets.calendar),
+ ),
+ );
+ }
+
Widget _buildLabelText() {
return Text(
labelText,
- style: const TextStyle(
- fontSize: 12,
+ style: TextStyle(
+ fontSize: 12.fSize,
fontWeight: FontWeight.w500,
- color: Color(0xff898A8D),
+ color: AppColors.inputLabelTextColor,
letterSpacing: -0.2,
height: 18 / 12,
),
@@ -137,30 +194,18 @@ class TextInputWidget extends StatelessWidget {
onChanged: onChange,
focusNode: focusNode,
autofocus: autoFocus,
- style: const TextStyle(
- fontSize: 14,
- height: 21 / 14,
- fontWeight: FontWeight.w500,
- color: Color(0xff2E3039),
- letterSpacing: -0.2,
- ),
+ style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, color: AppColors.textColor, letterSpacing: -0.2),
decoration: InputDecoration(
isDense: true,
hintText: hintText,
- hintStyle: const TextStyle(
- fontSize: 14,
- height: 21 / 16,
- fontWeight: FontWeight.w500,
- color: Color(0xff898A8D),
- letterSpacing: -0.2,
- ),
- prefixIconConstraints: const BoxConstraints(minWidth: 45),
+ hintStyle: TextStyle(fontSize: 14.fSize, height: 21 / 16, fontWeight: FontWeight.w500, color: Color(0xff898A8D), letterSpacing: -0.2),
+ prefixIconConstraints: BoxConstraints(minWidth: 45.h),
prefixIcon: prefix == null
? null
: Text(
"+" + prefix!,
- style: const TextStyle(
- fontSize: 14,
+ style: TextStyle(
+ fontSize: 14.fSize,
height: 21 / 14,
fontWeight: FontWeight.w500,
color: Color(0xff2E303A),
diff --git a/lib/widgets/language_switcher.dart b/lib/widgets/language_switcher.dart
index 4c621b7..4a81217 100644
--- a/lib/widgets/language_switcher.dart
+++ b/lib/widgets/language_switcher.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
@@ -51,18 +52,18 @@ class _LanguageSelectorState extends State {
widget.onLanguageChanged(newLanguage);
},
child: Container(
- padding: EdgeInsets.all(8),
+ padding: EdgeInsets.all(8.h),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Utils.buildSvgWithAssets(icon: AppAssets.language),
- const SizedBox(width: 6),
+ SizedBox(width: 6.h),
Text(
currentLangData['name']?.toUpperCase() ?? 'EN',
style: context.dynamicTextStyle(
fontWeight: FontWeight.w500,
- fontSize: 14,
+ fontSize: 14.fSize,
color: AppColors.primaryRedColor,
letterSpacing: 0.1,
isLanguageSwitcher: true,
diff --git a/lib/widgets/otp/otp.dart b/lib/widgets/otp/otp.dart
new file mode 100644
index 0000000..81c375b
--- /dev/null
+++ b/lib/widgets/otp/otp.dart
@@ -0,0 +1,230 @@
+import 'dart:async';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/presentation/authentication/register_step2.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
+
+class OTPVerificationPage extends StatefulWidget {
+ final String phoneNumber;
+
+ const OTPVerificationPage({Key? key, required this.phoneNumber}) : super(key: key);
+
+ @override
+ State createState() => _OTPVerificationPageState();
+}
+
+class _OTPVerificationPageState extends State {
+ final int _otpLength = 4;
+ late final List _controllers;
+ late final List _focusNodes;
+
+ Timer? _resendTimer;
+ int _resendTime = 60;
+ bool _isOtpComplete = false;
+
+ @override
+ void initState() {
+ super.initState();
+ _controllers = List.generate(_otpLength, (_) => TextEditingController());
+ _focusNodes = List.generate(_otpLength, (_) => FocusNode());
+ _startResendTimer();
+
+ // Focus the first field once the screen is built
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (_focusNodes.isNotEmpty) {
+ FocusScope.of(context).requestFocus(_focusNodes[0]);
+ }
+ });
+ }
+
+ @override
+ void dispose() {
+ for (final c in _controllers) c.dispose();
+ for (final f in _focusNodes) f.dispose();
+ _resendTimer?.cancel();
+ super.dispose();
+ }
+
+ void _startResendTimer() {
+ _resendTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
+ if (_resendTime > 0) {
+ setState(() => _resendTime--);
+ } else {
+ timer.cancel();
+ }
+ });
+ }
+
+ void _onOtpChanged(int index, String value) {
+ if (value.length == 1 && index < _otpLength - 1) {
+ _focusNodes[index + 1].requestFocus();
+ } else if (value.isEmpty && index > 0) {
+ _focusNodes[index - 1].requestFocus();
+ }
+ _checkOtpCompletion();
+ }
+
+ void _checkOtpCompletion() {
+ final isComplete = _controllers.every((c) => c.text.isNotEmpty);
+
+ if (isComplete != _isOtpComplete) {
+ setState(() => _isOtpComplete = isComplete);
+
+ if (isComplete) {
+ _verifyOtp();
+ }
+ }
+ }
+
+ void _resendOtp() {
+ if (_resendTime == 0) {
+ setState(() => _resendTime = 60);
+ _startResendTimer();
+ autoFillOtp("1234");
+
+ // call resend API here
+ }
+ }
+
+ String _getMaskedPhoneNumber() {
+ final phone = widget.phoneNumber;
+ return phone.length > 4 ? '05xxxxxx${phone.substring(phone.length - 2)}' : phone;
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: CustomAppBar(
+ hideLogoAndLang: true,
+ onBackPressed: () {
+ Navigator.of(context).pop();
+ },
+ onLanguageChanged: (lang) {},
+ ),
+ body: SingleChildScrollView(
+ child: Padding(
+ padding: EdgeInsets.symmetric(horizontal: 24.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(height: 40.h),
+ Text(
+ 'OTP Verification',
+ style: TextStyle(fontSize: 24.fSize, fontWeight: FontWeight.bold),
+ ),
+ SizedBox(height: 16.h),
+ Text(
+ 'We have sent you the OTP code on ${_getMaskedPhoneNumber()} via SMS for registration verification',
+ style: TextStyle(fontSize: 16.fSize, color: Colors.grey),
+ ),
+ SizedBox(height: 40.h),
+
+ // OTP Input Fields
+ SizedBox(
+ height: 100,
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
+ children: List.generate(_otpLength, (index) {
+ return ValueListenableBuilder(
+ valueListenable: _controllers[index],
+ builder: (context, value, _) {
+ final hasText = value.text.isNotEmpty;
+
+ return AnimatedContainer(
+ duration: const Duration(milliseconds: 200),
+ curve: Curves.easeInOut,
+ width: 70.h,
+ margin: EdgeInsets.symmetric(horizontal: 4.h),
+ decoration: RoundedRectangleBorder()
+ .toSmoothCornerDecoration(color: _isOtpComplete ? AppColors.successColor : (hasText ? AppColors.blackBgColor : AppColors.whiteColor), borderRadius: 16),
+ child: Center(
+ child: TextField(
+ controller: _controllers[index],
+ focusNode: _focusNodes[index],
+ textAlign: TextAlign.center,
+ keyboardType: TextInputType.number,
+ maxLength: 1,
+ style: TextStyle(
+ fontSize: 40.fSize,
+ fontWeight: FontWeight.bold,
+ color: AppColors.whiteColor,
+ ),
+ decoration: InputDecoration(
+ counterText: '',
+ filled: true,
+ fillColor: Colors.transparent,
+ border: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(18),
+ borderSide: BorderSide.none,
+ ),
+ ),
+ onChanged: (v) => _onOtpChanged(index, v),
+ ),
+ ),
+ );
+ },
+ );
+ }),
+ ),
+ ),
+
+ const SizedBox(height: 32),
+
+ // Resend OTP
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Text("Didn't receive it? "),
+ if (_resendTime > 0)
+ Text(
+ 'resend in (${_resendTime.toString().padLeft(2, '0')}:00). ',
+ style: const TextStyle(color: Colors.grey),
+ )
+ else
+ GestureDetector(
+ onTap: _resendOtp,
+ child: const Text(
+ 'Resend',
+ style: TextStyle(
+ color: AppColors.primaryRedColor,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+
+ void _verifyOtp() {
+ final otp = _controllers.map((c) => c.text).join();
+ debugPrint('Verifying OTP: $otp');
+
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text('Verifying OTP: $otp')),
+ );
+
+ Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) => RegisterNewStep2(null, {"nationalID": "12345678654321"})));
+ }
+
+ /// Auto fill OTP into text fields
+ void autoFillOtp(String otp) {
+ if (otp.length != _otpLength) return;
+
+ for (int i = 0; i < _otpLength; i++) {
+ _controllers[i].text = otp[i];
+ }
+
+ // Move focus to the last field
+ _focusNodes[_otpLength - 1].requestFocus();
+
+ // Trigger completion check and color update
+ _checkOtpCompletion();
+ }
+}
diff --git a/pubspec.lock b/pubspec.lock
index c503e1b..e2ebb3b 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -29,10 +29,10 @@ packages:
dependency: transitive
description:
name: async
- sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
+ sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev"
source: hosted
- version: "2.12.0"
+ version: "2.13.0"
audio_session:
dependency: transitive
description:
@@ -742,6 +742,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "13.1.3"
+ hijri_gregorian_calendar:
+ dependency: "direct main"
+ description:
+ name: hijri_gregorian_calendar
+ sha256: "9d23b52192783c1ad134b1ac001be7977342cb579c6b380647b6494fbd464d29"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.0.4"
html:
dependency: transitive
description:
@@ -1591,10 +1599,10 @@ packages:
dependency: transitive
description:
name: vm_service
- sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
+ sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev"
source: hosted
- version: "14.3.1"
+ version: "15.0.0"
web:
dependency: "direct main"
description:
@@ -1636,5 +1644,5 @@ packages:
source: hosted
version: "6.5.0"
sdks:
- dart: ">=3.8.0-0 <4.0.0"
+ dart: ">=3.8.1 <4.0.0"
flutter: ">=3.29.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 1263e33..cfb94d4 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -68,6 +68,7 @@ dependencies:
web: any
flutter_staggered_animations: ^1.1.1
smooth_corner: ^1.1.1
+ hijri_gregorian_calendar: ^0.0.4
dev_dependencies:
flutter_test:
@@ -81,6 +82,7 @@ flutter:
- assets/
- assets/fonts/
- assets/langs/
+ - assets/json/
- assets/images/
- assets/images/svg/
- assets/images/png/