From 84c718656b10467de924ca1046919e3514d23be2 Mon Sep 17 00:00:00 2001 From: Haroon Amjad <> Date: Thu, 23 Jan 2025 20:51:56 +0300 Subject: [PATCH] updates --- assets/images/HealthSnap/Frame.svg | 48 ++ assets/images/HealthSnap/Icon.svg | 3 + assets/images/HealthSnap/background.svg | 37 ++ .../HealthSnap/circle-arrow-doun-right.svg | 3 + .../HealthSnap/circle-arrow-down-02.svg | 3 + .../images/HealthSnap/circle-arrow-up 02.svg | 3 + .../HealthSnap/circle-arrow-up-right.svg | 3 + assets/images/HealthSnap/close_icon.svg | 4 + assets/images/HealthSnap/female.svg | 3 + assets/images/HealthSnap/male.svg | 3 + lib/pages/landing/home_page_2.dart | 259 ++++---- lib/pages/landing/landing_page.dart | 185 +++--- lib/theme/colors.dart | 2 +- lib/vital_signs/components/barWidget.dart | 457 ++++++++++++++ .../components/vital_sign_widget.dart | 582 ++++++++++-------- lib/vital_signs/result_screen.dart | 8 + lib/vital_signs/vital_sign.dart | 2 +- .../medical/LabResult/newUI/labWidgets.dart | 455 ++++++++++++++ pubspec.yaml | 1 + 19 files changed, 1602 insertions(+), 459 deletions(-) create mode 100644 assets/images/HealthSnap/Frame.svg create mode 100644 assets/images/HealthSnap/Icon.svg create mode 100644 assets/images/HealthSnap/background.svg create mode 100644 assets/images/HealthSnap/circle-arrow-doun-right.svg create mode 100644 assets/images/HealthSnap/circle-arrow-down-02.svg create mode 100644 assets/images/HealthSnap/circle-arrow-up 02.svg create mode 100644 assets/images/HealthSnap/circle-arrow-up-right.svg create mode 100644 assets/images/HealthSnap/close_icon.svg create mode 100644 assets/images/HealthSnap/female.svg create mode 100644 assets/images/HealthSnap/male.svg create mode 100644 lib/vital_signs/components/barWidget.dart create mode 100644 lib/widgets/data_display/medical/LabResult/newUI/labWidgets.dart diff --git a/assets/images/HealthSnap/Frame.svg b/assets/images/HealthSnap/Frame.svg new file mode 100644 index 00000000..985f06ce --- /dev/null +++ b/assets/images/HealthSnap/Frame.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/HealthSnap/Icon.svg b/assets/images/HealthSnap/Icon.svg new file mode 100644 index 00000000..aba5948c --- /dev/null +++ b/assets/images/HealthSnap/Icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/HealthSnap/background.svg b/assets/images/HealthSnap/background.svg new file mode 100644 index 00000000..2460ab54 --- /dev/null +++ b/assets/images/HealthSnap/background.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/HealthSnap/circle-arrow-doun-right.svg b/assets/images/HealthSnap/circle-arrow-doun-right.svg new file mode 100644 index 00000000..a2510857 --- /dev/null +++ b/assets/images/HealthSnap/circle-arrow-doun-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/HealthSnap/circle-arrow-down-02.svg b/assets/images/HealthSnap/circle-arrow-down-02.svg new file mode 100644 index 00000000..868edb1b --- /dev/null +++ b/assets/images/HealthSnap/circle-arrow-down-02.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/HealthSnap/circle-arrow-up 02.svg b/assets/images/HealthSnap/circle-arrow-up 02.svg new file mode 100644 index 00000000..d9ad2a6a --- /dev/null +++ b/assets/images/HealthSnap/circle-arrow-up 02.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/HealthSnap/circle-arrow-up-right.svg b/assets/images/HealthSnap/circle-arrow-up-right.svg new file mode 100644 index 00000000..4254f8b2 --- /dev/null +++ b/assets/images/HealthSnap/circle-arrow-up-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/HealthSnap/close_icon.svg b/assets/images/HealthSnap/close_icon.svg new file mode 100644 index 00000000..a36264b1 --- /dev/null +++ b/assets/images/HealthSnap/close_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/HealthSnap/female.svg b/assets/images/HealthSnap/female.svg new file mode 100644 index 00000000..c3e7c610 --- /dev/null +++ b/assets/images/HealthSnap/female.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/HealthSnap/male.svg b/assets/images/HealthSnap/male.svg new file mode 100644 index 00000000..bd895311 --- /dev/null +++ b/assets/images/HealthSnap/male.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/pages/landing/home_page_2.dart b/lib/pages/landing/home_page_2.dart index 61eaafc9..f4a0c69e 100644 --- a/lib/pages/landing/home_page_2.dart +++ b/lib/pages/landing/home_page_2.dart @@ -64,133 +64,160 @@ class _HomePageState2 extends State { // child: // SingleChildScrollView( // physics: BouncingScrollPhysics(), - body: SingleChildScrollView( - child: Material( - color: CustomColors.backgroudGreyColor, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - // crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SvgPicture.asset( - 'assets/images/new/cloud_logo.svg', - width: 150, - height: 150, - ), - mHeight(16.0), - Text( - "Contact-Less Vitals", - style: TextStyle(color: Colors.black, fontSize: 22, letterSpacing: -0.68, fontWeight: FontWeight.bold), - ), - mHeight(16.0), - Text( - "Select Gender:", - textAlign: TextAlign.start, - style: TextStyle(color: Colors.black, fontSize: 14, letterSpacing: -0.68, fontWeight: FontWeight.bold), - ), - mHeight(8.0), - Container( - padding: EdgeInsets.only(left: 6, right: 6), - child: Row( + body: SafeArea( + child: SingleChildScrollView( + child: Container( + height: MediaQuery.of(context).size.height, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topRight, + end: Alignment.bottomLeft, + // stops: [0.1, 0.3, 0.5], + colors: [ + Color(0xff2B35E), + Color(0xff596C7C), + ], + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + SvgPicture.asset( + 'assets/images/new/cloud_logo.svg', + width: 80, + height: 80, + ), + // mHeight(48.0), + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Flexible( + Text( + "Welcome To", + style: TextStyle(color: Color(0XFF2B353E), fontSize: 22, letterSpacing: 1.28, fontWeight: FontWeight.normal), + ), + Text( + "HealthSnap", + style: TextStyle(color: Color(0XFF2B353E), fontSize: 26, letterSpacing: 1.28, fontWeight: FontWeight.bold), + ), + mHeight(8.0), + Container( + padding: EdgeInsets.only(left: 6, right: 6), child: Row( children: [ - Radio( - value: TranslationBase.of(context).male, - groupValue: radioValue, - onChanged: (v) { - setState(() { - radioValue = v!; - }); - }, + Flexible( + child: Row( + children: [ + Radio( + value: TranslationBase.of(context).male, + groupValue: radioValue, + onChanged: (v) { + setState(() { + radioValue = v!; + }); + }, + ), + Text( + TranslationBase.of(context).male, + style: TextStyle( + fontSize: 12, + letterSpacing: -0.48, + fontWeight: FontWeight.w600, + ), + ), + ], + ), ), - Text( - TranslationBase.of(context).male, - style: TextStyle( - fontSize: 12, - letterSpacing: -0.48, - fontWeight: FontWeight.w600, + Flexible( + child: Row( + children: [ + Radio( + value: TranslationBase.of(context).female, + groupValue: radioValue, + onChanged: (v) { + setState(() { + radioValue = v!; + }); + }, + ), + Text( + TranslationBase.of(context).female, + style: TextStyle( + fontSize: 12, + letterSpacing: -0.48, + fontWeight: FontWeight.w600, + ), + ), + ], ), ), ], ), ), - Flexible( - child: Row( - children: [ - Radio( - value: TranslationBase.of(context).female, - groupValue: radioValue, - onChanged: (v) { - setState(() { - radioValue = v!; - }); + Row( + children: [ + Expanded( + child: inputWidget(TranslationBase.of(context).weightAdd + " (kg)", "Enter", weightTextController), + ), + mWidth(8), + Expanded( + child: inputWidget(TranslationBase.of(context).height + " (cm)", "Enter", heightTextController), + ), + ], + ), + mHeight(16.0), + inputWidget(TranslationBase.of(context).age, "Enter", ageTextController), + mHeight(16.0), + // inputWidget(TranslationBase.of(context).weightAdd + " (kg)", "Enter", weightTextController), + // mHeight(16.0), + // inputWidget(TranslationBase.of(context).height + " (cm)", "Enter", heightTextController), + // mHeight(16.0), + DefaultButton( + TranslationBase.of(context).start, + (ageTextController.text == "" || weightTextController.text == "" || heightTextController.text == "") + ? null + : () { + final UserInfo userInfo = UserInfo( + age: double.parse(ageTextController.text), + gender: radioValue == TranslationBase.of(context).male ? Gender.male : Gender.female, + weight: double.parse(weightTextController.text), + // kg, Optional + height: double.parse(heightTextController.text), + // cm, Optional + waistCircumference: 0, + // cm Optional + userId: 'dbd13e86-47f4-4a43-85f6-cf62fa750117'); + Navigator.push(context, FadePage(page: VitalSigns(userInfo: userInfo))); }, - ), - Text( - TranslationBase.of(context).female, - style: TextStyle( - fontSize: 12, - letterSpacing: -0.48, - fontWeight: FontWeight.w600, - ), - ), - ], - ), + color: Color(0XFF2B353E), + disabledColor: CustomColors.grey, ), ], ), - ), - mHeight(16.0), - inputWidget(TranslationBase.of(context).age, "0", ageTextController), - mHeight(16.0), - inputWidget(TranslationBase.of(context).weightAdd + " (kg)", "0", weightTextController), - mHeight(16.0), - inputWidget(TranslationBase.of(context).height + " (cm)", "0", heightTextController), - mHeight(16.0), - DefaultButton( - TranslationBase.of(context).start, - (ageTextController.text == "" || weightTextController.text == "" || heightTextController.text == "") - ? null - : () { - final UserInfo userInfo = UserInfo( - age: double.parse(ageTextController.text), - gender: radioValue == TranslationBase.of(context).male ? Gender.male : Gender.female, - weight: double.parse(weightTextController.text), - // kg, Optional - height: double.parse(heightTextController.text), - // cm, Optional - waistCircumference: 0, - // cm Optional - userId: 'dbd13e86-47f4-4a43-85f6-cf62fa750117'); - Navigator.push(context, FadePage(page: VitalSigns(userInfo: userInfo))); - }, - color: CustomColors.green, - disabledColor: CustomColors.grey, - ), - // HomePageFragment2( - // model, - // onLoginClick: () { - // widget.onLoginClick!(); - // // FirebaseCrashlytics.instance.crash(); - // // Navigator.pushNamed( - // // context, - // // "zoom_call_page", - // // arguments: CallArguments("h9qkpf02", "123", "Patient", "40", "1", false), - // // ); - // }, - // onPharmacyClick: () { - // // getPharmacyToken(model); - // Uri uri = Uri.parse(PHARMACY_REDIRECT_URL); - // launchUrl(uri, mode: LaunchMode.externalApplication); - // }, - // onMedicalFileClick: () { - // widget.onMedicalFileClick!(); - // }, - // ) - ], + // HomePageFragment2( + // model, + // onLoginClick: () { + // widget.onLoginClick!(); + // // FirebaseCrashlytics.instance.crash(); + // // Navigator.pushNamed( + // // context, + // // "zoom_call_page", + // // arguments: CallArguments("h9qkpf02", "123", "Patient", "40", "1", false), + // // ); + // }, + // onPharmacyClick: () { + // // getPharmacyToken(model); + // Uri uri = Uri.parse(PHARMACY_REDIRECT_URL); + // launchUrl(uri, mode: LaunchMode.externalApplication); + // }, + // onMedicalFileClick: () { + // widget.onMedicalFileClick!(); + // }, + // ) + ], + ), ), ), ), @@ -225,11 +252,11 @@ class _HomePageState2 extends State { Widget inputWidget(String _labelText, String _hintText, TextEditingController _controller, {String? prefix, bool isEnable = true, bool hasSelection = false}) { return Container( - padding: EdgeInsets.only(left: 16, right: 16, bottom: 15, top: 15), + padding: EdgeInsets.only(left: 16, right: 16, bottom: 10, top: 10), alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), - color: Colors.white, + color: CustomColors.appBackgroudGreyColor, border: Border.all( color: Color(0xffefefef), width: 1, diff --git a/lib/pages/landing/landing_page.dart b/lib/pages/landing/landing_page.dart index 618dc894..751dc5f6 100644 --- a/lib/pages/landing/landing_page.dart +++ b/lib/pages/landing/landing_page.dart @@ -479,96 +479,101 @@ class _LandingPageState extends State with WidgetsBindingObserver { model = Provider.of(context); // currentTab == 0 ? 45 : 0.0 return Scaffold( - appBar: currentTab == 0 - ? PreferredSize( - preferredSize: Size.fromHeight(currentTab == 0 ? 45 : 0.0), - child: AppBar( - elevation: 0, - backgroundColor: CustomColors.backgroudGreyColor, - //Changed By Aamir - // textTheme: TextTheme( - // headline6: TextStyle(color: Theme.of(context).textTheme.headline1!.color, fontWeight: FontWeight.bold), - // ), - title: Text( - getText(currentTab).toUpperCase(), - style: TextStyle( - fontWeight: FontWeight.bold, - color: currentTab == 0 ? CustomColors.backgroudGreyColor : CustomColors.accentColor, - // : Theme.of(context).textTheme.headline1!.color, - fontFamily: projectViewModel.isArabic ? 'Cairo' : 'Poppins'), - // bold: true, - ), - leading: Builder( - builder: (BuildContext context) { - return new Stack( - children: [ - currentTab == 0 - ? IconButton( - icon: SvgPicture.asset("assets/images/new/menu.svg"), - // color: Theme.of(context).textTheme.headline1!.color, - color: CustomColors.white, - onPressed: () => Scaffold.of(context).openDrawer(), - ) - : IconButton( - icon: Icon(Icons.arrow_back), - onPressed: () { - setState(() { - currentTab = 0; - }); - pageController.jumpToPage(0); - }, - ), - projectViewModel.isLogin && model.notificationsCount != null && !projectViewModel.isLoginChild - ? Positioned( - right: projectViewModel.isArabic ? 35 : 0, - top: 5, - child: new Container( - padding: EdgeInsets.all(4), - decoration: new BoxDecoration( - color: CustomColors.accentColor, - borderRadius: BorderRadius.circular(20), - ), - constraints: BoxConstraints( - minWidth: 20, - minHeight: 20, - ), - child: Text( - model.notificationsCount.toString(), - style: TextStyle( - color: Colors.white, - fontSize: projectViewModel.isArabic ? 8 : 9, - ), - textAlign: TextAlign.center, - ), - ), - ) - : SizedBox() - ], - ); - }, - ), - actions: [ - // IconButton( - // //iconSize: 70, - // icon: Icon( - // projectViewModel.isLogin ? Icons.settings : Icons.login, - // color: Theme.of(context).textTheme.headline1.color, - // ), - // onPressed: () { - // if (projectViewModel.isLogin) - // Navigator.of(context).pushNamed( - // SETTINGS, - // ); - // else - // login(); - // }, //do something, - // ) - ], - centerTitle: true, - ), - ) - : null, - drawer: SafeArea(child: AppDrawer()), + appBar: + // currentTab == 0 + // ? + // PreferredSize( + // preferredSize: Size.fromHeight(currentTab == 0 ? 45 : 0.0), + // child: AppBar( + // elevation: 0, + // backgroundColor: CustomColors.backgroudGreyColor, + // //Changed By Aamir + // // textTheme: TextTheme( + // // headline6: TextStyle(color: Theme.of(context).textTheme.headline1!.color, fontWeight: FontWeight.bold), + // // ), + // title: Text( + // getText(currentTab).toUpperCase(), + // style: TextStyle( + // fontWeight: FontWeight.bold, + // color: currentTab == 0 ? CustomColors.backgroudGreyColor : CustomColors.accentColor, + // // : Theme.of(context).textTheme.headline1!.color, + // fontFamily: projectViewModel.isArabic ? 'Cairo' : 'Poppins'), + // // bold: true, + // ), + // leading: Builder( + // builder: (BuildContext context) { + // return new Stack( + // children: [ + // // currentTab == 0 + // // ? + // // IconButton( + // // icon: SvgPicture.asset("assets/images/new/menu.svg"), + // // // color: Theme.of(context).textTheme.headline1!.color, + // // color: CustomColors.white, + // // onPressed: () => Scaffold.of(context).openDrawer(), + // // ) + // // : + // // IconButton( + // // icon: Icon(Icons.arrow_back), + // // onPressed: () { + // // setState(() { + // // currentTab = 0; + // // }); + // // pageController.jumpToPage(0); + // // }, + // // ), + // projectViewModel.isLogin && model.notificationsCount != null && !projectViewModel.isLoginChild + // ? Positioned( + // right: projectViewModel.isArabic ? 35 : 0, + // top: 5, + // child: new Container( + // padding: EdgeInsets.all(4), + // decoration: new BoxDecoration( + // color: CustomColors.accentColor, + // borderRadius: BorderRadius.circular(20), + // ), + // constraints: BoxConstraints( + // minWidth: 20, + // minHeight: 20, + // ), + // child: Text( + // model.notificationsCount.toString(), + // style: TextStyle( + // color: Colors.white, + // fontSize: projectViewModel.isArabic ? 8 : 9, + // ), + // textAlign: TextAlign.center, + // ), + // ), + // ) + // : SizedBox() + // ], + // ); + // }, + // ), + // actions: [ + // // IconButton( + // // //iconSize: 70, + // // icon: Icon( + // // projectViewModel.isLogin ? Icons.settings : Icons.login, + // // color: Theme.of(context).textTheme.headline1.color, + // // ), + // // onPressed: () { + // // if (projectViewModel.isLogin) + // // Navigator.of(context).pushNamed( + // // SETTINGS, + // // ); + // // else + // // login(); + // // }, //do something, + // // ) + // ], + // centerTitle: true, + // ), + // ) + // : + null, + // drawer: SafeArea(child: AppDrawer()), extendBody: false, body: WillPopScope( onWillPop: onWillPop, diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index a9d4e437..46d87856 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -18,7 +18,7 @@ class CustomColors { static const Color pharmacyGreyColor = Color(0xFFDBDBDB); static const Color backgroudGreyColor = Color(0xFFEFEFEF); static const Color appBackgroudGreyColor = Color(0xFFF7F7F7); - static const Color appBackgroudGrey2Color = Color(0xFFF8F8F8); + static const Color appBackgroudGrey2Color = Color(0xFFFAFAFA); static const Color green = Color(0xFF359846); static const Color orange = Color(0xFFCC9B14); static const Color darkOrange = Color(0xFFE16F17); diff --git a/lib/vital_signs/components/barWidget.dart b/lib/vital_signs/components/barWidget.dart new file mode 100644 index 00000000..6ef40422 --- /dev/null +++ b/lib/vital_signs/components/barWidget.dart @@ -0,0 +1,457 @@ +import 'dart:math'; + +import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; +import 'package:diplomaticquarterapp/uitl/utils.dart'; +import 'package:flutter/material.dart'; + +enum ResultTypes { criticalLow, low, normal, high, criticalHigh } + +class ItemResultCardWidget extends StatelessWidget { + final Widget child; + + const ItemResultCardWidget({ + Key? key, + required this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 12, right: 12, bottom: 12), + child: child, + ), + ), + ], + ); + } +} + +class CustomResultProgressBar extends StatelessWidget { + final double percentage; + final String value; + final ResultTypes type; + + const CustomResultProgressBar({Key? key, required this.percentage, required this.value, required this.type}) : super(key: key); + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, constraints) { + final double totalWidth = constraints.maxWidth; + final double spacing = 2; + final double tooltipPosition = (totalWidth * (percentage / 100)).clamp(0, totalWidth - 50); + return Stack( + children: [ + Row( + children: [ + _buildResultBar( + flex: 2, + color: Color(0xFFDE7676), + title: "Poor", + ), + SizedBox( + width: spacing, + ), + _buildResultBar( + flex: 3, + color: Color(0xff84d368), + title: "Good", + ), + SizedBox( + width: spacing, + ), + _buildResultBar( + flex: 2, + color: Color(0xFF14a45f), + title: "Excellent", + ), + SizedBox( + width: spacing, + ), + // _buildResultBar( + // flex: 2, + // color: Color(0xFFEAB157), + // title: "High", + // ), + // SizedBox( + // width: spacing, + // ), + // _buildResultBar( + // flex: 3, + // color: Color(0xFFDE7676), + // title: "Critical High", + // ), + ], + ), + Positioned( + left: tooltipPosition, + top: 33, + child: Column( + children: [ + TextCloud( + text: "${value}", + color: percentage == 4 ? Color(0xFFDE7676) : percentage == 40 ? Color(0xff84d368) : Color(0xFF14a45f), + // color: type == ResultTypes.criticalLow || type == ResultTypes.criticalHigh + // ? Color(0xFFDE7676) + // : type == ResultTypes.low || type == ResultTypes.high + // ? Color(0xFFEAB157) + // : type == ResultTypes.normal + // ? Color(0xFF09AA28) + // : Color(0xFF09AA28), + width: 50, + height: 20, + padding: EdgeInsets.zero, + axisDirection: AxisDirection.up, + ), + ], + ), + ), + ], + ); + }); + } + + Widget _buildResultBar({ + required int flex, + required Color color, + required String title, + }) { + return Expanded( + flex: flex, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + title, + style: const TextStyle(fontSize: 10, fontFamily: 'Poppins', letterSpacing: -0.24, fontWeight: FontWeight.w500), + textAlign: TextAlign.center, + ), + const SizedBox(height: 5), + Container( + height: 5, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.all(Radius.circular(10)), + ), + ), + ], + ), + ); + } +} + +class TextCloud extends StatelessWidget { + final String text; // write here box content + final Color color; // Set box Color + final EdgeInsets padding; // Set content padding + final double width; // Box width + final double height; // Box Height + final AxisDirection axisDirection; // Set triangle location up,left,right,down + final double locationOfArrow; // set between 0 and 1, If 0.5 is set triangle position will be centered + const TextCloud({ + super.key, + required this.text, + this.color = Colors.white, + this.padding = const EdgeInsets.all(10), + this.width = 200, + this.height = 100, + this.axisDirection = AxisDirection.down, + this.locationOfArrow = 0.5, + }); + + @override + Widget build(BuildContext context) { + Size arrowSize = const Size(12, 12); + return Stack( + clipBehavior: Clip.none, + children: [ + Container( + width: width, + height: height, + padding: padding, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(10), + ), + child: Center( + child: Text( + text, + style: TextStyle(color: Colors.white, fontSize: 12), + )), + ), + Builder(builder: (context) { + double angle = 0; + switch (axisDirection) { + case AxisDirection.left: + angle = pi * -0.5; + break; + case AxisDirection.up: + angle = pi * -2; + break; + case AxisDirection.right: + angle = pi * 0.5; + break; + case AxisDirection.down: + angle = pi; + break; + default: + angle = 0; + } + return Positioned( + left: axisDirection == AxisDirection.left + ? -arrowSize.width + 5 + : (axisDirection == AxisDirection.up || axisDirection == AxisDirection.down ? width * locationOfArrow - arrowSize.width / 2 : null), + right: axisDirection == AxisDirection.right ? -arrowSize.width + 5 : null, + top: axisDirection == AxisDirection.up + ? -arrowSize.width + 5 + : (axisDirection == AxisDirection.right || axisDirection == AxisDirection.left ? height * locationOfArrow - arrowSize.width / 2 : null), + bottom: axisDirection == AxisDirection.down ? -arrowSize.width + 5 : null, + child: Transform.rotate( + angle: angle, + child: CustomPaint( + size: arrowSize, + painter: ArrowPaint(color: color), + ), + ), + ); + }) + ], + ); + } +} + +class ArrowPaint extends CustomPainter { + final Color color; + + ArrowPaint({required this.color}); + + @override + void paint(Canvas canvas, Size size) { + Path path_0 = Path(); + path_0.moveTo(size.width * 0.5745375, size.height * 0.06573708); + path_0.lineTo(size.width * 0.9813667, size.height * 0.8794000); + path_0.cubicTo(size.width * 1.001950, size.height * 0.9205625, size.width * 0.9852625, size.height * 0.9706208, size.width * 0.9441000, size.height * 0.9912000); + path_0.cubicTo(size.width * 0.9325250, size.height * 0.9969875, size.width * 0.9197667, size.height, size.width * 0.9068292, size.height); + path_0.lineTo(size.width * 0.09316958, size.height); + path_0.cubicTo(size.width * 0.04714583, size.height, size.width * 0.009836208, size.height * 0.9626917, size.width * 0.009836208, size.height * 0.9166667); + path_0.cubicTo(size.width * 0.009836208, size.height * 0.9037292, size.width * 0.01284829, size.height * 0.8909708, size.width * 0.01863392, size.height * 0.8794000); + path_0.lineTo(size.width * 0.4254625, size.height * 0.06573708); + path_0.cubicTo(size.width * 0.4460458, size.height * 0.02457225, size.width * 0.4961042, size.height * 0.007886875, size.width * 0.5372667, size.height * 0.02846929); + path_0.cubicTo(size.width * 0.5533958, size.height * 0.03653296, size.width * 0.5664708, size.height * 0.04961000, size.width * 0.5745375, size.height * 0.06573708); + path_0.close(); + + Paint paint_0_fill = Paint()..style = PaintingStyle.fill; + paint_0_fill.color = color; + canvas.drawPath(path_0, paint_0_fill); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return true; + } +} + +class ItemResultCardWidgetWithParamsVitalSign extends StatelessWidget { + final String title; + final String subTitle; + final String note; + final String source; + final String referenceRange; + final double percentage; + final String buttonText; + final ResultTypes type; + final bool isArabic; + final Function()? onButtonPressed; + + const ItemResultCardWidgetWithParamsVitalSign({ + Key? key, + required this.title, + required this.subTitle, + required this.referenceRange, + required this.percentage, + required this.note, + required this.source, + required this.buttonText, + required this.type, + this.onButtonPressed, + this.isArabic = false, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final fontFamily = isArabic ? 'Cairo' : 'Poppins'; + + return Container( + // margin: const EdgeInsets.only(left: 0, right: 0, top: 11), + // decoration: BoxDecoration( + // borderRadius: const BorderRadius.all(Radius.circular(10)), + // border: Border( + // left: BorderSide( + // color: type == ResultTypes.criticalLow || type == ResultTypes.criticalHigh + // ? Color(0xFFDE7676) + // : type == ResultTypes.low || type == ResultTypes.high + // ? Color(0xFFEAB157) + // : type == ResultTypes.normal + // ? Color(0xFF09AA28) + // : Color(0xFF09AA28), + // width: MediaQuery.of(context).size.width * 0.015, + // ), + // ), + // color: Colors.white, + // boxShadow: [ + // BoxShadow( + // color: Colors.grey.withOpacity(0.2), + // spreadRadius: 2, + // blurRadius: 5, + // offset: const Offset(0, 3), + // ), + // ], + // ), + child: Padding( + padding: const EdgeInsets.all(0.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + // Title Row + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // title, + // style: TextStyle( + // fontSize: 16, + // fontFamily: fontFamily, + // fontWeight: FontWeight.w600, + // color: const Color(0xff2E303A), + // letterSpacing: -0.64, + // height: 16 / 25, + // ), + // ), + // GestureDetector( + // onTap: onButtonPressed, + // child: Utils.tableColumnValueWithUnderLine(TranslationBase.of(context).viewFlowChart, isLast: true, isCapitable: false), + // ), + // ], + // ), + // const SizedBox(height: 8), + // Row( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // ResultStatusWidget(type: type), + // SizedBox(width: 5), + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Text( + // subTitle, + // style: TextStyle(fontSize: 14, fontFamily: fontFamily, fontWeight: FontWeight.w600, color: const Color(0xff2E303A), letterSpacing: -0.56), + // ), + // Text( + // "Reference Range \n $referenceRange", + // style: TextStyle( + // fontSize: 10, + // fontFamily: fontFamily, + // fontWeight: FontWeight.w600, + // color: const Color(0xff2E303A), + // letterSpacing: -0.4, + // ), + // ), + // ], + // ), + // ], + // ), + // const SizedBox(height: 10), + // Progress Bar + SizedBox( + height: 55, + width: 220, + child: CustomResultProgressBar( + percentage: percentage, + value: subTitle, + type: type, + ), + ), + // const SizedBox(height: 12), + // Note Section + // if (note.isNotEmpty) + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // const Divider(color: Color(0xFFEFEFEF), thickness: 1), + // const SizedBox(height: 10), + // Text( + // "Notes:", + // style: TextStyle( + // fontSize: 12, + // color: const Color(0xFF2E303A), + // letterSpacing: -0.4, + // fontWeight: FontWeight.bold, + // ), + // ), + // Text( + // note, + // style: TextStyle( + // fontSize: 12, + // color: const Color(0xFF575757), + // letterSpacing: -0.4, + // fontWeight: FontWeight.bold, + // ), + // ), + // ], + // ), + const SizedBox(height: 3), + ], + ), + ), + ); + } +} + +class ResultStatusWidget extends StatelessWidget { + final ResultTypes type; + + const ResultStatusWidget({ + Key? key, + required this.type, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + Color? color; + IconData? icon; + + switch (type) { + case ResultTypes.criticalLow: + color = const Color(0xFFDE7676); + icon = Icons.arrow_circle_down; + break; + case ResultTypes.low: + color = const Color(0xFFEAB157); + icon = Icons.arrow_circle_down; + break; + case ResultTypes.criticalHigh: + color = const Color(0xFFDE7676); + icon = Icons.arrow_circle_up; + break; + case ResultTypes.high: + color = const Color(0xFFEAB157); + icon = Icons.arrow_circle_up; + break; + case ResultTypes.normal: + color = const Color(0xFF09AA28); + icon = Icons.check_circle; + break; + } + + // Return the icon if defined, otherwise a placeholder widget + return icon != null + ? Icon( + icon, + color: color, + ) + : const SizedBox(); // Use SizedBox to keep the layout consistent + } +} \ No newline at end of file diff --git a/lib/vital_signs/components/vital_sign_widget.dart b/lib/vital_signs/components/vital_sign_widget.dart index 4630ae26..3c007fe5 100644 --- a/lib/vital_signs/components/vital_sign_widget.dart +++ b/lib/vital_signs/components/vital_sign_widget.dart @@ -1,6 +1,7 @@ import 'package:diplomaticquarterapp/theme/colors.dart'; import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; import 'package:diplomaticquarterapp/uitl/utils_new.dart'; +import 'package:diplomaticquarterapp/vital_signs/components/barWidget.dart'; import 'package:diplomaticquarterapp/vital_signs/data/vitals_benchmark.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -13,11 +14,13 @@ class VitalSignWidget extends StatelessWidget { final Map? lookupValues; final Map? bloodPressureValues; final Color? textColor; + final String vitalName; const VitalSignWidget({ Key? key, required this.vitalSignName, required this.vitalSignValue, + required this.vitalName, this.textColor, required this.condition, required this.vectorUrl, @@ -27,268 +30,345 @@ class VitalSignWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - color: Colors.white, - elevation: 2, - shadowColor: CustomColors.lightGreyColor, - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - Row( - children: [ - SvgPicture.asset(vectorUrl, width: 36.0, height: 36.0), - SizedBox( - width: 16, + return Container( + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + color: Colors.white, + elevation: 2, + shadowColor: CustomColors.lightGreyColor, + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(8)), + border: Border( + left: BorderSide( + color: condition is RangeConditionWithTitle ? getColor(condition.current.interpretation) : getColor(condition.systolic?.systolic.interpretation), + // type == ResultTypes.criticalLow || type == ResultTypes.criticalHigh + // ? + // Color(0xFFDE7676), + // : type == ResultTypes.low || type == ResultTypes.high + // ? Color(0xFFEAB157) + // : type == ResultTypes.normal + // ? Color(0xFF09AA28) + // : Color(0xFF09AA28), + width: MediaQuery.of(context).size.width * 0.015, ), - Text( - vitalSignName, - textAlign: TextAlign.start, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - color: textColor ?? Colors.white, - ), + ), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.2), + spreadRadius: 2, + blurRadius: 5, + offset: const Offset(0, 3), ), ], ), - SizedBox( - height: 8, - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "$vitalSignValue ${condition.unit}", - style: TextStyle( - fontSize: 14, - color: textColor ?? Colors.white, - fontWeight: FontWeight.w500, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + vitalSignName, + textAlign: TextAlign.start, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: textColor ?? Colors.white, + ), ), ), - SizedBox( - width: 8, - ), - if (condition is RangeConditionWithTitle) - Status( - status: "${TranslationBase.of(context).getValue(condition.current?.interpretation ?? '')}", - originalData: condition.current?.interpretation ?? '', - ) - else if (condition is RangeConditionForBloodPressure) - Status( - status: "${TranslationBase.of(context).getValue(condition.systolic?.systolic.interpretation ?? '')}", - originalData: condition.systolic?.systolic.interpretation ?? '', - ) - // Text( - // "${TranslationBase.of(context).getValue(condition.current?.quantitativeInterpretation ?? '')}", - // style: TextStyle( - // fontSize: 14, - // color: textColor ?? Colors.white, - // fontWeight: FontWeight.w500, - // ), + // SizedBox( + // height: 8, // ), - ], - ), - SizedBox( - height: 8, - ), - // SizedBox( - // height: 30, - // child: Row(mainAxisSize: MainAxisSize.max, children: [ - if (condition is RangeConditionWithTitle) - SizedBox( - height: 30, - child: Row(mainAxisSize: MainAxisSize.max, children: [ - Expanded( - flex: 1, - child: ListView.separated( - separatorBuilder: (_, __) => SizedBox( - width: 8, - ), - scrollDirection: Axis.horizontal, - shrinkWrap: true, - itemCount: condition.condition?.length ?? 0, - itemBuilder: (_, index) => RangeWidget( - title: condition.condition?[index].title ?? 'test', - isSelected: condition.condition?[index].isInRange(vitalSignValue) == true, - originalData: condition.current?.interpretation ?? '', + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + transform: Matrix4.translationValues(0.0, -8.0, 0.0), + width: 80, + child: Text( + "$vitalSignValue ${condition.unit}", + textAlign: TextAlign.start, + style: TextStyle( + fontSize: 14, + color: CustomColors.grey, + fontWeight: FontWeight.bold, + ), ), ), - ), - ]), - ), - // Flexible( - // flex: 1, - // child: ListView.separated( - // physics: NeverScrollableScrollPhysics(), - // separatorBuilder: (_, __) => SizedBox( - // width: 8, - // ), - // shrinkWrap: true, - // itemCount: lookupValues?.length ?? 0, - // itemBuilder: (_, index) { - // var key = lookupValues?.keys.elementAt(index); - // return RangeWidget( - // title: key ?? '', - // values: lookupValues?[key] ?? [], - // isSelected: false, - // ); - // }), - // ), + // if (condition is RangeConditionWithTitle) + ItemResultCardWidgetWithParamsVitalSign( + title: "Creatinine", + subTitle: vitalSignValue, + referenceRange: "64-104", + // percentage: 4, + percentage: getPercentage(vitalName, vitalSignValue, condition is RangeConditionWithTitle ? condition.current.interpretation : condition.systolic?.systolic.interpretation), + note: "Mayoclinic is the source of pediatrics value", + source: "source", + buttonText: "Click", + type: ResultTypes.criticalLow) + // else if (condition is RangeConditionForBloodPressure) + // Status( + // status: "${TranslationBase.of(context).getValue(condition.systolic?.systolic.interpretation ?? '')}", + // originalData: condition.systolic?.systolic.interpretation ?? '', + // ) + // Text( + // "${TranslationBase.of(context).getValue(condition.current?.quantitativeInterpretation ?? '')}", + // style: TextStyle( + // fontSize: 14, + // color: textColor ?? Colors.white, + // fontWeight: FontWeight.w500, + // ), + // ), + ], + ), + // SizedBox( + // height: 8, + // ), + // SizedBox( + // height: 30, + // child: Row(mainAxisSize: MainAxisSize.max, children: [ - if (condition is RangeConditionForBloodPressure) - SizedBox( - height: 30, - child: Row(mainAxisSize: MainAxisSize.max, children: [ - Expanded( - flex: 1, - child: ListView.separated( - separatorBuilder: (_, __) => SizedBox( - width: 8, - ), - scrollDirection: Axis.horizontal, - shrinkWrap: true, - itemCount: condition.condition?.length ?? 0, - itemBuilder: (_, currentIndex) { - var title = condition.condition?[currentIndex].quotient ?? ''; - return RangeWidget( - title: title, - isSelected: condition.condition?[currentIndex]?.getValue("systolic-lng")?.isSelected == true, - originalData: condition.systolic?.systolic.interpretation ?? '', - ); - }), - ), - ]), - ), - // Flexible( - // flex: 1, - // child: ListView.separated( - // separatorBuilder: (_, __) => SizedBox( - // width: 8, - // ), - // physics: NeverScrollableScrollPhysics(), - // shrinkWrap: true, - // itemCount: bloodPressure.length ?? 0, - // itemBuilder: (_, index) { - // // return RangeWidget( - // // title: key??'', - // // values: lookupValues?[key]??[],isSelected: false, - // // ); - // return Column( - // children: [ - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Text( - // TranslationBase.of(context) - // .getValue(bloodPressure[index] ?? ''), - // textAlign: TextAlign.center, - // style: - // TextStyle(color: Colors.black, fontSize: 16), - // ), - // - // Status( - // status: - // "${TranslationBase.of(context).getValue(condition.getValue(bloodPressure[index])?.quantitativeInterpretation ?? '')}", - // originalData:condition.getValue(bloodPressure[index])?.quantitativeInterpretation ?? '' , - // ) - // ], - // ), - // SizedBox(height: 16), - // SizedBox( - // height: 30, - // child: Row( - // mainAxisSize: MainAxisSize.max, - // children: [ - // Expanded( - // flex: 1, - // child: ListView.separated( - // separatorBuilder: (_, __) => SizedBox( - // width: 8, - // ), - // scrollDirection: Axis.horizontal, - // shrinkWrap: true, - // itemCount: - // condition.condition?.length ?? 0, - // itemBuilder: (_, currentIndex) { - // var title = condition - // .condition?[currentIndex] - // .getValue( - // bloodPressure[index]).title ?? - // ''; - // return RangeWidget( - // title: title, - // isSelected: condition - // .condition?[currentIndex] - // ?.getValue( - // bloodPressure[index]) - // ?.isSelected == - // true, - // ); - // }), - // ), - // ]), - // ), - // SizedBox(height: 16), - // // SizedBox( - // // height:220, - // // child: ListView.separated( - // // separatorBuilder: (_, __) => SizedBox( - // // width: 8, - // // ), - // // physics: NeverScrollableScrollPhysics(), - // // shrinkWrap: true, - // // itemCount: bloodPressureValues?[key]?.length ?? 0, - // // itemBuilder: (_, index) { - // // var title = bloodPressureValues?[key]?.keys.elementAt(index); - // // - // // return RangeWidget( - // // title: title ?? '', - // // values: bloodPressureValues?[key]?[title] ?? [], - // // isSelected: false, - // // ); - // // })) - // ], - // ); - // }), - // ), - // ]), - // ), - SizedBox( - height: 8, + if (condition is RangeConditionWithTitle) + // SizedBox( + // height: 30, + // child: Row(mainAxisSize: MainAxisSize.max, children: [ + // Expanded( + // flex: 1, + // child: ListView.separated( + // separatorBuilder: (_, __) => SizedBox( + // width: 8, + // ), + // scrollDirection: Axis.horizontal, + // shrinkWrap: true, + // itemCount: condition.condition?.length ?? 0, + // itemBuilder: (_, index) => RangeWidget( + // title: condition.condition?[index].title ?? 'test', + // isSelected: condition.condition?[index].isInRange(vitalSignValue) == true, + // originalData: condition.current?.interpretation ?? '', + // ), + // ), + // ), + // ]), + // ), + + // ItemResultCardWidgetWithParamsVitalSign( + // title: "Creatinine", + // subTitle: vitalSignValue, + // referenceRange: "64-104", + // // percentage: 4, + // percentage: getPercentage(vitalName, vitalSignValue), + // note: "Mayoclinic is the source of pediatrics value", + // source: "source", + // buttonText: "Click", + // type: ResultTypes.criticalLow), + + // Flexible( + // flex: 1, + // child: ListView.separated( + // physics: NeverScrollableScrollPhysics(), + // separatorBuilder: (_, __) => SizedBox( + // width: 8, + // ), + // shrinkWrap: true, + // itemCount: lookupValues?.length ?? 0, + // itemBuilder: (_, index) { + // var key = lookupValues?.keys.elementAt(index); + // return RangeWidget( + // title: key ?? '', + // values: lookupValues?[key] ?? [], + // isSelected: false, + // ); + // }), + // ), + + if (condition is RangeConditionForBloodPressure) + SizedBox( + height: 30, + child: Row(mainAxisSize: MainAxisSize.max, children: [ + Expanded( + flex: 1, + child: ListView.separated( + separatorBuilder: (_, __) => SizedBox( + width: 8, + ), + scrollDirection: Axis.horizontal, + shrinkWrap: true, + itemCount: condition.condition?.length ?? 0, + itemBuilder: (_, currentIndex) { + var title = condition.condition?[currentIndex].quotient ?? ''; + return RangeWidget( + title: title, + isSelected: condition.condition?[currentIndex]?.getValue("systolic-lng")?.isSelected == true, + originalData: condition.systolic?.systolic.interpretation ?? '', + ); + }), + ), + ]), + ), + // Flexible( + // flex: 1, + // child: ListView.separated( + // separatorBuilder: (_, __) => SizedBox( + // width: 8, + // ), + // physics: NeverScrollableScrollPhysics(), + // shrinkWrap: true, + // itemCount: bloodPressure.length ?? 0, + // itemBuilder: (_, index) { + // // return RangeWidget( + // // title: key??'', + // // values: lookupValues?[key]??[],isSelected: false, + // // ); + // return Column( + // children: [ + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // TranslationBase.of(context) + // .getValue(bloodPressure[index] ?? ''), + // textAlign: TextAlign.center, + // style: + // TextStyle(color: Colors.black, fontSize: 16), + // ), + // + // Status( + // status: + // "${TranslationBase.of(context).getValue(condition.getValue(bloodPressure[index])?.quantitativeInterpretation ?? '')}", + // originalData:condition.getValue(bloodPressure[index])?.quantitativeInterpretation ?? '' , + // ) + // ], + // ), + // SizedBox(height: 16), + // SizedBox( + // height: 30, + // child: Row( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Expanded( + // flex: 1, + // child: ListView.separated( + // separatorBuilder: (_, __) => SizedBox( + // width: 8, + // ), + // scrollDirection: Axis.horizontal, + // shrinkWrap: true, + // itemCount: + // condition.condition?.length ?? 0, + // itemBuilder: (_, currentIndex) { + // var title = condition + // .condition?[currentIndex] + // .getValue( + // bloodPressure[index]).title ?? + // ''; + // return RangeWidget( + // title: title, + // isSelected: condition + // .condition?[currentIndex] + // ?.getValue( + // bloodPressure[index]) + // ?.isSelected == + // true, + // ); + // }), + // ), + // ]), + // ), + // SizedBox(height: 16), + // // SizedBox( + // // height:220, + // // child: ListView.separated( + // // separatorBuilder: (_, __) => SizedBox( + // // width: 8, + // // ), + // // physics: NeverScrollableScrollPhysics(), + // // shrinkWrap: true, + // // itemCount: bloodPressureValues?[key]?.length ?? 0, + // // itemBuilder: (_, index) { + // // var title = bloodPressureValues?[key]?.keys.elementAt(index); + // // + // // return RangeWidget( + // // title: title ?? '', + // // values: bloodPressureValues?[key]?[title] ?? [], + // // isSelected: false, + // // ); + // // })) + // ], + // ); + // }), + // ), + // ]), + // ), + // SizedBox( + // height: 8, + // ), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // TranslationBase.of(context).getValue( + // condition.condition?.first.interpretation ?? ''), + // style: TextStyle( + // fontSize: 14, + // color: textColor ?? Colors.white, + // fontWeight: FontWeight.w500, + // ), + // ), + // Text( + // TranslationBase.of(context).getValue( + // condition.condition?.last.interpretation ?? ''), + // style: TextStyle( + // fontSize: 14, + // color: textColor ?? Colors.white, + // fontWeight: FontWeight.w500, + // ), + // ), + // ], + // ), + // SizedBox( + // height: 8, + // ), + ]), ), - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // Text( - // TranslationBase.of(context).getValue( - // condition.condition?.first.interpretation ?? ''), - // style: TextStyle( - // fontSize: 14, - // color: textColor ?? Colors.white, - // fontWeight: FontWeight.w500, - // ), - // ), - // Text( - // TranslationBase.of(context).getValue( - // condition.condition?.last.interpretation ?? ''), - // style: TextStyle( - // fontSize: 14, - // color: textColor ?? Colors.white, - // fontWeight: FontWeight.w500, - // ), - // ), - // ], - // ), - // SizedBox( - // height: 8, - // ), - ]), + ), ), ); } + + double getPercentage(String vitalName, String value, String interpretationValue) { + double percent = 0; + + switch (interpretationValue) { + case "poor": + percent = 4; + case "good": + percent = 40; + case "excellent": + percent = 75; + } + + return percent; + } + + getColor(title) { + switch (title) { + case "excellent": + return Color(0xFF14a45f); + case "good": + return Color(0xff84d368); + case "poor": + return Color(0xFFDE7676); + case "atRisk": + return Color(0xFFa2494b); + } + } + } class RangeWidget extends StatelessWidget { @@ -325,11 +405,11 @@ class RangeWidget extends StatelessWidget { getColor(title) { switch (title) { case "excellent": - return Color(0xFF275b45); + return Color(0xFF14a45f); case "good": - return Color(0xFF507a3d); + return Color(0xff84d368); case "poor": - return Color(0xFFa86452); + return Color(0xFFDE7676); case "atRisk": return Color(0xFFa2494b); } diff --git a/lib/vital_signs/result_screen.dart b/lib/vital_signs/result_screen.dart index b5b17979..6178f233 100644 --- a/lib/vital_signs/result_screen.dart +++ b/lib/vital_signs/result_screen.dart @@ -64,6 +64,7 @@ class ResultScreen extends StatelessWidget { condition: RangeConditionWithTitle.fromJson(vital['generalWellness']!, formatValueToTwoDp(healthResult?.holisticHealth?.generalWellness)), vitalSignValue: formatValueToTwoDp(healthResult?.holisticHealth?.generalWellness), vectorUrl: 'assets/images/svg/wellness.svg', + vitalName: "generalWellness", ), //wellness if (_vitalSign?.stress != null) VitalSignWidget( @@ -73,6 +74,7 @@ class ResultScreen extends StatelessWidget { lookupValues: vitalValues['stressLevel']!, vitalSignValue: formatValueToTwoDp(healthResult?.vitalSigns.stress), vectorUrl: 'assets/images/svg/stress.svg', + vitalName: "stressLevel", ), VitalSignWidget( textColor: textColor, @@ -81,6 +83,7 @@ class ResultScreen extends StatelessWidget { lookupValues: vitalValues['HR']!, vitalSignValue: formatValueToTwoDp(healthResult?.vitalSigns.heartRate), vectorUrl: 'assets/images/svg/heartrate.svg', + vitalName: "HR", ), //heart rate if (_vitalSign?.bloodPressure != null) @@ -91,6 +94,7 @@ class ResultScreen extends StatelessWidget { bloodPressureValues: vitalValues['blood_pressure']!, vitalSignValue: healthResult?.vitalSigns.bloodPressure ?? "", vectorUrl: 'assets/images/svg/bloodpressure.svg', + vitalName: "blood_pressure", ), if (_vitalSign?.hrvSdnn != null) VitalSignWidget( @@ -99,12 +103,14 @@ class ResultScreen extends StatelessWidget { vitalSignName: "HRV SDNN", lookupValues: vitalValues['hrvv']!, condition: RangeConditionWithTitle.fromJson(vital['hrvv']!, formatValueToTwoDp(healthResult?.vitalSigns.hrvSdnn)), + vitalName: "hrvv", vitalSignValue: formatValueToTwoDp(healthResult?.vitalSigns.hrvSdnn)), //oxygen VitalSignWidget( textColor: textColor, lookupValues: vitalValues['Sp02']!, + vitalName: "Sp02", vitalSignName: TranslationBase.of(context).oxygenSaturation, condition: RangeConditionWithTitle.fromJson(vital['Sp02']!, formatValueToTwoDp(healthResult?.vitalSigns.spo2)), vitalSignValue: formatValueToTwoDp(healthResult?.vitalSigns.spo2), @@ -114,6 +120,7 @@ class ResultScreen extends StatelessWidget { VitalSignWidget( textColor: textColor, lookupValues: vitalValues['respiratoryRate']!, + vitalName: "respiratoryRate", vitalSignName: TranslationBase.of(context).respiratoryRate, condition: RangeConditionWithTitle.fromJson(vital['respiratoryRate']!, formatValueToTwoDp(healthResult?.vitalSigns.respiratoryRate)), vitalSignValue: formatValueToTwoDp(healthResult?.vitalSigns.respiratoryRate), @@ -123,6 +130,7 @@ class ResultScreen extends StatelessWidget { VitalSignWidget( textColor: textColor, lookupValues: vitalValues['bmi']!, + vitalName: "bmi", vitalSignName: TranslationBase.of(context).BMI, condition: RangeConditionWithTitle.fromJson(vital['bmi']!, formatValueToTwoDp(healthResult?.holisticHealth?.bmi)), vitalSignValue: formatValueToTwoDp(healthResult?.holisticHealth?.bmi), diff --git a/lib/vital_signs/vital_sign.dart b/lib/vital_signs/vital_sign.dart index 201c4506..da71ab15 100644 --- a/lib/vital_signs/vital_sign.dart +++ b/lib/vital_signs/vital_sign.dart @@ -12,7 +12,7 @@ import 'components/scan_condition_checklist.dart'; import 'components/scan_status.dart'; import 'components/start_button.dart'; import 'result_screen.dart'; -import 'package:lottie/lottie.dart'; +// import 'package:lottie/lottie.dart'; final UserInfo userInfo = UserInfo( age: 30, diff --git a/lib/widgets/data_display/medical/LabResult/newUI/labWidgets.dart b/lib/widgets/data_display/medical/LabResult/newUI/labWidgets.dart new file mode 100644 index 00000000..91023357 --- /dev/null +++ b/lib/widgets/data_display/medical/LabResult/newUI/labWidgets.dart @@ -0,0 +1,455 @@ +import 'dart:math'; + +import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; +import 'package:diplomaticquarterapp/uitl/utils.dart'; +import 'package:flutter/material.dart'; + +enum ResultTypes { criticalLow, low, normal, high, criticalHigh } + +class ItemResultCardWidget extends StatelessWidget { + final Widget child; + + const ItemResultCardWidget({ + Key? key, + required this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 12, right: 12, bottom: 12), + child: child, + ), + ), + ], + ); + } +} + +class CustomResultProgressBar extends StatelessWidget { + final double percentage; + final String value; + final ResultTypes type; + + const CustomResultProgressBar({Key? key, required this.percentage, required this.value, required this.type}) : super(key: key); + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, constraints) { + final double totalWidth = constraints.maxWidth; + final double spacing = 2; + final double tooltipPosition = (totalWidth * (percentage / 100)).clamp(0, totalWidth - 50); + return Stack( + children: [ + Row( + children: [ + _buildResultBar( + flex: 3, + color: Color(0xFFDE7676), + title: "Critical Low", + ), + SizedBox( + width: spacing, + ), + _buildResultBar( + flex: 2, + color: Color(0xFFEAB157), + title: "Low", + ), + SizedBox( + width: spacing, + ), + _buildResultBar( + flex: 5, + color: Color(0xFF09AA28), + title: "Normal", + ), + SizedBox( + width: spacing, + ), + _buildResultBar( + flex: 2, + color: Color(0xFFEAB157), + title: "High", + ), + SizedBox( + width: spacing, + ), + _buildResultBar( + flex: 3, + color: Color(0xFFDE7676), + title: "Critical High", + ), + ], + ), + Positioned( + left: tooltipPosition, + top: 33, + child: Column( + children: [ + TextCloud( + text: "${value}", + color: type == ResultTypes.criticalLow || type == ResultTypes.criticalHigh + ? Color(0xFFDE7676) + : type == ResultTypes.low || type == ResultTypes.high + ? Color(0xFFEAB157) + : type == ResultTypes.normal + ? Color(0xFF09AA28) + : Color(0xFF09AA28), + width: 60, + height: 20, + padding: EdgeInsets.zero, + axisDirection: AxisDirection.up, + ), + ], + ), + ), + ], + ); + }); + } + + Widget _buildResultBar({ + required int flex, + required Color color, + required String title, + }) { + return Expanded( + flex: flex, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + title, + style: const TextStyle(fontSize: 10, fontFamily: 'Poppins', letterSpacing: -0.24, fontWeight: FontWeight.w500), + textAlign: TextAlign.center, + ), + const SizedBox(height: 5), + Container( + height: 5, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.all(Radius.circular(10)), + ), + ), + ], + ), + ); + } +} + +class TextCloud extends StatelessWidget { + final String text; // write here box content + final Color color; // Set box Color + final EdgeInsets padding; // Set content padding + final double width; // Box width + final double height; // Box Height + final AxisDirection axisDirection; // Set triangle location up,left,right,down + final double locationOfArrow; // set between 0 and 1, If 0.5 is set triangle position will be centered + const TextCloud({ + super.key, + required this.text, + this.color = Colors.white, + this.padding = const EdgeInsets.all(10), + this.width = 200, + this.height = 100, + this.axisDirection = AxisDirection.down, + this.locationOfArrow = 0.5, + }); + + @override + Widget build(BuildContext context) { + Size arrowSize = const Size(12, 12); + return Stack( + clipBehavior: Clip.none, + children: [ + Container( + width: width, + height: height, + padding: padding, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(10), + ), + child: Center( + child: Text( + text, + style: TextStyle(color: Colors.white), + )), + ), + Builder(builder: (context) { + double angle = 0; + switch (axisDirection) { + case AxisDirection.left: + angle = pi * -0.5; + break; + case AxisDirection.up: + angle = pi * -2; + break; + case AxisDirection.right: + angle = pi * 0.5; + break; + case AxisDirection.down: + angle = pi; + break; + default: + angle = 0; + } + return Positioned( + left: axisDirection == AxisDirection.left + ? -arrowSize.width + 5 + : (axisDirection == AxisDirection.up || axisDirection == AxisDirection.down ? width * locationOfArrow - arrowSize.width / 2 : null), + right: axisDirection == AxisDirection.right ? -arrowSize.width + 5 : null, + top: axisDirection == AxisDirection.up + ? -arrowSize.width + 5 + : (axisDirection == AxisDirection.right || axisDirection == AxisDirection.left ? height * locationOfArrow - arrowSize.width / 2 : null), + bottom: axisDirection == AxisDirection.down ? -arrowSize.width + 5 : null, + child: Transform.rotate( + angle: angle, + child: CustomPaint( + size: arrowSize, + painter: ArrowPaint(color: color), + ), + ), + ); + }) + ], + ); + } +} + +class ArrowPaint extends CustomPainter { + final Color color; + + ArrowPaint({required this.color}); + + @override + void paint(Canvas canvas, Size size) { + Path path_0 = Path(); + path_0.moveTo(size.width * 0.5745375, size.height * 0.06573708); + path_0.lineTo(size.width * 0.9813667, size.height * 0.8794000); + path_0.cubicTo(size.width * 1.001950, size.height * 0.9205625, size.width * 0.9852625, size.height * 0.9706208, size.width * 0.9441000, size.height * 0.9912000); + path_0.cubicTo(size.width * 0.9325250, size.height * 0.9969875, size.width * 0.9197667, size.height, size.width * 0.9068292, size.height); + path_0.lineTo(size.width * 0.09316958, size.height); + path_0.cubicTo(size.width * 0.04714583, size.height, size.width * 0.009836208, size.height * 0.9626917, size.width * 0.009836208, size.height * 0.9166667); + path_0.cubicTo(size.width * 0.009836208, size.height * 0.9037292, size.width * 0.01284829, size.height * 0.8909708, size.width * 0.01863392, size.height * 0.8794000); + path_0.lineTo(size.width * 0.4254625, size.height * 0.06573708); + path_0.cubicTo(size.width * 0.4460458, size.height * 0.02457225, size.width * 0.4961042, size.height * 0.007886875, size.width * 0.5372667, size.height * 0.02846929); + path_0.cubicTo(size.width * 0.5533958, size.height * 0.03653296, size.width * 0.5664708, size.height * 0.04961000, size.width * 0.5745375, size.height * 0.06573708); + path_0.close(); + + Paint paint_0_fill = Paint()..style = PaintingStyle.fill; + paint_0_fill.color = color; + canvas.drawPath(path_0, paint_0_fill); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return true; + } +} + +class ItemResultCardWidgetWithParams extends StatelessWidget { + final String title; + final String subTitle; + final String note; + final String source; + final String referenceRange; + final double percentage; + final String buttonText; + final ResultTypes type; + final bool isArabic; + final Function()? onButtonPressed; + + const ItemResultCardWidgetWithParams({ + Key? key, + required this.title, + required this.subTitle, + required this.referenceRange, + required this.percentage, + required this.note, + required this.source, + required this.buttonText, + required this.type, + this.onButtonPressed, + this.isArabic = false, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final fontFamily = isArabic ? 'Cairo' : 'Poppins'; + + return Container( + margin: const EdgeInsets.only(left: 21, right: 21, top: 21), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(10)), + border: Border( + left: BorderSide( + color: type == ResultTypes.criticalLow || type == ResultTypes.criticalHigh + ? Color(0xFFDE7676) + : type == ResultTypes.low || type == ResultTypes.high + ? Color(0xFFEAB157) + : type == ResultTypes.normal + ? Color(0xFF09AA28) + : Color(0xFF09AA28), + width: MediaQuery.of(context).size.width * 0.015, + ), + ), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.2), + spreadRadius: 2, + blurRadius: 5, + offset: const Offset(0, 3), + ), + ], + ), + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + // Title Row + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: TextStyle( + fontSize: 16, + fontFamily: fontFamily, + fontWeight: FontWeight.w600, + color: const Color(0xff2E303A), + letterSpacing: -0.64, + height: 16 / 25, + ), + ), + GestureDetector( + onTap: onButtonPressed, + child: Utils.tableColumnValueWithUnderLine(TranslationBase.of(context).viewFlowChart, isLast: true, isCapitable: false), + ), + ], + ), + const SizedBox(height: 8), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ResultStatusWidget(type: type), + SizedBox(width: 5), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + subTitle, + style: TextStyle(fontSize: 14, fontFamily: fontFamily, fontWeight: FontWeight.w600, color: const Color(0xff2E303A), letterSpacing: -0.56), + ), + Text( + "Reference Range \n $referenceRange", + style: TextStyle( + fontSize: 10, + fontFamily: fontFamily, + fontWeight: FontWeight.w600, + color: const Color(0xff2E303A), + letterSpacing: -0.4, + ), + ), + ], + ), + ], + ), + const SizedBox(height: 10), + // Progress Bar + SizedBox( + height: 55, + child: CustomResultProgressBar( + percentage: percentage, + value: subTitle, + type: type, + ), + ), + const SizedBox(height: 12), + // Note Section + if (note.isNotEmpty) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Divider(color: Color(0xFFEFEFEF), thickness: 1), + const SizedBox(height: 10), + Text( + "Notes:", + style: TextStyle( + fontSize: 12, + color: const Color(0xFF2E303A), + letterSpacing: -0.4, + fontWeight: FontWeight.bold, + ), + ), + Text( + note, + style: TextStyle( + fontSize: 12, + color: const Color(0xFF575757), + letterSpacing: -0.4, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + const SizedBox(height: 3), + ], + ), + ), + ); + } +} + +class ResultStatusWidget extends StatelessWidget { + final ResultTypes type; + + const ResultStatusWidget({ + Key? key, + required this.type, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + Color? color; + IconData? icon; + + switch (type) { + case ResultTypes.criticalLow: + color = const Color(0xFFDE7676); + icon = Icons.arrow_circle_down; + break; + case ResultTypes.low: + color = const Color(0xFFEAB157); + icon = Icons.arrow_circle_down; + break; + case ResultTypes.criticalHigh: + color = const Color(0xFFDE7676); + icon = Icons.arrow_circle_up; + break; + case ResultTypes.high: + color = const Color(0xFFEAB157); + icon = Icons.arrow_circle_up; + break; + case ResultTypes.normal: + color = const Color(0xFF09AA28); + icon = Icons.check_circle; + break; + } + + // Return the icon if defined, otherwise a placeholder widget + return icon != null + ? Icon( + icon, + color: color, + ) + : const SizedBox(); // Use SizedBox to keep the layout consistent + } +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index ff78e2fb..ce02a498 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -226,6 +226,7 @@ flutter: - assets/payment_options/ - assets/images/new/ + - assets/images/new/HealthSnap/ - assets/images/new/zoom/ - assets/images/new/inpatient/ - assets/images/new/mass/