Refactor screens to MVC architecture with modular widgets

- Extract business logic from screens into dedicated controllers
- Split large screen files into smaller, reusable widget components
- Add controllers for AddSubscriptionScreen and DetailScreen
- Create modular widgets for subscription and detail features
- Improve code organization and maintainability
- Remove duplicated code and improve reusability

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
JiWoong Sul
2025-07-11 00:21:18 +09:00
parent 4731288622
commit 83c5e3d64e
56 changed files with 9092 additions and 4579 deletions

View File

@@ -0,0 +1,112 @@
import 'package:flutter/material.dart';
/// 주요 액션에 사용되는 Primary 버튼
/// 저장, 추가, 확인 등의 주요 액션에 사용됩니다.
class PrimaryButton extends StatefulWidget {
final String text;
final VoidCallback? onPressed;
final bool isLoading;
final IconData? icon;
final double? width;
final double height;
final Color? backgroundColor;
final Color? foregroundColor;
final double fontSize;
final EdgeInsetsGeometry? padding;
final double borderRadius;
final bool enableHoverEffect;
const PrimaryButton({
super.key,
required this.text,
this.onPressed,
this.isLoading = false,
this.icon,
this.width,
this.height = 60,
this.backgroundColor,
this.foregroundColor,
this.fontSize = 18,
this.padding,
this.borderRadius = 16,
this.enableHoverEffect = true,
});
@override
State<PrimaryButton> createState() => _PrimaryButtonState();
}
class _PrimaryButtonState extends State<PrimaryButton> {
bool _isHovered = false;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final effectiveBackgroundColor = widget.backgroundColor ?? theme.primaryColor;
final effectiveForegroundColor = widget.foregroundColor ?? Colors.white;
Widget button = AnimatedContainer(
duration: const Duration(milliseconds: 200),
width: widget.width ?? double.infinity,
height: widget.height,
transform: widget.enableHoverEffect && _isHovered
? (Matrix4.identity()..scale(1.02))
: Matrix4.identity(),
child: ElevatedButton(
onPressed: widget.isLoading ? null : widget.onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: effectiveBackgroundColor,
foregroundColor: effectiveForegroundColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(widget.borderRadius),
),
padding: widget.padding ?? const EdgeInsets.symmetric(vertical: 16),
elevation: widget.enableHoverEffect && _isHovered ? 8 : 4,
shadowColor: effectiveBackgroundColor.withOpacity(0.5),
disabledBackgroundColor: effectiveBackgroundColor.withOpacity(0.6),
),
child: widget.isLoading
? SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2.5,
color: effectiveForegroundColor,
),
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
if (widget.icon != null) ...[
Icon(
widget.icon,
color: effectiveForegroundColor,
size: _isHovered ? 24 : 20,
),
const SizedBox(width: 8),
],
Text(
widget.text,
style: TextStyle(
fontSize: widget.fontSize,
fontWeight: FontWeight.w600,
color: effectiveForegroundColor,
),
),
],
),
),
);
if (widget.enableHoverEffect) {
return MouseRegion(
onEnter: (_) => setState(() => _isHovered = true),
onExit: (_) => setState(() => _isHovered = false),
child: button,
);
}
return button;
}
}