tasq/lib/widgets/m3_card.dart

137 lines
3.6 KiB
Dart

import 'package:flutter/material.dart';
/// M3 Expressive Card variants.
///
/// Use these factory constructors to build semantically correct cards:
/// - [M3Card.elevated] — default, uses tonal surface tint (no hard shadow).
/// - [M3Card.filled] — uses surfaceContainerHighest for emphasis.
/// - [M3Card.outlined] — transparent fill with a subtle outline border.
class M3Card extends StatelessWidget {
const M3Card._({
required this.child,
this.color,
this.elevation,
this.shadowColor,
this.surfaceTintColor,
this.shape,
this.margin,
this.clipBehavior = Clip.none,
this.onTap,
});
/// Elevated card — tonal surface tint, minimal shadow.
/// Best for primary content surfaces (metric cards, detail panels).
factory M3Card.elevated({
required Widget child,
Color? color,
ShapeBorder? shape,
EdgeInsetsGeometry? margin,
Clip clipBehavior = Clip.none,
VoidCallback? onTap,
}) {
return M3Card._(
color: color,
elevation: 1,
shadowColor: Colors.transparent,
shape: shape,
margin: margin,
clipBehavior: clipBehavior,
onTap: onTap,
child: child,
);
}
/// Filled card — uses surfaceContainerHighest for high emphasis.
/// Best for summary cards, status counts, callout panels.
factory M3Card.filled({
required Widget child,
Color? color,
ShapeBorder? shape,
EdgeInsetsGeometry? margin,
Clip clipBehavior = Clip.none,
VoidCallback? onTap,
}) {
return M3Card._(
color: color, // caller passes surfaceContainerHighest or semantic color
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
shape: shape,
margin: margin,
clipBehavior: clipBehavior,
onTap: onTap,
child: child,
);
}
/// Outlined card — transparent fill with outline border.
/// Best for list items, form sections, grouped content.
factory M3Card.outlined({
required Widget child,
Color? color,
ShapeBorder? shape,
EdgeInsetsGeometry? margin,
Clip clipBehavior = Clip.none,
VoidCallback? onTap,
}) {
return M3Card._(
color: color,
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
shape: shape,
margin: margin,
clipBehavior: clipBehavior,
onTap: onTap,
child: child,
);
}
final Widget child;
final Color? color;
final double? elevation;
final Color? shadowColor;
final Color? surfaceTintColor;
final ShapeBorder? shape;
final EdgeInsetsGeometry? margin;
final Clip clipBehavior;
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
// For outlined, we need the border side
final resolvedShape =
shape ??
(elevation == 0 && surfaceTintColor == Colors.transparent
? RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: (shadowColor == Colors.transparent && color == null)
? BorderSide(color: cs.outlineVariant)
: BorderSide.none,
)
: null);
final card = Card(
color: color,
elevation: elevation,
shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,
shape: resolvedShape,
margin: margin ?? EdgeInsets.zero,
clipBehavior: clipBehavior,
child: onTap != null
? InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(16),
child: child,
)
: child,
);
return card;
}
}