164 lines
4.3 KiB
Dart
164 lines
4.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import 'claims_overview_scope.dart';
|
|
|
|
/// Horizontal panel row for dashboard tabs; uses fixed widths during PDF capture.
|
|
class ClaimsTabPanelRow extends StatelessWidget {
|
|
final List<ClaimsTabPanel> panels;
|
|
final double gap;
|
|
final CrossAxisAlignment crossAxisAlignment;
|
|
|
|
const ClaimsTabPanelRow({
|
|
super.key,
|
|
required this.panels,
|
|
this.gap = 16,
|
|
this.crossAxisAlignment = CrossAxisAlignment.start,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final laneWidth = ClaimsPdfExportScope.laneWidthOf(context);
|
|
final isPdf = ClaimsPdfExportScope.of(context) && laneWidth != null;
|
|
|
|
if (!isPdf) {
|
|
return Row(
|
|
crossAxisAlignment: crossAxisAlignment,
|
|
children: _withGaps(
|
|
panels
|
|
.map((p) => Expanded(flex: p.flex, child: p.child))
|
|
.toList(),
|
|
),
|
|
);
|
|
}
|
|
|
|
final totalFlex = panels.fold<int>(0, (sum, p) => sum + p.flex);
|
|
final gaps = gap * (panels.length - 1);
|
|
final available = laneWidth! - gaps;
|
|
|
|
return Row(
|
|
crossAxisAlignment: crossAxisAlignment,
|
|
children: _withGaps(
|
|
panels.map((p) {
|
|
return SizedBox(
|
|
width: available * p.flex / totalFlex,
|
|
child: p.child,
|
|
);
|
|
}).toList(),
|
|
),
|
|
);
|
|
}
|
|
|
|
List<Widget> _withGaps(List<Widget> children) {
|
|
if (children.isEmpty) return const [];
|
|
final out = <Widget>[children.first];
|
|
for (var i = 1; i < children.length; i++) {
|
|
out.add(SizedBox(width: gap));
|
|
out.add(children[i]);
|
|
}
|
|
return out;
|
|
}
|
|
}
|
|
|
|
class ClaimsTabPanel {
|
|
final int flex;
|
|
final Widget child;
|
|
|
|
const ClaimsTabPanel({required this.flex, required this.child});
|
|
}
|
|
|
|
/// Mini grid for metric tiles inside section cards.
|
|
class ClaimsTabMiniGrid extends StatelessWidget {
|
|
final List<Widget> items;
|
|
final double gap;
|
|
final int columns;
|
|
|
|
const ClaimsTabMiniGrid({
|
|
super.key,
|
|
required this.items,
|
|
this.gap = 8,
|
|
this.columns = 2,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
assert(items.length == 3 || items.length == 4);
|
|
assert(columns == 2 || (columns == 3 && items.length == 3));
|
|
|
|
return LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
final maxW = constraints.maxWidth;
|
|
final isPdf = ClaimsPdfExportScope.of(context);
|
|
|
|
Widget cell(int index, {bool fullWidth = false, int? rowColumns}) {
|
|
final child = items[index];
|
|
if (isPdf && maxW.isFinite) {
|
|
final cols = rowColumns ?? columns;
|
|
final width = fullWidth
|
|
? maxW
|
|
: (maxW - gap * (cols - 1)) / cols;
|
|
return SizedBox(width: width, child: child);
|
|
}
|
|
return Expanded(child: child);
|
|
}
|
|
|
|
if (items.length == 3 && columns == 3) {
|
|
return Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
cell(0, rowColumns: 3),
|
|
SizedBox(width: gap),
|
|
cell(1, rowColumns: 3),
|
|
SizedBox(width: gap),
|
|
cell(2, rowColumns: 3),
|
|
],
|
|
);
|
|
}
|
|
|
|
if (items.length == 3) {
|
|
return Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
cell(0),
|
|
SizedBox(width: gap),
|
|
cell(1),
|
|
],
|
|
),
|
|
SizedBox(height: gap),
|
|
Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [cell(2, fullWidth: true)],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
return Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
cell(0),
|
|
SizedBox(width: gap),
|
|
cell(1),
|
|
],
|
|
),
|
|
SizedBox(height: gap),
|
|
Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
cell(2),
|
|
SizedBox(width: gap),
|
|
cell(3),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|