baseproject

This commit is contained in:
Gaurav Kumar
2025-09-06 19:21:52 +05:30
commit 01be9df2ed
254 changed files with 28342 additions and 0 deletions

View File

@@ -0,0 +1,342 @@
import 'package:flutter/material.dart';
import '../../../core/constants/ui_constants.dart';
enum ModernButtonType {
primary,
secondary,
outline,
text,
danger,
}
enum ModernButtonSize {
small,
medium,
large,
}
class ModernButton extends StatefulWidget {
final String text;
final VoidCallback? onPressed;
final ModernButtonType type;
final ModernButtonSize size;
final bool isLoading;
final bool isDisabled;
final Widget? icon;
final bool isIconOnly;
final double? width;
final double? height;
final EdgeInsetsGeometry? padding;
final BorderRadius? borderRadius;
final String? tooltip;
const ModernButton({
super.key,
required this.text,
this.onPressed,
this.type = ModernButtonType.primary,
this.size = ModernButtonSize.medium,
this.isLoading = false,
this.isDisabled = false,
this.icon,
this.isIconOnly = false,
this.width,
this.height,
this.padding,
this.borderRadius,
this.tooltip,
});
@override
State<ModernButton> createState() => _ModernButtonState();
}
class _ModernButtonState extends State<ModernButton>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _scaleAnimation;
late Animation<double> _elevationAnimation;
bool _isPressed = false;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: UIConstants.durationFast,
vsync: this,
);
_scaleAnimation = Tween<double>(
begin: 1.0,
end: 0.95,
).animate(CurvedAnimation(
parent: _animationController,
curve: UIConstants.curveFast,
));
_elevationAnimation = Tween<double>(
begin: 1.0,
end: 0.0,
).animate(CurvedAnimation(
parent: _animationController,
curve: UIConstants.curveFast,
));
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
void _onTapDown(TapDownDetails details) {
if (!widget.isDisabled && !widget.isLoading) {
setState(() {
_isPressed = true;
});
_animationController.forward();
}
}
void _onTapUp(TapUpDetails details) {
if (!widget.isDisabled && !widget.isLoading) {
setState(() {
_isPressed = false;
});
_animationController.reverse();
}
}
void _onTapCancel() {
if (!widget.isDisabled && !widget.isLoading) {
setState(() {
_isPressed = false;
});
_animationController.reverse();
}
}
bool get _canPress =>
!widget.isDisabled && !widget.isLoading && widget.onPressed != null;
Color _getBackgroundColor(ThemeData theme) {
if (!_canPress) {
return theme.colorScheme.surfaceVariant;
}
switch (widget.type) {
case ModernButtonType.primary:
return theme.colorScheme.primary;
case ModernButtonType.secondary:
return theme.colorScheme.secondary;
case ModernButtonType.outline:
case ModernButtonType.text:
return Colors.transparent;
case ModernButtonType.danger:
return theme.colorScheme.error;
}
}
Color _getTextColor(ThemeData theme) {
if (!_canPress) {
return theme.colorScheme.onSurfaceVariant;
}
switch (widget.type) {
case ModernButtonType.primary:
case ModernButtonType.secondary:
case ModernButtonType.danger:
return theme.colorScheme.onPrimary;
case ModernButtonType.outline:
return theme.colorScheme.primary;
case ModernButtonType.text:
return theme.colorScheme.primary;
}
}
Color _getBorderColor(ThemeData theme) {
if (!_canPress) {
return theme.colorScheme.outline;
}
switch (widget.type) {
case ModernButtonType.outline:
return theme.colorScheme.primary;
case ModernButtonType.danger:
return theme.colorScheme.error;
default:
return Colors.transparent;
}
}
double _getHeight() {
if (widget.height != null) return widget.height!;
switch (widget.size) {
case ModernButtonSize.small:
return UIConstants.buttonHeightSmall +
4; // extra room to avoid text clip
case ModernButtonSize.medium:
return UIConstants.buttonHeightMedium + 4;
case ModernButtonSize.large:
return UIConstants.buttonHeightLarge + 4;
}
}
EdgeInsetsGeometry _getPadding() {
if (widget.padding != null) return widget.padding!;
switch (widget.size) {
case ModernButtonSize.small:
return const EdgeInsets.symmetric(
horizontal: UIConstants.spacing16,
vertical: UIConstants.spacing6,
);
case ModernButtonSize.medium:
return const EdgeInsets.symmetric(
horizontal: UIConstants.spacing24,
vertical: UIConstants.spacing14,
);
case ModernButtonSize.large:
return const EdgeInsets.symmetric(
horizontal: UIConstants.spacing32,
vertical: UIConstants.spacing18,
);
}
}
double _getBorderRadius() {
if (widget.borderRadius != null) {
return widget.borderRadius!.topLeft.x;
}
switch (widget.size) {
case ModernButtonSize.small:
return UIConstants.radius8;
case ModernButtonSize.medium:
return UIConstants.radius16;
case ModernButtonSize.large:
return UIConstants.radius20;
}
}
TextStyle _getTextStyle(ThemeData theme) {
switch (widget.size) {
case ModernButtonSize.small:
return theme.textTheme.labelMedium?.copyWith(
fontWeight: FontWeight.w600,
) ??
const TextStyle();
case ModernButtonSize.medium:
return theme.textTheme.labelLarge?.copyWith(
fontWeight: FontWeight.w600,
) ??
const TextStyle();
case ModernButtonSize.large:
return theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
) ??
const TextStyle();
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
return AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return Transform.scale(
scale: _scaleAnimation.value,
child: GestureDetector(
onTapDown: _onTapDown,
onTapUp: _onTapUp,
onTapCancel: _onTapCancel,
child: Container(
width: widget.width,
height: _getHeight(),
decoration: BoxDecoration(
color: _getBackgroundColor(theme),
borderRadius: BorderRadius.circular(_getBorderRadius()),
border: Border.all(
color: _getBorderColor(theme),
width: widget.type == ModernButtonType.outline ? 1.5 : 0,
),
boxShadow: _canPress
? [
BoxShadow(
color: _getBackgroundColor(theme).withOpacity(0.3),
blurRadius: 8 * _elevationAnimation.value,
offset: Offset(0, 4 * _elevationAnimation.value),
),
]
: null,
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: _canPress ? widget.onPressed : null,
borderRadius: BorderRadius.circular(_getBorderRadius()),
child: Container(
padding: _getPadding(),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (widget.isLoading) ...[
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
_getTextColor(theme),
),
),
),
if (!widget.isIconOnly) ...[
const SizedBox(width: UIConstants.spacing12),
],
] else ...[
if (widget.icon != null && !widget.isIconOnly) ...[
IconTheme(
data: IconThemeData(
color: _getTextColor(theme),
size: _getHeight() * 0.4,
),
child: widget.icon!,
),
const SizedBox(width: UIConstants.spacing12),
],
],
if (!widget.isIconOnly) ...[
Flexible(
child: Text(
widget.text,
style: _getTextStyle(theme).copyWith(
color: _getTextColor(theme),
height: 1.2,
),
strutStyle: const StrutStyle(
height: 1.2,
forceStrutHeight: true,
),
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
),
),
],
],
),
),
),
),
),
),
);
},
);
}
}

