feat: adopt material 3 theme and billing adjustments
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import '../../../theme/app_colors.dart';
|
||||
// import '../../../theme/app_colors.dart';
|
||||
|
||||
/// 공통 텍스트 필드 위젯
|
||||
/// 프로젝트 전체에서 일관된 스타일의 텍스트 입력 필드를 제공합니다.
|
||||
@@ -66,10 +66,10 @@ class BaseTextField extends StatelessWidget {
|
||||
if (label != null) ...[
|
||||
Text(
|
||||
label!,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.textSecondary,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -89,22 +89,22 @@ class BaseTextField extends StatelessWidget {
|
||||
maxLines: maxLines,
|
||||
minLines: minLines,
|
||||
readOnly: readOnly,
|
||||
cursorColor: cursorColor ?? theme.primaryColor,
|
||||
cursorColor: cursorColor ?? theme.colorScheme.primary,
|
||||
style: style ??
|
||||
const TextStyle(
|
||||
TextStyle(
|
||||
fontSize: 16,
|
||||
color: AppColors.textPrimary,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintText: hintText,
|
||||
hintStyle: const TextStyle(
|
||||
color: AppColors.textMuted,
|
||||
hintStyle: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
prefixIcon: prefixIcon,
|
||||
prefixText: prefixText,
|
||||
suffixIcon: suffixIcon,
|
||||
filled: true,
|
||||
fillColor: fillColor ?? AppColors.surfaceColorAlt,
|
||||
fillColor: fillColor ?? Theme.of(context).colorScheme.surface,
|
||||
contentPadding: contentPadding ?? const EdgeInsets.all(16),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
@@ -113,15 +113,15 @@ class BaseTextField extends StatelessWidget {
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderSide: BorderSide(
|
||||
color: theme.primaryColor,
|
||||
color: theme.colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderSide: BorderSide(
|
||||
color: AppColors.borderColor.withValues(alpha: 0.7),
|
||||
width: 1.5,
|
||||
color: theme.colorScheme.outline.withValues(alpha: 0.6),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
disabledBorder: OutlineInputBorder(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../theme/app_colors.dart';
|
||||
// import '../../../theme/app_colors.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
|
||||
/// 결제 주기 선택 위젯
|
||||
@@ -8,8 +8,8 @@ class BillingCycleSelector extends StatelessWidget {
|
||||
final String billingCycle;
|
||||
final ValueChanged<String> onChanged;
|
||||
final Color? baseColor;
|
||||
final List<Color>? gradientColors;
|
||||
final bool isGlassmorphism;
|
||||
final List<Color>? gradientColors; // deprecated: ignored
|
||||
final bool isGlassmorphism; // deprecated: ignored
|
||||
|
||||
const BillingCycleSelector({
|
||||
super.key,
|
||||
@@ -24,19 +24,12 @@ class BillingCycleSelector extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalizations.of(context);
|
||||
// 상세 화면에서는 '매월', 추가 화면에서는 '월간'으로 표시
|
||||
final cycles = isGlassmorphism
|
||||
? [
|
||||
localization.billingCycleMonthly,
|
||||
localization.billingCycleQuarterly,
|
||||
localization.billingCycleHalfYearly,
|
||||
localization.billingCycleYearly,
|
||||
]
|
||||
: [
|
||||
localization.monthly,
|
||||
localization.billingCycleQuarterly,
|
||||
localization.billingCycleHalfYearly,
|
||||
localization.yearly,
|
||||
];
|
||||
final cycles = [
|
||||
localization.monthly,
|
||||
localization.billingCycleQuarterly,
|
||||
localization.billingCycleHalfYearly,
|
||||
localization.yearly,
|
||||
];
|
||||
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
@@ -54,16 +47,16 @@ class BillingCycleSelector extends StatelessWidget {
|
||||
vertical: 12,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: _getBackgroundColor(isSelected),
|
||||
color: _getBackgroundColor(context, isSelected),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: _getBorder(isSelected),
|
||||
border: _getBorder(context, isSelected),
|
||||
),
|
||||
child: Text(
|
||||
cycle,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: _getTextColor(isSelected),
|
||||
color: _getTextColor(context, isSelected),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -74,38 +67,22 @@ class BillingCycleSelector extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Color _getBackgroundColor(bool isSelected) {
|
||||
if (!isSelected) {
|
||||
return isGlassmorphism
|
||||
? AppColors.backgroundColor
|
||||
: Colors.grey.withValues(alpha: 0.1);
|
||||
}
|
||||
|
||||
if (baseColor != null) {
|
||||
return baseColor!;
|
||||
}
|
||||
|
||||
if (gradientColors != null && gradientColors!.isNotEmpty) {
|
||||
return gradientColors![0];
|
||||
}
|
||||
|
||||
return const Color(0xFF3B82F6);
|
||||
}
|
||||
|
||||
Border? _getBorder(bool isSelected) {
|
||||
if (isSelected || !isGlassmorphism) {
|
||||
return null;
|
||||
}
|
||||
return Border.all(
|
||||
color: AppColors.borderColor.withValues(alpha: 0.5),
|
||||
width: 1.5,
|
||||
);
|
||||
}
|
||||
|
||||
Color _getTextColor(bool isSelected) {
|
||||
Color _getBackgroundColor(BuildContext context, bool isSelected) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
if (isSelected) {
|
||||
return Colors.white;
|
||||
return baseColor ?? scheme.primary;
|
||||
}
|
||||
return isGlassmorphism ? AppColors.darkNavy : Colors.grey[700]!;
|
||||
return scheme.surface;
|
||||
}
|
||||
|
||||
Border? _getBorder(BuildContext context, bool isSelected) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
if (isSelected) return null;
|
||||
return Border.all(color: scheme.outline.withValues(alpha: 0.6), width: 1);
|
||||
}
|
||||
|
||||
Color _getTextColor(BuildContext context, bool isSelected) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
return isSelected ? scheme.onPrimary : scheme.onSurface;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../../theme/app_colors.dart';
|
||||
// import '../../../theme/app_colors.dart';
|
||||
import '../../../providers/category_provider.dart';
|
||||
|
||||
/// 카테고리 선택 위젯
|
||||
@@ -10,8 +10,8 @@ class CategorySelector extends StatelessWidget {
|
||||
final String? selectedCategoryId;
|
||||
final ValueChanged<String?> onChanged;
|
||||
final Color? baseColor;
|
||||
final List<Color>? gradientColors;
|
||||
final bool isGlassmorphism;
|
||||
final List<Color>? gradientColors; // deprecated: ignored
|
||||
final bool isGlassmorphism; // deprecated: ignored
|
||||
|
||||
const CategorySelector({
|
||||
super.key,
|
||||
@@ -39,9 +39,9 @@ class CategorySelector extends StatelessWidget {
|
||||
vertical: 10,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: _getBackgroundColor(isSelected),
|
||||
color: _getBackgroundColor(context, isSelected),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: _getBorder(isSelected),
|
||||
border: _getBorder(context, isSelected),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -49,7 +49,7 @@ class CategorySelector extends StatelessWidget {
|
||||
Icon(
|
||||
_getCategoryIcon(category),
|
||||
size: 18,
|
||||
color: _getTextColor(isSelected),
|
||||
color: _getTextColor(context, isSelected),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Consumer<CategoryProvider>(
|
||||
@@ -60,7 +60,7 @@ class CategorySelector extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: _getTextColor(isSelected),
|
||||
color: _getTextColor(context, isSelected),
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -100,38 +100,22 @@ class CategorySelector extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
Color _getBackgroundColor(bool isSelected) {
|
||||
if (!isSelected) {
|
||||
return isGlassmorphism
|
||||
? AppColors.backgroundColor
|
||||
: Colors.grey.withValues(alpha: 0.1);
|
||||
}
|
||||
|
||||
if (baseColor != null) {
|
||||
return baseColor!;
|
||||
}
|
||||
|
||||
if (gradientColors != null && gradientColors!.isNotEmpty) {
|
||||
return gradientColors![0];
|
||||
}
|
||||
|
||||
return const Color(0xFF3B82F6);
|
||||
}
|
||||
|
||||
Border? _getBorder(bool isSelected) {
|
||||
if (isSelected || !isGlassmorphism) {
|
||||
return null;
|
||||
}
|
||||
return Border.all(
|
||||
color: AppColors.borderColor.withValues(alpha: 0.5),
|
||||
width: 1.5,
|
||||
);
|
||||
}
|
||||
|
||||
Color _getTextColor(bool isSelected) {
|
||||
Color _getBackgroundColor(BuildContext context, bool isSelected) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
if (isSelected) {
|
||||
return Colors.white;
|
||||
return baseColor ?? scheme.primary;
|
||||
}
|
||||
return isGlassmorphism ? AppColors.darkNavy : Colors.grey[700]!;
|
||||
return scheme.surface;
|
||||
}
|
||||
|
||||
Border? _getBorder(BuildContext context, bool isSelected) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
if (isSelected) return null;
|
||||
return Border.all(color: scheme.outline.withValues(alpha: 0.6), width: 1);
|
||||
}
|
||||
|
||||
Color _getTextColor(BuildContext context, bool isSelected) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
return isSelected ? scheme.onPrimary : scheme.onSurface;
|
||||
}
|
||||
}
|
||||
|
||||
112
lib/widgets/common/form_fields/currency_dropdown_field.dart
Normal file
112
lib/widgets/common/form_fields/currency_dropdown_field.dart
Normal file
@@ -0,0 +1,112 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CurrencyDropdownField extends StatelessWidget {
|
||||
final String currency;
|
||||
final ValueChanged<String> onChanged;
|
||||
|
||||
const CurrencyDropdownField({
|
||||
super.key,
|
||||
required this.currency,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return DropdownButtonFormField<String>(
|
||||
initialValue: currency,
|
||||
isExpanded: true,
|
||||
icon: const Icon(Icons.keyboard_arrow_down_rounded),
|
||||
// 선택된 아이템은 코드만 간결하게 표시하여 오버플로우 방지
|
||||
selectedItemBuilder: (context) {
|
||||
final color = theme.colorScheme.onSurface;
|
||||
return const [
|
||||
'KRW',
|
||||
'USD',
|
||||
'JPY',
|
||||
'CNY',
|
||||
].map((code) {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
code,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 14, color: color),
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: theme.colorScheme.surface,
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderSide: BorderSide(
|
||||
color: theme.colorScheme.outline.withValues(alpha: 0.6),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
borderSide: BorderSide(
|
||||
color: theme.colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
items: const [
|
||||
DropdownMenuItem(
|
||||
value: 'KRW', child: _CurrencyItem(symbol: '₩', code: 'KRW')),
|
||||
DropdownMenuItem(
|
||||
value: 'USD', child: _CurrencyItem(symbol: '\$', code: 'USD')),
|
||||
DropdownMenuItem(
|
||||
value: 'JPY', child: _CurrencyItem(symbol: '¥', code: 'JPY')),
|
||||
DropdownMenuItem(
|
||||
value: 'CNY', child: _CurrencyItem(symbol: '¥', code: 'CNY')),
|
||||
],
|
||||
onChanged: (val) {
|
||||
if (val != null) onChanged(val);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CurrencyItem extends StatelessWidget {
|
||||
final String symbol;
|
||||
final String code;
|
||||
|
||||
const _CurrencyItem({required this.symbol, required this.code});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final color = Theme.of(context).colorScheme.onSurface;
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
symbol,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
code,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,10 @@ import 'base_text_field.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
|
||||
/// 통화 입력 필드 위젯
|
||||
/// 원화(KRW)와 달러(USD)를 지원하며 자동 포맷팅을 제공합니다.
|
||||
/// KRW/JPY(정수), USD/CNY(소수점 2자리)를 지원하며 자동 포맷팅을 제공합니다.
|
||||
class CurrencyInputField extends StatefulWidget {
|
||||
final TextEditingController controller;
|
||||
final String currency; // 'KRW' or 'USD'
|
||||
final String currency; // 'KRW' | 'USD' | 'JPY' | 'CNY'
|
||||
final String? label;
|
||||
final String? hintText;
|
||||
final Function(double?)? onChanged;
|
||||
@@ -39,6 +39,7 @@ class CurrencyInputField extends StatefulWidget {
|
||||
class _CurrencyInputFieldState extends State<CurrencyInputField> {
|
||||
late FocusNode _focusNode;
|
||||
bool _isFormatted = false;
|
||||
bool _isPostFrameUpdating = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -66,6 +67,29 @@ class _CurrencyInputFieldState extends State<CurrencyInputField> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant CurrencyInputField oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.currency != widget.currency) {
|
||||
// 통화 변경 시 빌드 이후에 안전하게 재포맷 적용
|
||||
if (_focusNode.hasFocus) return;
|
||||
final value = _parseValue(widget.controller.text);
|
||||
if (value == null) return;
|
||||
final formatted = _formatCurrency(value);
|
||||
if (widget.controller.text == formatted || _isPostFrameUpdating) return;
|
||||
_isPostFrameUpdating = true;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
widget.controller.value = TextEditingValue(
|
||||
text: formatted,
|
||||
selection: TextSelection.collapsed(offset: formatted.length),
|
||||
);
|
||||
_isFormatted = true;
|
||||
_isPostFrameUpdating = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onFocusChanged() {
|
||||
if (!_focusNode.hasFocus && widget.controller.text.isNotEmpty) {
|
||||
// 포커스를 잃었을 때 포맷팅 적용
|
||||
@@ -81,7 +105,7 @@ class _CurrencyInputFieldState extends State<CurrencyInputField> {
|
||||
final value = _parseValue(widget.controller.text);
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
if (widget.currency == 'KRW') {
|
||||
if (_isIntegerCurrency(widget.currency)) {
|
||||
widget.controller.text = value.toInt().toString();
|
||||
} else {
|
||||
widget.controller.text = value.toString();
|
||||
@@ -97,7 +121,7 @@ class _CurrencyInputFieldState extends State<CurrencyInputField> {
|
||||
}
|
||||
|
||||
String _formatCurrency(double value) {
|
||||
if (widget.currency == 'KRW') {
|
||||
if (_isIntegerCurrency(widget.currency)) {
|
||||
return NumberFormat.decimalPattern().format(value.toInt());
|
||||
} else {
|
||||
return NumberFormat('#,##0.00').format(value);
|
||||
@@ -108,13 +132,26 @@ class _CurrencyInputFieldState extends State<CurrencyInputField> {
|
||||
final cleanText = text
|
||||
.replaceAll(',', '')
|
||||
.replaceAll('₩', '')
|
||||
.replaceAll('¥', '')
|
||||
.replaceAll('¥', '')
|
||||
.replaceAll('\$', '')
|
||||
.trim();
|
||||
return double.tryParse(cleanText);
|
||||
}
|
||||
|
||||
// ignore: unused_element
|
||||
String get _prefixText {
|
||||
return widget.currency == 'KRW' ? '₩ ' : '\$ ';
|
||||
switch (widget.currency) {
|
||||
case 'KRW':
|
||||
return '₩ ';
|
||||
case 'JPY':
|
||||
return '¥ ';
|
||||
case 'CNY':
|
||||
return '¥ ';
|
||||
case 'USD':
|
||||
default:
|
||||
return '4 ';
|
||||
}
|
||||
}
|
||||
|
||||
String _getDefaultHintText(BuildContext context) {
|
||||
@@ -132,26 +169,27 @@ class _CurrencyInputFieldState extends State<CurrencyInputField> {
|
||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(
|
||||
widget.currency == 'KRW' ? RegExp(r'[0-9]') : RegExp(r'[0-9.]')),
|
||||
if (widget.currency == 'USD')
|
||||
// USD의 경우 소수점 이하 2자리까지만 허용
|
||||
_isIntegerCurrency(widget.currency)
|
||||
? RegExp(r'[0-9]')
|
||||
: RegExp(r'[0-9.]'),
|
||||
),
|
||||
if (!_isIntegerCurrency(widget.currency))
|
||||
// 소수 통화(USD/CNY): 소수점 이하 2자리 제한
|
||||
TextInputFormatter.withFunction((oldValue, newValue) {
|
||||
final text = newValue.text;
|
||||
if (text.isEmpty) return newValue;
|
||||
|
||||
final parts = text.split('.');
|
||||
if (parts.length > 2) {
|
||||
// 소수점이 2개 이상인 경우 거부
|
||||
return oldValue;
|
||||
return oldValue; // 소수점이 2개 이상인 경우 거부
|
||||
}
|
||||
if (parts.length == 2 && parts[1].length > 2) {
|
||||
// 소수점 이하가 2자리를 초과하는 경우 거부
|
||||
return oldValue;
|
||||
return oldValue; // 소수점 이하 2자 초과 거부
|
||||
}
|
||||
return newValue;
|
||||
}),
|
||||
],
|
||||
prefixText: _prefixText,
|
||||
prefixText: _getPrefixText(),
|
||||
onEditingComplete: widget.onEditingComplete,
|
||||
enabled: widget.enabled,
|
||||
onChanged: (value) {
|
||||
@@ -172,3 +210,23 @@ class _CurrencyInputFieldState extends State<CurrencyInputField> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool _isIntegerCurrency(String code) => code == 'KRW' || code == 'JPY';
|
||||
|
||||
// 안전한 프리픽스 계산 함수(모든 통화 지원)
|
||||
String _currencySymbol(String code) {
|
||||
switch (code) {
|
||||
case 'KRW':
|
||||
return '₩';
|
||||
case 'JPY':
|
||||
case 'CNY':
|
||||
return '¥';
|
||||
case 'USD':
|
||||
default:
|
||||
return '\$';
|
||||
}
|
||||
}
|
||||
|
||||
extension on _CurrencyInputFieldState {
|
||||
String _getPrefixText() => '${_currencySymbol(widget.currency)} ';
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../theme/app_colors.dart';
|
||||
// import '../../../theme/app_colors.dart';
|
||||
|
||||
/// 통화 선택 위젯
|
||||
/// KRW(원화), USD(달러), JPY(엔화), CNY(위안화) 중 선택할 수 있습니다.
|
||||
class CurrencySelector extends StatelessWidget {
|
||||
final String currency;
|
||||
final ValueChanged<String> onChanged;
|
||||
final bool isGlassmorphism;
|
||||
final bool isGlassmorphism; // deprecated: ignored
|
||||
|
||||
const CurrencySelector({
|
||||
super.key,
|
||||
@@ -72,7 +72,7 @@ class _CurrencyOption extends StatelessWidget {
|
||||
final String? subtitle;
|
||||
final bool isSelected;
|
||||
final VoidCallback onTap;
|
||||
final bool isGlassmorphism;
|
||||
final bool isGlassmorphism; // deprecated: ignored
|
||||
|
||||
const _CurrencyOption({
|
||||
required this.label,
|
||||
@@ -96,7 +96,7 @@ class _CurrencyOption extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: _getBackgroundColor(theme),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: _getBorder(),
|
||||
border: _getBorder(theme),
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
@@ -107,7 +107,7 @@ class _CurrencyOption extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: _getTextColor(),
|
||||
color: _getTextColor(theme),
|
||||
),
|
||||
),
|
||||
if (subtitle != null) ...[
|
||||
@@ -117,7 +117,7 @@ class _CurrencyOption extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: _getTextColor().withValues(alpha: 0.8),
|
||||
color: _getTextColor(theme).withValues(alpha: 0.8),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -130,28 +130,20 @@ class _CurrencyOption extends StatelessWidget {
|
||||
}
|
||||
|
||||
Color _getBackgroundColor(ThemeData theme) {
|
||||
if (isSelected) {
|
||||
return isGlassmorphism ? theme.primaryColor : const Color(0xFF3B82F6);
|
||||
}
|
||||
return isGlassmorphism
|
||||
? AppColors.surfaceColorAlt
|
||||
: Colors.grey.withValues(alpha: 0.1);
|
||||
final scheme = theme.colorScheme;
|
||||
return isSelected ? scheme.primary : scheme.surface;
|
||||
}
|
||||
|
||||
Border? _getBorder() {
|
||||
if (isSelected || !isGlassmorphism) {
|
||||
return null;
|
||||
}
|
||||
Border? _getBorder(ThemeData theme) {
|
||||
if (isSelected) return null;
|
||||
return Border.all(
|
||||
color: AppColors.borderColor,
|
||||
width: 1.5,
|
||||
color: theme.colorScheme.outline.withValues(alpha: 0.6),
|
||||
width: 1,
|
||||
);
|
||||
}
|
||||
|
||||
Color _getTextColor() {
|
||||
if (isSelected) {
|
||||
return Colors.white;
|
||||
}
|
||||
return isGlassmorphism ? AppColors.navyGray : Colors.grey[600]!;
|
||||
Color _getTextColor(ThemeData theme) {
|
||||
final scheme = theme.colorScheme;
|
||||
return isSelected ? scheme.onPrimary : scheme.onSurface;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../../theme/app_colors.dart';
|
||||
// import '../../../theme/app_colors.dart';
|
||||
import '../../../l10n/app_localizations.dart';
|
||||
|
||||
/// 날짜 선택 필드 위젯
|
||||
@@ -48,10 +48,10 @@ class DatePickerField extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.darkNavy,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -67,13 +67,14 @@ class DatePickerField extends StatelessWidget {
|
||||
lastDate: lastDate ??
|
||||
DateTime.now().add(const Duration(days: 365 * 10)),
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
final cs = Theme.of(context).colorScheme;
|
||||
return Theme(
|
||||
data: ThemeData.light().copyWith(
|
||||
colorScheme: ColorScheme.light(
|
||||
data: Theme.of(context).copyWith(
|
||||
colorScheme: cs.copyWith(
|
||||
primary: effectivePrimaryColor,
|
||||
onPrimary: Colors.white,
|
||||
surface: Colors.white,
|
||||
onSurface: Colors.black,
|
||||
onPrimary: cs.onPrimary,
|
||||
surface: cs.surface,
|
||||
onSurface: cs.onSurface,
|
||||
),
|
||||
),
|
||||
child: child!,
|
||||
@@ -90,10 +91,13 @@ class DatePickerField extends StatelessWidget {
|
||||
child: Container(
|
||||
padding: contentPadding ?? const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor ?? AppColors.surfaceColorAlt,
|
||||
color: backgroundColor ?? Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: AppColors.borderColor.withValues(alpha: 0.7),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.outline
|
||||
.withValues(alpha: 0.6),
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
@@ -105,15 +109,18 @@ class DatePickerField extends StatelessWidget {
|
||||
.format(selectedDate),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color:
|
||||
enabled ? AppColors.textPrimary : AppColors.textMuted,
|
||||
color: enabled
|
||||
? Theme.of(context).colorScheme.onSurface
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
Icon(
|
||||
Icons.calendar_today,
|
||||
size: 20,
|
||||
color: enabled ? AppColors.navyGray : AppColors.textMuted,
|
||||
color: enabled
|
||||
? Theme.of(context).colorScheme.onSurfaceVariant
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -214,13 +221,14 @@ class _DateRangeItem extends StatelessWidget {
|
||||
firstDate: firstDate,
|
||||
lastDate: lastDate,
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
final cs = Theme.of(context).colorScheme;
|
||||
return Theme(
|
||||
data: ThemeData.light().copyWith(
|
||||
colorScheme: ColorScheme.light(
|
||||
data: Theme.of(context).copyWith(
|
||||
colorScheme: cs.copyWith(
|
||||
primary: effectivePrimaryColor,
|
||||
onPrimary: Colors.white,
|
||||
surface: Colors.white,
|
||||
onSurface: Colors.black,
|
||||
onPrimary: cs.onPrimary,
|
||||
surface: cs.surface,
|
||||
onSurface: cs.onSurface,
|
||||
),
|
||||
),
|
||||
child: child!,
|
||||
@@ -237,10 +245,10 @@ class _DateRangeItem extends StatelessWidget {
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surfaceColorAlt,
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: AppColors.borderColor.withValues(alpha: 0.7),
|
||||
color: Theme.of(context).colorScheme.outline.withValues(alpha: 0.6),
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
@@ -249,9 +257,9 @@ class _DateRangeItem extends StatelessWidget {
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.textSecondary,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
@@ -263,8 +271,9 @@ class _DateRangeItem extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color:
|
||||
date != null ? AppColors.textPrimary : AppColors.textMuted,
|
||||
color: date != null
|
||||
? Theme.of(context).colorScheme.onSurface
|
||||
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user