import 'dart:math'; import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; import 'package:diplomaticquarterapp/uitl/utils.dart'; import 'package:flutter/material.dart'; ///@author /// enum ResultTypes { lowCriticalLow, criticalLow, low, normal, high, criticalHigh, highCriticalHigh, IRR, unknown } 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 num percentage; final String value; final ResultTypes type; CustomResultProgressBar( {Key? key, required this.percentage, required this.value, required this.type}) : super(key: key); final GlobalKey lcl = GlobalKey(); final GlobalKey cl = GlobalKey(); final GlobalKey l = GlobalKey(); final GlobalKey n = GlobalKey(); final GlobalKey h = GlobalKey(); final GlobalKey ch = GlobalKey(); final GlobalKey hch = GlobalKey(); OverlayEntry? overlayEntry; @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( key: cl, flex: 3, color: Color(0xFFDE7676), title: TranslationBase.of(context).criticalLow, ), SizedBox( width: 2, ), _buildResultBar( key: l, flex: 2, color: Color(0xFFEAB157), title: TranslationBase.of(context).low, ), SizedBox( width: 2, ), _buildResultBar( key: n, flex: 5, color: Color(0xFF09AA28), title: TranslationBase.of(context).normal, ), SizedBox( width: 2, ), _buildResultBar( key: h, flex: 2, color: Color(0xFFEAB157), title: TranslationBase.of(context).high, ), SizedBox( width: 2, ), _buildResultBar( key: ch, flex: 3, color: Color(0xFFDE7676), title: TranslationBase.of(context).criticalHigh, ), ], ), if (!percentage.isNegative) Positioned( left: tooltipPosition, top: 33, child: Column( children: [ TextCloud( text: "${value}", color: getColorForResultType(type), width: 100, height: 20, padding: EdgeInsets.zero, axisDirection: AxisDirection.up, ), ], ), ), ], ); }); } Color getColorForResultType(ResultTypes type) { switch (type) { case ResultTypes.lowCriticalLow: case ResultTypes.highCriticalHigh: case ResultTypes.criticalLow: case ResultTypes.criticalHigh: return Color(0xFFDE7676); case ResultTypes.low: case ResultTypes.high: return Color(0xFFEAB157); case ResultTypes.normal: return Color(0xFF09AA28); default: return Color(0xFF09AA28); } } Widget _buildResultBar({ required int flex, required Color color, required String title, Key? key}) { return Expanded( key: key, flex: flex, child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( title, style: TextStyle( fontSize: 8, fontFamily: 'Poppins', letterSpacing: -0.3, color: color, fontWeight: FontWeight.w400, ), 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: 9, letterSpacing: -.3), )), ), 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 num 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(top: 21), decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(10)), border: BorderDirectional( start: BorderSide( color: getColorForResultType(type), 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: [ Expanded( child: Text( title, style: TextStyle( fontSize: 16, fontFamily: fontFamily, fontWeight: FontWeight.w600, color: const Color(0xff2E303A), letterSpacing: -.64), ), ), 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( "${TranslationBase.of(context).referenceRange} \n $referenceRange", style: TextStyle( fontSize: 10, fontFamily: fontFamily, fontWeight: FontWeight.w600, color: const Color(0xff2E303A), letterSpacing: -0.4, ), ), ], ), ], ), const SizedBox(height: 10), // Progress Bar Visibility( visible: type != ResultTypes.IRR, child: Column( children: [ 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( "${TranslationBase.of(context).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), ], ), ), ); } Color getColorForResultType(ResultTypes type) { switch (type) { case ResultTypes.criticalLow: case ResultTypes.criticalHigh: case ResultTypes.lowCriticalLow: case ResultTypes.highCriticalHigh: return Color(0xFFDE7676); case ResultTypes.low: case ResultTypes.high: return Color(0xFFEAB157); case ResultTypes.normal: return Color(0xFF09AA28); //todo handle irr here default: return Color(0xFF09AA28); } } } 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: case ResultTypes.lowCriticalLow: 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: case ResultTypes.highCriticalHigh: 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; case ResultTypes.unknown: case ResultTypes.IRR: color = 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 } }