button static method

String button()

Implementation

static String button() => r'''
import 'package:flutter/material.dart';

import '../styles/design_style_config.dart';
import '../tokens/app_colors.dart';
import '../tokens/app_gradients.dart';
import '../tokens/app_motion.dart';
import '../tokens/app_radius.dart';
import '../tokens/app_shadows.dart';
import '../tokens/app_sizes.dart';
import '../tokens/app_spacing.dart';
import '../tokens/app_typography.dart';

enum AppButtonVariant {
primary,
secondary,
tertiary,
outline,
text,
destructive,
}

class AppButton extends StatelessWidget {
const AppButton({
  super.key,
  required this.label,
  required this.onPressed,
  this.variant = AppButtonVariant.primary,
  this.loading = false,
  this.icon,
  this.expanded = false,
});

final String label;
final VoidCallback? onPressed;
final AppButtonVariant variant;
final bool loading;
final IconData? icon;
final bool expanded;

bool get _enabled => onPressed != null && !loading;

@override
Widget build(BuildContext context) {
  final child = _content(context);
  return Semantics(
    button: true,
    enabled: _enabled,
    label: label,
    child: ConstrainedBox(
      constraints: const BoxConstraints(
        minHeight: AppSizes.minTapTarget,
        minWidth: AppSizes.minTapTarget,
      ),
      child: child,
    ),
  );
}

Widget _content(BuildContext context) {
  final isDark = Theme.of(context).brightness == Brightness.dark;
  final bg = _background(isDark);
  final fg = _foreground(isDark);

  Widget body = Row(
    mainAxisSize: expanded ? MainAxisSize.max : MainAxisSize.min,
    mainAxisAlignment: MainAxisAlignment.center,
    children: <Widget>[
      if (loading)
        SizedBox(
          width: AppSizes.iconSm,
          height: AppSizes.iconSm,
          child: CircularProgressIndicator(
            strokeWidth: 2,
            color: fg,
          ),
        )
      else if (icon != null) ...<Widget>[
        Icon(icon, size: AppSizes.iconSm, color: fg),
        const SizedBox(width: AppSpacing.sm),
      ],
      if (!loading)
        Flexible(
          child: Text(
            label,
            style: AppTypography.button.copyWith(color: fg),
            overflow: TextOverflow.ellipsis,
          ),
        ),
    ],
  );

  body = Padding(
    padding: const EdgeInsets.symmetric(
      horizontal: AppSpacing.lg,
      vertical: AppSpacing.sm,
    ),
    child: body,
  );

  final shape = RoundedRectangleBorder(
    borderRadius: BorderRadius.circular(
      AppStyleConfig.usesHardOffsetShadow ? AppRadius.none : AppRadius.md,
    ),
    side: variant == AppButtonVariant.outline ||
            AppStyleConfig.usesHardOffsetShadow
        ? BorderSide(
            color: variant == AppButtonVariant.destructive
                ? AppColors.error
                : (isDark ? AppColorsDark.outline : AppColors.outline),
            width: AppStyleConfig.usesHardOffsetShadow ? 3 : 1.5,
          )
        : BorderSide.none,
  );

  return AnimatedOpacity(
    duration: AppMotion.fast,
    opacity: _enabled ? 1 : 0.5,
    child: Material(
      color: AppStyleConfig.prefersGradientButtons &&
              variant == AppButtonVariant.primary
          ? Colors.transparent
          : bg,
      shape: shape,
      clipBehavior: Clip.antiAlias,
      elevation: 0,
      child: InkWell(
        onTap: _enabled ? onPressed : null,
        customBorder: shape,
        child: Ink(
          decoration: AppStyleConfig.prefersGradientButtons &&
                  variant == AppButtonVariant.primary
              ? BoxDecoration(
                  gradient: AppGradients.primary,
                  borderRadius: BorderRadius.circular(AppRadius.md),
                  boxShadow: AppStyleConfig.usesClayInflation
                      ? AppShadows.clay
                      : AppStyleConfig.usesNeoShadows
                          ? AppShadows.neoRaised
                          : null,
                )
              : BoxDecoration(
                  color: bg,
                  borderRadius: BorderRadius.circular(
                    AppStyleConfig.usesHardOffsetShadow
                        ? AppRadius.none
                        : AppRadius.md,
                  ),
                  boxShadow: _shadows(),
                ),
          child: body,
        ),
      ),
    ),
  );
}

List<BoxShadow>? _shadows() {
  if (variant == AppButtonVariant.text ||
      variant == AppButtonVariant.tertiary) {
    return null;
  }
  if (AppStyleConfig.usesNeoShadows) {
    return AppShadows.neoRaised;
  }
  if (AppStyleConfig.usesHardOffsetShadow) {
    return AppShadows.hardOffset;
  }
  if (AppStyleConfig.usesClayInflation) {
    return AppShadows.clay;
  }
  return AppShadows.low;
}

Color _background(bool isDark) {
  switch (variant) {
    case AppButtonVariant.primary:
      return isDark ? AppColorsDark.primary : AppColors.primary;
    case AppButtonVariant.secondary:
      return isDark
          ? AppColorsDark.surfaceVariant
          : AppColors.surfaceVariant;
    case AppButtonVariant.tertiary:
    case AppButtonVariant.text:
    case AppButtonVariant.outline:
      return Colors.transparent;
    case AppButtonVariant.destructive:
      return isDark ? AppColorsDark.error : AppColors.error;
  }
}

Color _foreground(bool isDark) {
  switch (variant) {
    case AppButtonVariant.primary:
      return isDark ? AppColorsDark.onPrimary : AppColors.onPrimary;
    case AppButtonVariant.destructive:
      return isDark ? AppColorsDark.onPrimary : AppColors.onPrimary;
    case AppButtonVariant.secondary:
    case AppButtonVariant.tertiary:
    case AppButtonVariant.outline:
    case AppButtonVariant.text:
      return isDark ? AppColorsDark.textPrimary : AppColors.textPrimary;
  }
}
}
''';