Files
tasq/lib/widgets/m3_card.dart
T
redz1029 57377b1b21 UI: Cyber/IT design overhaul with semantic status tokens (Material 3)
Re-theme TasQ with a refined enterprise "Cyber/IT" identity on Material 3.
Functionality is unchanged — only color/shape tokens and a few state widgets.

Foundation:
- Hand-tuned light + dark ColorSchemes (deep-slate surfaces, azure/teal/violet
  accents), tightened 12-16dp shape language, flat hairline-outlined cards.
- New AppStatusColors theme extension (success/warning/danger/info/neutral,
  per-brightness) replacing scattered raw Colors.green/orange/red/grey so status
  color is consistent and legible in dark mode.
- New TechChip mono primitive; StatusPill.kind; M3Card hairline variants;
  tightened AppSurfaces radii.

Per-screen:
- Tokenized status colors + radii across dashboard, tickets, tasks, IT service
  requests, attendance, whereabouts, network map, reports charts, auth, and
  shared/admin screens. Added _ticketStatusKind/_taskStatusKind/_isrStatusKind
  mappers so status carries meaning consistently.
- Fixed white pie-slice borders for dark mode; dropped ad-hoc dialog radius
  overrides in favor of the theme default. Categorical palettes (leave-type
  icons, team color picker, distribution charts) intentionally preserved.

Verified: flutter analyze lib clean; flutter build web green; visual QA in
light and dark (dashboard, tickets, attendance).

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-09-26 15:13:47 +08:00

146 lines
3.9 KiB
Dart

import 'package:flutter/material.dart';
import '../theme/app_surfaces.dart';
enum _M3CardVariant { elevated, filled, outlined }
/// Cyber/IT Card variants (Material 3).
///
/// Use these factory constructors to build semantically correct cards:
/// - [M3Card.elevated] — default panel: tonal fill + hairline outline (flat).
/// - [M3Card.filled] — high-emphasis fill (caller passes [color]); no border.
/// - [M3Card.outlined] — transparent fill with a hairline outline border.
class M3Card extends StatelessWidget {
const M3Card._(
this._variant, {
required this.child,
this.color,
this.shape,
this.margin,
this.clipBehavior = Clip.none,
this.onTap,
});
/// Elevated card — default panel with a tonal fill + hairline outline.
/// 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._(
_M3CardVariant.elevated,
color: color,
shape: shape,
margin: margin,
clipBehavior: clipBehavior,
onTap: onTap,
child: child,
);
}
/// Filled card — a high-emphasis fill (caller passes a semantic [color] or a
/// surfaceContainer tone). No border. Best for summary/callout panels.
factory M3Card.filled({
required Widget child,
Color? color,
ShapeBorder? shape,
EdgeInsetsGeometry? margin,
Clip clipBehavior = Clip.none,
VoidCallback? onTap,
}) {
return M3Card._(
_M3CardVariant.filled,
color: color,
shape: shape,
margin: margin,
clipBehavior: clipBehavior,
onTap: onTap,
child: child,
);
}
/// Outlined card — transparent fill with a hairline 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._(
_M3CardVariant.outlined,
color: color,
shape: shape,
margin: margin,
clipBehavior: clipBehavior,
onTap: onTap,
child: child,
);
}
final _M3CardVariant _variant;
final Widget child;
final Color? color;
final ShapeBorder? shape;
final EdgeInsetsGeometry? margin;
final Clip clipBehavior;
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
final radius = AppSurfaces.of(context).cardRadius;
final isDark = cs.brightness == Brightness.dark;
final Color resolvedColor;
final bool bordered;
switch (_variant) {
case _M3CardVariant.elevated:
resolvedColor =
color ??
(isDark ? cs.surfaceContainer : cs.surfaceContainerLowest);
bordered = true;
case _M3CardVariant.filled:
resolvedColor = color ?? cs.surfaceContainerHighest;
bordered = false;
case _M3CardVariant.outlined:
resolvedColor = color ?? Colors.transparent;
bordered = true;
}
final resolvedShape =
shape ??
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radius),
side: bordered
? BorderSide(color: cs.outlineVariant)
: BorderSide.none,
);
return Card(
color: resolvedColor,
elevation: 0,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
shape: resolvedShape,
margin: margin ?? EdgeInsets.zero,
clipBehavior: onTap != null && clipBehavior == Clip.none
? Clip.antiAlias
: clipBehavior,
child: onTap != null
? InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(radius),
child: child,
)
: child,
);
}
}