inou/design/flutter/widgets/inou_card.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),
),
],
),
),
);
}
}