profile_stat.dart

Open source file
1import 'package:flutter/widgets.dart';23import '../tokens/design_tokens.dart';4enum ProfileStatType { primary, secondary }56enum ProfileStatState { default_, pressed, disabled }78/// Statistics card defined by profile-stat.json (Type x State).9class ProfileStat extends StatelessWidget {10  const ProfileStat({11    super.key,12    required this.tokens,13    required this.value,14    required this.label,15    required this.type,16    this.state = ProfileStatState.default_,17    this.onTap,18    this.horizontal = false,19  });2021  final DesignTokens tokens;22  final String value;23  final String label;24  final ProfileStatType type;25  final ProfileStatState state;26  /// Disabled cards ignore this callback.27  final VoidCallback? onTap;28  /// Later responsive adaptation, not part of this JSON specification.29  /// The caller selects the layout; this widget does not detect screen width.30  final bool horizontal;3132  @override33  Widget build(BuildContext context) {34    final valueStyle = tokens.typography('title');35    final labelStyle = tokens.typography('caption');36    final colors = _colorsFor(tokens, type, state);37    final isDisabled = state == ProfileStatState.disabled;3839    final valueText = Text(40      valueStyle.applyCase(value),41      style: valueStyle.toTextStyle(42        fontFamily: tokens.fontFamily,43        color: colors.text,44        fontSizeOverride: horizontal ? 28 : null,45      ),46    );47    final labelText = Text(48      labelStyle.applyCase(label),49      style: labelStyle.toTextStyle(50        fontFamily: tokens.fontFamily,51        color: colors.text,52      ),53    );5455    return GestureDetector(56      onTap: isDisabled ? null : onTap,57      behavior: HitTestBehavior.opaque,58      child: Container(59        width: horizontal ? double.infinity : 122,60        height: horizontal ? 64 : 128,61        padding: EdgeInsets.all(tokens.space(12)),62        decoration: BoxDecoration(63          color: colors.fill,64          borderRadius: BorderRadius.circular(tokens.radiusDefault),65        ),66        child: horizontal67            ? Row(68                mainAxisAlignment: MainAxisAlignment.spaceBetween,69                children: [valueText, labelText],70              )71            : Column(72                crossAxisAlignment: CrossAxisAlignment.start,73                mainAxisAlignment: MainAxisAlignment.spaceBetween,74                children: [valueText, labelText],75              ),76      ),77    );78  }79  _ProfileStatColors _colorsFor(80    DesignTokens tokens,81    ProfileStatType type,82    ProfileStatState state,83  ) {84    final isPrimary = type == ProfileStatType.primary;85    switch (state) {86      case ProfileStatState.default_:87        return isPrimary88            ? _ProfileStatColors(tokens.accent, tokens.ink)89            : _ProfileStatColors(tokens.ink, tokens.accent);90      case ProfileStatState.pressed:91        return isPrimary92            ? _ProfileStatColors(tokens.accentPressed, tokens.ink)93            : _ProfileStatColors(tokens.raised, tokens.accent);94      case ProfileStatState.disabled:95        return _ProfileStatColors(tokens.muted, tokens.raised);96    }97  }98}99100class _ProfileStatColors {101  const _ProfileStatColors(this.fill, this.text);102103  final Color fill;104  final Color text;105}106