57377b1b21
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>
146 lines
3.9 KiB
Dart
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,
|
|
);
|
|
}
|
|
}
|