enrollment-app/lib/presentation/claims_overview/claims_overview_widgets.dart

1135 lines
32 KiB
Dart

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'claims_overview_animations.dart';
import 'claims_overview_scope.dart';
import 'claims_overview_theme.dart';
bool _overviewUsesWideLayout(
BuildContext context,
BoxConstraints constraints, {
double minWidth = 520,
}) {
if (ClaimsPdfExportScope.of(context)) return true;
return constraints.maxWidth >= minWidth;
}
/// Hover / tap focus wrapper for dashboard tiles.
class ClaimsFocusCard extends StatefulWidget {
final Widget child;
final BorderRadius borderRadius;
final Color accentColor;
final EdgeInsetsGeometry? margin;
final BoxConstraints? constraints;
final double? width;
final VoidCallback? onTap;
const ClaimsFocusCard({
super.key,
required this.child,
this.borderRadius = const BorderRadius.all(Radius.circular(10)),
this.accentColor = ClaimsOverviewTheme.primary,
this.margin,
this.constraints,
this.width,
this.onTap,
});
@override
State<ClaimsFocusCard> createState() => _ClaimsFocusCardState();
}
class _ClaimsFocusCardState extends State<ClaimsFocusCard> {
bool _hovering = false;
bool _selected = false;
bool get _focused => _hovering || _selected;
@override
Widget build(BuildContext context) {
if (ClaimsPdfExportScope.of(context)) {
return _staticCard();
}
return MouseRegion(
onEnter: (_) => setState(() => _hovering = true),
onExit: (_) => setState(() => _hovering = false),
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: widget.onTap ?? () => setState(() => _selected = !_selected),
child: _animatedCard(),
),
);
}
Widget _staticCard() {
return Container(
width: widget.width,
margin: widget.margin,
constraints: widget.constraints,
decoration: _cardDecoration(focused: false),
child: widget.child,
);
}
Widget _animatedCard() {
return AnimatedContainer(
width: widget.width,
margin: widget.margin,
constraints: widget.constraints,
duration: const Duration(milliseconds: 180),
curve: Curves.easeOutCubic,
transform: _focused
? (Matrix4.identity()..translateByDouble(0, -3, 0, 1))
: Matrix4.identity(),
decoration: _cardDecoration(focused: _focused),
child: widget.child,
);
}
BoxDecoration _cardDecoration({required bool focused}) {
return BoxDecoration(
color: ClaimsOverviewTheme.cardBg,
borderRadius: widget.borderRadius,
border: Border.all(
color: focused
? widget.accentColor.withValues(alpha: 0.55)
: ClaimsOverviewTheme.border,
width: focused ? 1.5 : 1,
),
boxShadow: focused
? [
BoxShadow(
color: widget.accentColor.withValues(alpha: 0.16),
blurRadius: 16,
offset: const Offset(0, 6),
),
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 8,
offset: const Offset(0, 2),
),
]
: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.04),
blurRadius: 6,
offset: const Offset(0, 2),
),
],
);
}
}
class ClaimsPolicyInformationBody extends StatelessWidget {
final String policyStartDate;
final String policyEndDate;
final String policyRunDays;
final String tpaName;
final String insurerName;
const ClaimsPolicyInformationBody({
super.key,
required this.policyStartDate,
required this.policyEndDate,
required this.policyRunDays,
required this.tpaName,
required this.insurerName,
});
@override
Widget build(BuildContext context) {
final startTile = _OverviewInnerTile(
label: 'Policy Start Date',
icon: Icons.calendar_today,
accentColor: ClaimsOverviewTheme.blue,
child: _OverviewTileValue(policyStartDate),
);
final endTile = _OverviewInnerTile(
label: 'Policy End Date',
icon: Icons.event,
accentColor: ClaimsOverviewTheme.red,
child: _OverviewTileValue(policyEndDate),
);
final runDaysTile = _OverviewInnerTile(
label: 'Policy Run Days',
icon: Icons.timelapse,
accentColor: ClaimsOverviewTheme.purple,
child: _OverviewTileValue(policyRunDays),
);
final tpaTile = _OverviewInnerTile(
label: 'TPA',
icon: Icons.apartment,
accentColor: ClaimsOverviewTheme.teal,
child: _OverviewTileValue(tpaName, maxLines: 2),
);
final insurerTile = _OverviewInnerTile(
label: 'Insurer',
icon: Icons.military_tech,
accentColor: ClaimsOverviewTheme.orange,
child: _OverviewTileValue(insurerName, maxLines: 2),
);
return LayoutBuilder(
builder: (context, constraints) {
final wide = _overviewUsesWideLayout(context, constraints);
if (!wide) {
return Column(
children: [
startTile,
const SizedBox(height: 12),
endTile,
const SizedBox(height: 12),
runDaysTile,
const SizedBox(height: 12),
tpaTile,
const SizedBox(height: 12),
insurerTile,
],
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_OverviewThreeColumnRow(
children: [startTile, endTile, runDaysTile],
),
const SizedBox(height: 12),
_OverviewTwoColumnRow(
children: [tpaTile, insurerTile],
),
],
);
},
);
}
}
class ClaimsExperienceBody extends StatelessWidget {
final String incurredClaims;
final String incurredRatio;
final String projectedClaims;
final String projectedRatio;
final String claimsIncidenceRate;
const ClaimsExperienceBody({
super.key,
required this.incurredClaims,
required this.incurredRatio,
required this.projectedClaims,
required this.projectedRatio,
required this.claimsIncidenceRate,
});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final wide = _overviewUsesWideLayout(context, constraints);
final incurredTile = _OverviewInnerTile(
label: 'Incurred Claims',
icon: Icons.gps_fixed,
accentColor: ClaimsOverviewTheme.red,
matchRowHeight: wide,
child: _ClaimsExperienceDualValues(
primaryValue: incurredClaims,
secondaryValue: incurredRatio,
),
);
final projectedTile = _OverviewInnerTile(
label: 'Projected Claims',
icon: Icons.trending_up,
accentColor: ClaimsOverviewTheme.orange,
matchRowHeight: wide,
child: _ClaimsExperienceDualValues(
primaryValue: projectedClaims,
secondaryValue: projectedRatio,
),
);
final incidenceTile = _OverviewInnerTile(
label: 'Claims Incidence Rate',
icon: Icons.timeline,
accentColor: ClaimsOverviewTheme.cyan,
matchRowHeight: wide,
child: _ClaimsExperienceDualValues(
primaryValue: claimsIncidenceRate,
),
);
if (!wide) {
return Column(
children: [
incurredTile,
const SizedBox(height: 12),
projectedTile,
const SizedBox(height: 12),
incidenceTile,
],
);
}
return _OverviewThreeColumnRow(
equalizeHeight: true,
children: [incurredTile, projectedTile, incidenceTile],
);
},
);
}
}
class ClaimsInceptionBody extends StatelessWidget {
final String inceptionEmployee;
final String inceptionLives;
const ClaimsInceptionBody({
super.key,
required this.inceptionEmployee,
required this.inceptionLives,
});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final wide = _overviewUsesWideLayout(
context,
constraints,
minWidth: 400,
);
final valueStyle = GoogleFonts.poppins(
fontSize: 18,
fontWeight: FontWeight.w700,
color: ClaimsOverviewTheme.textPrimary,
height: 1.25,
);
final employeeTile = _OverviewInnerTile(
label: 'Inception Employee',
icon: Icons.person_add_alt,
accentColor: ClaimsOverviewTheme.blue,
matchRowHeight: wide,
child: Text(inceptionEmployee, style: valueStyle),
);
final livesTile = _OverviewInnerTile(
label: 'Inception Lives',
icon: Icons.favorite_border,
accentColor: ClaimsOverviewTheme.pink,
matchRowHeight: wide,
child: Text(inceptionLives, style: valueStyle),
);
if (!wide) {
return Column(
children: [
employeeTile,
const SizedBox(height: 12),
livesTile,
],
);
}
return _OverviewTwoColumnRow(
equalizeHeight: true,
children: [employeeTile, livesTile],
);
},
);
}
}
class _OverviewTileValue extends StatelessWidget {
final String value;
final int maxLines;
const _OverviewTileValue(this.value, {this.maxLines = 1});
@override
Widget build(BuildContext context) {
return Text(
value,
maxLines: maxLines,
overflow: TextOverflow.ellipsis,
style: GoogleFonts.poppins(
fontSize: maxLines > 1 ? 15 : 18,
fontWeight: FontWeight.w700,
color: ClaimsOverviewTheme.textPrimary,
height: 1.25,
),
);
}
}
class _OverviewInnerTile extends StatelessWidget {
final String label;
final IconData icon;
final Color accentColor;
final Widget child;
final bool matchRowHeight;
const _OverviewInnerTile({
required this.label,
required this.icon,
required this.accentColor,
required this.child,
this.matchRowHeight = false,
});
@override
Widget build(BuildContext context) {
return ClaimsFocusCard(
accentColor: accentColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: matchRowHeight ? MainAxisSize.max : MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(14, 10, 14, 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
label.toUpperCase(),
style: GoogleFonts.poppins(
fontSize: 10,
fontWeight: FontWeight.w600,
color: ClaimsOverviewTheme.textSecondary,
letterSpacing: 0.4,
height: 1.3,
),
),
),
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: accentColor.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, size: 16, color: accentColor),
),
],
),
const SizedBox(height: 10),
child,
],
),
),
if (matchRowHeight) const Spacer(),
Container(
height: 3,
decoration: BoxDecoration(
color: accentColor,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
),
),
],
),
);
}
}
class _ClaimsExperienceDualValues extends StatelessWidget {
final String primaryValue;
final String? secondaryValue;
const _ClaimsExperienceDualValues({
required this.primaryValue,
this.secondaryValue,
});
@override
Widget build(BuildContext context) {
final valueStyle = GoogleFonts.poppins(
fontSize: 18,
fontWeight: FontWeight.w700,
color: ClaimsOverviewTheme.textPrimary,
height: 1.2,
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
primaryValue,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: valueStyle,
),
if (secondaryValue != null && secondaryValue!.isNotEmpty) ...[
const SizedBox(height: 4),
Text(
secondaryValue!,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: valueStyle.copyWith(fontSize: 16),
),
],
],
);
}
}
class ClaimsPremiumMembershipBody extends StatelessWidget {
final String premiumAsOnDate;
final String earnedPremium;
final String currentEmployee;
final String currentLives;
final String avgFamilySize;
const ClaimsPremiumMembershipBody({
super.key,
required this.premiumAsOnDate,
required this.earnedPremium,
required this.currentEmployee,
required this.currentLives,
required this.avgFamilySize,
});
@override
Widget build(BuildContext context) {
final premiumTile = _OverviewInnerTile(
label: 'Premium As On Date',
icon: Icons.account_balance_wallet,
accentColor: ClaimsOverviewTheme.orange,
child: _OverviewTileValue(premiumAsOnDate),
);
final earnedTile = _OverviewInnerTile(
label: 'Earned Premium',
icon: Icons.payments,
accentColor: ClaimsOverviewTheme.green,
child: _OverviewTileValue(earnedPremium),
);
final employeeTile = _OverviewInnerTile(
label: 'Current Employee',
icon: Icons.business_center,
accentColor: ClaimsOverviewTheme.blue,
child: _OverviewTileValue(currentEmployee),
);
final livesTile = _OverviewInnerTile(
label: 'Current Lives',
icon: Icons.favorite_border,
accentColor: ClaimsOverviewTheme.pink,
child: _OverviewTileValue(currentLives),
);
final avgSizeTile = _OverviewInnerTile(
label: 'Avg F-Size',
icon: Icons.groups,
accentColor: ClaimsOverviewTheme.purple,
child: _OverviewTileValue(avgFamilySize),
);
return LayoutBuilder(
builder: (context, constraints) {
if (!_overviewUsesWideLayout(context, constraints)) {
return Column(
children: [
premiumTile,
const SizedBox(height: 12),
earnedTile,
const SizedBox(height: 12),
employeeTile,
const SizedBox(height: 12),
livesTile,
const SizedBox(height: 12),
avgSizeTile,
],
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_OverviewThreeColumnRow(
children: [premiumTile, earnedTile, employeeTile],
),
const SizedBox(height: 12),
_OverviewTwoColumnRow(
children: [livesTile, avgSizeTile],
),
],
);
},
);
}
}
/// Two equal columns — used for TPA / Insurer side-by-side rows.
class _OverviewTwoColumnRow extends StatelessWidget {
final List<Widget> children;
final bool equalizeHeight;
const _OverviewTwoColumnRow({
required this.children,
this.equalizeHeight = false,
});
@override
Widget build(BuildContext context) {
assert(children.length == 2);
final row = Row(
crossAxisAlignment: equalizeHeight
? CrossAxisAlignment.stretch
: CrossAxisAlignment.start,
children: [
Expanded(child: children[0]),
const SizedBox(width: 12),
Expanded(child: children[1]),
],
);
if (!equalizeHeight) return row;
return IntrinsicHeight(child: row);
}
}
/// Three equal columns — matches Policy Information top-row alignment.
class _OverviewThreeColumnRow extends StatelessWidget {
final List<Widget> children;
final bool equalizeHeight;
const _OverviewThreeColumnRow({
required this.children,
this.equalizeHeight = false,
});
@override
Widget build(BuildContext context) {
assert(children.isNotEmpty && children.length <= 3);
final row = Row(
crossAxisAlignment: equalizeHeight
? CrossAxisAlignment.stretch
: CrossAxisAlignment.start,
children: [
for (var i = 0; i < 3; i++) ...[
if (i > 0) const SizedBox(width: 12),
Expanded(
child: i < children.length
? children[i]
: const SizedBox.shrink(),
),
],
],
);
if (!equalizeHeight) return row;
return IntrinsicHeight(child: row);
}
}
class ClaimsMetricCard extends StatelessWidget {
/// Measured layout: label row (~46) + value block (54) + accent (3).
static const double layoutHeight = 107;
final String label;
final String value;
final String? secondaryValue;
final IconData icon;
final Color accentColor;
final int replayToken;
final bool animateValue;
const ClaimsMetricCard({
super.key,
required this.label,
required this.value,
this.secondaryValue,
required this.icon,
required this.accentColor,
this.replayToken = 0,
this.animateValue = true,
});
@override
Widget build(BuildContext context) {
final compact = ClaimsPdfExportScope.of(context);
final valueStyle = GoogleFonts.poppins(
fontSize: compact ? 15 : 20,
fontWeight: FontWeight.w700,
color: ClaimsOverviewTheme.textPrimary,
height: 1.2,
);
return ClaimsFocusCard(
width: double.infinity,
constraints: BoxConstraints(minHeight: compact ? 80 : 100),
accentColor: accentColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.fromLTRB(
compact ? 10 : 14,
compact ? 8 : 10,
compact ? 10 : 14,
compact ? 4 : 6,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
label.toUpperCase(),
style: GoogleFonts.poppins(
fontSize: 10,
fontWeight: FontWeight.w600,
color: ClaimsOverviewTheme.textSecondary,
letterSpacing: 0.4,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.all(6),
decoration: BoxDecoration(
color: accentColor.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, size: compact ? 16 : 18, color: accentColor),
),
],
),
),
Padding(
padding: EdgeInsets.fromLTRB(
compact ? 10 : 14,
0,
compact ? 10 : 14,
compact ? 6 : 8,
),
child: SizedBox(
height: compact ? 40 : 48,
child: Align(
alignment: Alignment.centerLeft,
child: secondaryValue == null
? (compact || !animateValue
? Text(
value,
style: valueStyle,
maxLines: 3,
overflow: TextOverflow.ellipsis,
)
: ClaimsAnimatedValueText(
value: value,
replayToken: replayToken,
style: valueStyle,
))
: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: compact || !animateValue
? Text(
value,
style: valueStyle,
maxLines: 2,
overflow: TextOverflow.ellipsis,
)
: ClaimsAnimatedValueText(
value: value,
replayToken: replayToken,
style: valueStyle,
),
),
const SizedBox(width: 8),
Text(
secondaryValue!,
style: valueStyle.copyWith(fontSize: compact ? 13 : 16),
),
],
),
),
),
),
if (compact)
Container(
height: 3,
decoration: BoxDecoration(
color: accentColor,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
),
)
else
TweenAnimationBuilder<double>(
key: ValueKey('accent-$replayToken-$label'),
tween: Tween(begin: 0, end: 1),
duration: const Duration(milliseconds: 700),
curve: Curves.easeOutCubic,
builder: (_, w, __) {
return ClipRRect(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
child: Align(
alignment: Alignment.centerLeft,
child: FractionallySizedBox(
widthFactor: w.clamp(0.0, 1.0),
child: Container(
height: 3,
color: accentColor,
),
),
),
);
},
),
],
),
);
}
}
class ClaimsSectionCard extends StatelessWidget {
/// Min height for side-by-side overview pairs (e.g. Claims Experience / Inception).
static const double pairedSectionMinHeight = 170;
final String title;
final IconData icon;
final Widget child;
final bool fillHeight;
final double? height;
final double? minHeight;
const ClaimsSectionCard({
super.key,
required this.title,
required this.icon,
required this.child,
this.fillHeight = false,
this.height,
this.minHeight,
});
@override
Widget build(BuildContext context) {
final body = Padding(
padding: const EdgeInsets.all(12),
child: child,
);
final header = Padding(
padding: const EdgeInsets.fromLTRB(12, 10, 12, 0),
child: Row(
children: [
Icon(icon, size: 20, color: ClaimsOverviewTheme.primary),
const SizedBox(width: 8),
Expanded(
child: Text(
title,
style: GoogleFonts.poppins(
fontSize: 15,
fontWeight: FontWeight.w600,
color: ClaimsOverviewTheme.textPrimary,
),
),
),
],
),
);
final useExpandedBody = height != null;
final stretchVertically = fillHeight || height != null;
final bodySlot = useExpandedBody ? Expanded(child: body) : body;
return Container(
width: double.infinity,
height: height,
constraints: height == null && minHeight != null
? BoxConstraints(minHeight: minHeight!)
: null,
decoration: BoxDecoration(
color: ClaimsOverviewTheme.cardBg,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: ClaimsOverviewTheme.border),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.04),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize:
stretchVertically ? MainAxisSize.max : MainAxisSize.min,
children: [
header,
bodySlot,
],
),
);
}
}
/// Metric tiles in a grid — [columns] per row (default 3), title + value inside each card.
class ClaimsMetricTileGrid extends StatelessWidget {
static const _gap = 8.0;
static const _tileMinHeight = 100.0;
final List<String> labels;
final List<String> values;
final List<Color> accentColors;
final int replayToken;
final int columns;
final IconData icon;
const ClaimsMetricTileGrid({
super.key,
required this.labels,
required this.values,
required this.accentColors,
required this.replayToken,
this.columns = 3,
this.icon = Icons.insights_outlined,
});
@override
Widget build(BuildContext context) {
assert(labels.length == values.length);
final cols = columns.clamp(1, 4);
return LayoutBuilder(
builder: (context, constraints) {
final maxW = constraints.maxWidth.isFinite
? constraints.maxWidth
: 800.0;
final cellWidth = (maxW - _gap * (cols - 1)) / cols;
final rowCount = (labels.length + cols - 1) ~/ cols;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: List.generate(rowCount, (row) {
final start = row * cols;
return Padding(
padding: EdgeInsets.only(top: row == 0 ? 0 : _gap),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: List.generate(cols, (col) {
final index = start + col;
if (index >= labels.length) {
return SizedBox(width: cellWidth);
}
final color =
accentColors[index % accentColors.length];
return SizedBox(
width: cellWidth,
child: ConstrainedBox(
constraints:
const BoxConstraints(minHeight: _tileMinHeight),
child: ClaimsMetricCard(
label: labels[index],
value: values[index],
icon: icon,
accentColor: color,
replayToken: replayToken,
),
),
);
}),
),
);
}),
);
},
);
}
}
class ClaimsListRow extends StatelessWidget {
final IconData icon;
final Color iconColor;
final String title;
final String value;
const ClaimsListRow({
super.key,
required this.icon,
required this.iconColor,
required this.title,
required this.value,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: iconColor.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, size: 18, color: iconColor),
),
const SizedBox(width: 12),
Expanded(
child: Text(
title,
style: GoogleFonts.poppins(
fontSize: 13,
color: ClaimsOverviewTheme.textPrimary,
),
),
),
Text(
value,
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w700,
color: ClaimsOverviewTheme.textPrimary,
),
),
],
),
);
}
}
class ClaimsRelationshipCard extends StatelessWidget {
final String relation;
final int claims;
final String amount;
final Color color;
const ClaimsRelationshipCard({
super.key,
required this.relation,
required this.claims,
required this.amount,
required this.color,
});
@override
Widget build(BuildContext context) {
return Expanded(
child: ClaimsFocusCard(
margin: const EdgeInsets.symmetric(horizontal: 4),
accentColor: color,
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(Icons.person_outline, color: color, size: 22),
const SizedBox(height: 4),
SizedBox(
height: 20,
child: Align(
alignment: Alignment.centerLeft,
child: FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.centerLeft,
child: Text(
relation,
maxLines: 1,
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
),
),
),
const SizedBox(height: 4),
Text(
'$claims Claims',
style: GoogleFonts.poppins(
fontSize: 11,
color: ClaimsOverviewTheme.textSecondary,
),
),
SizedBox(
height: 12,
child: Align(
alignment: Alignment.centerLeft,
child: FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.centerLeft,
child: Text(
'Amount: $amount',
maxLines: 1,
style: GoogleFonts.poppins(
fontSize: 11,
fontWeight: FontWeight.w600,
color: ClaimsOverviewTheme.textPrimary,
),
),
),
),
),
],
),
),
),
);
}
}
class ClaimsCityBar extends StatelessWidget {
final String city;
final String amount;
final double fraction;
final Color color;
final int replayToken;
const ClaimsCityBar({
super.key,
required this.city,
required this.amount,
required this.fraction,
required this.color,
this.replayToken = 0,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(city, style: GoogleFonts.poppins(fontSize: 13)),
Text(
amount,
style: GoogleFonts.poppins(
fontSize: 13,
fontWeight: FontWeight.w600,
),
),
],
),
const SizedBox(height: 4),
ClaimsAnimatedProgressBar(
value: fraction,
color: color,
replayToken: replayToken,
),
],
),
);
}
}