313 lines
7.8 KiB
Dart
313 lines
7.8 KiB
Dart
// AUTO-GENERATED widget — matches web .data-card
|
|
import 'package:flutter/material.dart';
|
|
import '../inou_theme.dart';
|
|
import 'inou_badge.dart';
|
|
import 'inou_button.dart';
|
|
|
|
/// Data card with colored indicator bar
|
|
class InouCard extends StatelessWidget {
|
|
final String? title;
|
|
final String? subtitle;
|
|
final Color indicatorColor;
|
|
final Widget? trailing;
|
|
final Widget? child;
|
|
final VoidCallback? onTap;
|
|
|
|
const InouCard({
|
|
super.key,
|
|
this.title,
|
|
this.subtitle,
|
|
this.indicatorColor = InouTheme.accent,
|
|
this.trailing,
|
|
this.child,
|
|
this.onTap,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
margin: const EdgeInsets.only(bottom: InouTheme.spaceLg),
|
|
decoration: BoxDecoration(
|
|
color: InouTheme.bgCard,
|
|
borderRadius: InouTheme.borderRadiusLg,
|
|
border: Border.all(color: InouTheme.border),
|
|
),
|
|
clipBehavior: Clip.antiAlias,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
if (title != null)
|
|
_Header(
|
|
title: title!,
|
|
subtitle: subtitle,
|
|
indicatorColor: indicatorColor,
|
|
trailing: trailing,
|
|
),
|
|
if (child != null) child!,
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _Header extends StatelessWidget {
|
|
final String title;
|
|
final String? subtitle;
|
|
final Color indicatorColor;
|
|
final Widget? trailing;
|
|
|
|
const _Header({
|
|
required this.title,
|
|
this.subtitle,
|
|
required this.indicatorColor,
|
|
this.trailing,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(InouTheme.spaceLg),
|
|
decoration: const BoxDecoration(
|
|
border: Border(
|
|
bottom: BorderSide(color: InouTheme.border),
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Container(
|
|
width: 4,
|
|
height: 32,
|
|
decoration: BoxDecoration(
|
|
color: indicatorColor,
|
|
borderRadius: BorderRadius.circular(2),
|
|
),
|
|
),
|
|
const SizedBox(width: InouTheme.spaceMd),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
title.toUpperCase(),
|
|
style: InouTheme.labelSmall.copyWith(
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: 0.8,
|
|
),
|
|
),
|
|
if (subtitle != null)
|
|
Text(
|
|
subtitle!,
|
|
style: InouTheme.bodySmall.copyWith(
|
|
color: InouTheme.textMuted,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
if (trailing != null) trailing!,
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Simple card without indicator
|
|
class InouSimpleCard extends StatelessWidget {
|
|
final Widget child;
|
|
final EdgeInsets? padding;
|
|
final VoidCallback? onTap;
|
|
|
|
const InouSimpleCard({
|
|
super.key,
|
|
required this.child,
|
|
this.padding,
|
|
this.onTap,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final card = Container(
|
|
padding: padding ?? const EdgeInsets.all(InouTheme.spaceLg),
|
|
decoration: BoxDecoration(
|
|
color: InouTheme.bgCard,
|
|
borderRadius: InouTheme.borderRadiusLg,
|
|
border: Border.all(color: InouTheme.border),
|
|
),
|
|
child: child,
|
|
);
|
|
|
|
if (onTap != null) {
|
|
return InkWell(
|
|
onTap: onTap,
|
|
borderRadius: InouTheme.borderRadiusLg,
|
|
child: card,
|
|
);
|
|
}
|
|
return card;
|
|
}
|
|
}
|
|
|
|
/// Profile card for dashboard
|
|
class InouProfileCard extends StatelessWidget {
|
|
final String name;
|
|
final String? role;
|
|
final String? dob;
|
|
final String? sex;
|
|
final List<ProfileStat> stats;
|
|
final bool isCare;
|
|
final VoidCallback? onTap;
|
|
final VoidCallback? onEdit;
|
|
|
|
const InouProfileCard({
|
|
super.key,
|
|
required this.name,
|
|
this.role,
|
|
this.dob,
|
|
this.sex,
|
|
this.stats = const [],
|
|
this.isCare = false,
|
|
this.onTap,
|
|
this.onEdit,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return InkWell(
|
|
onTap: onTap,
|
|
borderRadius: InouTheme.borderRadiusLg,
|
|
child: Container(
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: InouTheme.bgCard,
|
|
borderRadius: InouTheme.borderRadiusLg,
|
|
border: Border.all(color: InouTheme.border),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(name, style: InouTheme.h3),
|
|
),
|
|
if (onEdit != null)
|
|
GestureDetector(
|
|
onTap: onEdit,
|
|
child: Text('✎', style: TextStyle(color: InouTheme.textMuted)),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 4),
|
|
Row(
|
|
children: [
|
|
Text(
|
|
role ?? 'you',
|
|
style: InouTheme.bodySmall.copyWith(color: InouTheme.textSubtle),
|
|
),
|
|
if (isCare) ...[
|
|
const SizedBox(width: 8),
|
|
const InouBadge(text: 'care', variant: BadgeVariant.care),
|
|
],
|
|
],
|
|
),
|
|
if (dob != null) ...[
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'Born: $dob${sex != null ? ' · $sex' : ''}',
|
|
style: InouTheme.bodySmall.copyWith(color: InouTheme.textMuted),
|
|
),
|
|
],
|
|
if (stats.isNotEmpty) ...[
|
|
const SizedBox(height: 12),
|
|
Wrap(
|
|
spacing: 16,
|
|
runSpacing: 8,
|
|
children: stats.map((s) => _StatChip(stat: s)).toList(),
|
|
),
|
|
],
|
|
const Spacer(),
|
|
InouButton(
|
|
text: 'View',
|
|
size: ButtonSize.small,
|
|
onPressed: onTap,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class ProfileStat {
|
|
final String emoji;
|
|
final String label;
|
|
|
|
const ProfileStat(this.emoji, this.label);
|
|
}
|
|
|
|
class _StatChip extends StatelessWidget {
|
|
final ProfileStat stat;
|
|
|
|
const _StatChip({required this.stat});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Text(
|
|
'${stat.emoji} ${stat.label}',
|
|
style: InouTheme.bodySmall.copyWith(
|
|
color: InouTheme.textMuted,
|
|
fontSize: 12,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Add card (dashed border)
|
|
class InouAddCard extends StatelessWidget {
|
|
final String label;
|
|
final VoidCallback? onTap;
|
|
|
|
const InouAddCard({
|
|
super.key,
|
|
required this.label,
|
|
this.onTap,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return InkWell(
|
|
onTap: onTap,
|
|
borderRadius: InouTheme.borderRadiusLg,
|
|
child: Container(
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
borderRadius: InouTheme.borderRadiusLg,
|
|
border: Border.all(
|
|
color: InouTheme.border,
|
|
width: 2,
|
|
style: BorderStyle.solid, // Note: Flutter doesn't support dashed directly
|
|
),
|
|
),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Text(
|
|
'+',
|
|
style: TextStyle(
|
|
fontSize: 28,
|
|
color: InouTheme.accent,
|
|
fontWeight: FontWeight.w300,
|
|
),
|
|
),
|
|
const SizedBox(height: 6),
|
|
Text(
|
|
label,
|
|
style: InouTheme.bodyMedium.copyWith(color: InouTheme.textMuted),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|