View File

@@ -0,0 +1,176 @@
import 'package:flutter/material.dart';
import '../../../core/constants/ui_constants.dart';
class QuickActionButton extends StatefulWidget {
final String label;
final IconData icon;
final VoidCallback? onTap;
final Color? backgroundColor;
final Color? iconColor;
final Color? labelColor;
final double? size;
final bool isLoading;
final bool isDisabled;
const QuickActionButton({
super.key,
required this.label,
required this.icon,
this.onTap,
this.backgroundColor,
this.iconColor,
this.labelColor,
this.size,
this.isLoading = false,
this.isDisabled = false,
});
@override
State<QuickActionButton> createState() => _QuickActionButtonState();
}
class _QuickActionButtonState extends State<QuickActionButton>
with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _scaleAnimation;
late Animation<double> _elevationAnimation;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: UIConstants.durationFast,
vsync: this,
);
_scaleAnimation = Tween<double>(
begin: 1.0,
end: 0.95,
).animate(CurvedAnimation(
parent: _animationController,
curve: UIConstants.curveFast,
));
_elevationAnimation = Tween<double>(
begin: 1.0,
end: 0.0,
).animate(CurvedAnimation(
parent: _animationController,
curve: UIConstants.curveFast,
));
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
void _onTapDown(TapDownDetails details) {
if (widget.onTap != null && !widget.isDisabled && !widget.isLoading) {
_animationController.forward();
}
}
void _onTapUp(TapUpDetails details) {
if (widget.onTap != null && !widget.isDisabled && !widget.isLoading) {
_animationController.reverse();
}
}
void _onTapCancel() {
if (widget.onTap != null && !widget.isDisabled && !widget.isLoading) {
_animationController.reverse();
}
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorScheme = theme.colorScheme;
final buttonSize = widget.size ?? UIConstants.logoSizeMedium;
final backgroundColor =
widget.backgroundColor ?? colorScheme.primaryContainer;
final iconColor = widget.iconColor ?? colorScheme.onPrimaryContainer;
final labelColor = widget.labelColor ?? colorScheme.onSurface;
return AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return Transform.scale(
scale: _scaleAnimation.value,
child: GestureDetector(
onTapDown: _onTapDown,
onTapUp: _onTapUp,
onTapCancel: _onTapCancel,
child: Container(
width: buttonSize,
height: buttonSize,
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(UIConstants.radius16),
boxShadow: [
BoxShadow(
color: backgroundColor.withOpacity(0.3),
blurRadius: 8 * _elevationAnimation.value,
offset: Offset(0, 4 * _elevationAnimation.value),
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: (widget.onTap != null &&
!widget.isDisabled &&
!widget.isLoading)
? widget.onTap
: null,
borderRadius: BorderRadius.circular(UIConstants.radius16),
child: Padding(
padding: const EdgeInsets.all(UIConstants.spacing12),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Icon
if (widget.isLoading)
SizedBox(
width: buttonSize * 0.3,
height: buttonSize * 0.3,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(iconColor),
),
)
else
Icon(
widget.icon,
color: iconColor,
size: buttonSize * 0.4,
),
const SizedBox(height: UIConstants.spacing8),
// Label
Text(
widget.label,
style: theme.textTheme.labelMedium?.copyWith(
color: labelColor,
fontWeight: FontWeight.w600,
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
),
),
),
),
);
},
);
}
}