314 lines
8.2 KiB
Dart
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'),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|