Files
GroundedHelper/frontend/lib/Grounded/designs/buttons/Buttons.dart
alvocool d3431ca5de Switch to Inter, round the corners softly and make the nav switch tabs
Inter replaces General Sans (Inter Display for titles and big numbers).
Corners come back from before the squaring commit, scaled to a soft set
(16 cards, 14 buttons, 12 rows, 8 chips) and applied to the screens built
since. The nav is now a cluster of round frosted buttons that switches
Today, Routines, Goals, Groups and Progress in place inside Home: no
pushed pages, no back arrows, and Android back returns to Today.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KhQh614DMeYoQEH9SoMRUw
2026-11-09 07:08:46 +03:00

181 lines
4.8 KiB
Dart

import 'package:flutter/material.dart';
import '../../about/internal/application/TextType.dart';
import '../../utils/Colors.dart';
import '../text/Text.dart';
/// The primary action. One per screen — if a screen appears to need two, one
/// of them is secondary.
Widget roundedCornerButton(
String label,
VoidCallback onPressed, {
Color? background,
Color? foreground,
IconData? icon,
bool enabled = true,
double radius = 14,
double verticalPadding = 16,
}) {
final Color bg = enabled ? (background ?? colorNight) : colorGrey;
final Color fg = foreground ?? colorWhite;
return ElevatedButton(
onPressed: enabled ? onPressed : null,
style: ElevatedButton.styleFrom(
elevation: 0,
backgroundColor: bg,
disabledBackgroundColor: colorGrey.withValues(alpha: 0.4),
padding: EdgeInsets.symmetric(vertical: verticalPadding),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radius),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (icon != null) ...[
// The default night pill carries the lime spark on its icon.
Icon(icon,
size: 16,
color: background == null && foreground == null ? colorLime : fg),
const SizedBox(width: 8),
],
text(label, 14, TextType.Medium, color: fg),
],
),
);
}
/// The secondary action — outlined, never filled, so the hierarchy is never
/// ambiguous.
Widget outlinedActionButton(
String label,
VoidCallback onPressed, {
Color? foreground,
IconData? icon,
bool enabled = true,
double radius = 14,
}) {
final Color fg = enabled ? (foreground ?? colorPrimaryDark) : colorGrey;
return OutlinedButton(
onPressed: enabled ? onPressed : null,
style: OutlinedButton.styleFrom(
side: BorderSide(color: fg.withValues(alpha: 0.35), width: 1),
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(radius),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (icon != null) ...[
Icon(icon, size: 16, color: fg),
const SizedBox(width: 8),
],
text(label, 14, TextType.Bold, color: fg),
],
),
);
}
Widget textButton(
String label,
VoidCallback onPressed, {
Color? color,
double textSize = 13,
TextType type = TextType.Regular,
}) {
return TextButton(
onPressed: onPressed,
child: text(label, textSize, type, color: color ?? colorGrey2),
);
}
Widget iconButton(
Widget icon,
VoidCallback onPressed, {
bool bordered = false,
Color? borderColor,
Color? background,
double radius = 10,
double size = 38,
}) {
return GestureDetector(
onTap: onPressed,
child: Container(
width: size,
height: size,
alignment: Alignment.center,
decoration: BoxDecoration(
color: background ?? Colors.transparent,
borderRadius: BorderRadius.circular(radius),
border: bordered
? Border.all(color: borderColor ?? colorBorder, width: 1)
: null,
),
child: icon,
),
);
}
/// The destructive action — abandoning, which costs the most debt of all and
/// so is always visually distinct from completing.
Widget destructiveButton(
String label,
VoidCallback onPressed, {
IconData? icon,
bool enabled = true,
}) {
return roundedCornerButton(
label,
onPressed,
background: colorDestructive,
foreground: colorWhite,
icon: icon,
enabled: enabled,
);
}
/// A segmented selector, used for class, energy, proof type and tone.
Widget segmentedSelector<T>({
required List<T> options,
required T selected,
required String Function(T) label,
required void Function(T) onSelected,
Color? activeColor,
}) {
final Color active = activeColor ?? colorPrimaryDark;
return Wrap(
spacing: 8,
runSpacing: 8,
children: options.map((option) {
final bool isSelected = option == selected;
return GestureDetector(
onTap: () => onSelected(option),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: isSelected ? active : colorWhite,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isSelected ? active : colorBorder,
width: 1,
),
),
child: text(
label(option),
12,
isSelected ? TextType.Bold : TextType.Regular,
color: isSelected ? colorWhite : colorGrey2,
),
),
);
}).toList(),
);
}