cr 7211 lab report new ui changes
parent
0f0b541563
commit
b74a433798
@ -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
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue