Files
hum-flutter/lib/widgets/widget_buttons.dart
2025-12-12 14:31:36 -05:00

314 lines
8.2 KiB
Dart

import 'package:flutter/cupertino.dart';
import 'package:hum/core/constants/app_theme.dart';
import 'package:phosphor_flutter/phosphor_flutter.dart';
class BTNSquareBG extends StatelessWidget {
final IconData icon;
final VoidCallback action;
const BTNSquareBG({super.key, required this.icon, required this.action});
@override
Widget build(BuildContext context) {
return CupertinoButton(
color: CupertinoDynamicColor.resolve(colorBarButton, context),
borderRadius: BorderRadius.circular(10),
padding: EdgeInsets.zero,
onPressed: action,
child: Icon(
size: 18,
color: CupertinoDynamicColor.resolve(
colorAccentSecondary, // 👈 define as dynamic in your theme
context,
),
icon,
),
);
}
}
class BTNRoundBG extends StatelessWidget {
final IconData icon;
final VoidCallback action;
const BTNRoundBG({super.key, required this.icon, required this.action});
@override
Widget build(BuildContext context) {
return CupertinoButton(
color: CupertinoDynamicColor.resolve(colorBarButton, context),
borderRadius: BorderRadius.circular(16),
padding: EdgeInsets.zero,
onPressed: action,
child: Icon(
size: 18,
color: CupertinoDynamicColor.resolve(
colorAccentSecondary, // 👈 define as dynamic in your theme
context,
),
icon,
),
);
}
}
class BTNFilled extends StatelessWidget {
const BTNFilled({
super.key,
this.text = '',
this.width = double.infinity,
this.color = colorAccentSecondary,
required this.action,
});
final String text;
final double width;
final VoidCallback action;
final Color color;
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
child: CupertinoButton.filled(
color: color,
borderRadius: BorderRadius.circular(roundLarge),
onPressed: action,
child: Text(text),
),
);
}
}
class BTNFilledIcon extends StatelessWidget {
const BTNFilledIcon({
super.key,
this.text = '',
this.width = double.infinity,
this.color = colorAccentSecondary,
this.alignment = MainAxisAlignment.start,
required this.icon,
required this.action,
});
final String text;
final double width;
final VoidCallback action;
final Color color;
final IconData icon;
final MainAxisAlignment alignment;
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
child: CupertinoButton.filled(
color: color,
borderRadius: BorderRadius.circular(roundLarge),
onPressed: action,
child: Row(
spacing: 14,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: alignment,
children: [PhosphorIcon(icon), Text(text)],
),
),
);
}
}
class BTNComplex extends StatelessWidget {
const BTNComplex({
super.key,
this.text = 'text',
this.label = 'label',
this.width = double.infinity,
this.color = colorAccentSecondary,
this.alignment = MainAxisAlignment.start,
this.trailingIcon = CupertinoIcons.right_chevron,
this.textColor = CupertinoColors.white,
required this.iconLeading,
required this.action,
});
final String label;
final String text;
final double width;
final VoidCallback action;
final Color color;
final IconData iconLeading;
final IconData trailingIcon;
final MainAxisAlignment alignment;
final Color textColor;
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
child: CupertinoButton.filled(
color: color,
borderRadius: BorderRadius.circular(roundLarge),
onPressed: action,
child: Row(
spacing: 14,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: alignment,
children: [
Icon(iconLeading),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Opacity(opacity: .6, child: Text(label, style: TextStyle(fontSize: 14))),
Text(text, style: TextStyle(color: textColor)),
],
),
Spacer(),
Opacity(opacity: .4, child: Icon(trailingIcon, size: 18)),
],
),
),
);
}
}
class BTNFilledAnimated extends StatelessWidget {
const BTNFilledAnimated({
super.key,
this.text = '',
this.width = double.infinity,
this.color = colorAccentSecondary,
this.working = false,
required this.action,
});
final String text;
final double width;
final Color color;
final bool working;
final VoidCallback action;
@override
Widget build(BuildContext context) {
const double spinnerSize = 16;
const double gap = 8;
const double slotWidth = spinnerSize + gap; // space reserved on both sides
return SizedBox(
width: width,
child: CupertinoButton.filled(
color: color,
borderRadius: BorderRadius.circular(roundLarge),
// Disable tap while working (optional UX)
onPressed: working ? null : action,
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
// right slot shows spinner when working, otherwise keeps same width
SizedBox(
width: slotWidth,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
transitionBuilder: (child, anim) => FadeTransition(opacity: anim, child: child),
child: working
? Align(
alignment: Alignment.centerRight,
key: const ValueKey('spinner'),
child: Padding(
padding: const EdgeInsets.only(left: gap),
child: CupertinoActivityIndicator(radius: spinnerSize / 2),
),
)
: const SizedBox(key: ValueKey('no-spinner')),
),
),
// centered label
Expanded(child: Center(child: Text(text))),
// left spacer to keep text perfectly centered
const SizedBox(width: slotWidth),
],
),
),
);
}
}
class BTNText extends StatelessWidget {
const BTNText({super.key, this.text = '', required this.action});
final VoidCallback action;
final String text;
@override
Widget build(BuildContext context) {
return CupertinoButton(onPressed: action, child: Text(text));
}
}
// CONTEXT MENU BUTTON
class MenuAction {
final String label;
final IconData? icon;
final VoidCallback onPressed;
final bool destructive;
const MenuAction({
required this.label,
required this.onPressed,
this.icon,
this.destructive = false,
});
}
class CupertinoMoreButton extends StatelessWidget {
final List<MenuAction> actions;
final double iconSize;
final EdgeInsetsGeometry padding;
const CupertinoMoreButton({
super.key,
required this.actions,
this.iconSize = 22,
this.padding = EdgeInsets.zero,
});
@override
Widget build(BuildContext context) {
return CupertinoButton(
padding: padding,
child: Icon(CupertinoIcons.ellipsis_vertical, size: iconSize),
onPressed: () => _showMenu(context),
);
}
void _showMenu(BuildContext context) {
showCupertinoModalPopup<void>(
context: context,
builder: (ctx) => CupertinoActionSheet(
actions: actions.map((a) {
final text = Text(a.label);
final row = a.icon == null
? text
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Icon(a.icon, size: 18), const SizedBox(width: 8), text],
);
return CupertinoActionSheetAction(
isDestructiveAction: a.destructive,
onPressed: () {
Navigator.of(ctx).pop();
a.onPressed();
},
child: row,
);
}).toList(),
cancelButton: CupertinoActionSheetAction(
onPressed: () => Navigator.of(ctx).pop(),
isDefaultAction: true,
child: const Text('Cancel'),
),
),
);
}
}