feat(flutter): 시간 선택 시트와 휠·날짜 선택 등 공용 위젯 확장

일정 추가 화면에 필요한 공용 부품을 먼저 넣는다.
- 시간 선택 시트(오전/오후·시·분) — 시·분은 끝없이 돌아가는 휠, 조건이 안 맞으면 시트 안에 안내
- 날짜 선택 시트에 연도 범위 지정 (기간 설정은 올해~2050)
- 큰 알약 토글(일정 관리/다이어리)과 아이콘, 본문 서체 알약 버튼
- "직접 추가" 입력 칩을 공용으로 옮기고, 일반 일정 칩용 큰 × 추가
- HourMinute(시각 값), 주소 등 바깥 값용 날짜 파서

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-28 22:32:00 +09:00
parent 3e6206630f
commit f2a4451b92
10 changed files with 430 additions and 126 deletions
@@ -1,12 +1,9 @@
import 'package:flutter/material.dart';
import 'package:tma_app/core/constants/app_colors.dart';
import 'package:tma_app/core/constants/app_sizes.dart';
import 'package:tma_app/features/pet/data/repositories/allergen_menu_repository.dart';
import 'package:tma_app/shared/widgets/icons/rup_icon.dart';
import 'package:tma_app/shared/widgets/rup_form_fields.dart';
const int _kCustomMaxLength = 12;
const double _kCustomInputWidth = 76;
/// 알레르기 선택 — 기본 항목 + 직접 추가한 항목(메뉴에서 지울 수 있음) + 직접 추가
class AllergenSelector extends StatefulWidget {
@@ -68,77 +65,10 @@ class _AllergenSelectorState extends State<AllergenSelector> {
onDelete: () => widget.onRemoveCustom(a),
),
if (_adding)
_CustomInputChip(controller: _input, onSubmit: _commit)
RupInputChip(controller: _input, maxLength: _kCustomMaxLength, onSubmit: _commit)
else
RupAddChip(label: '직접 추가', onTap: () => setState(() => _adding = true)),
],
);
}
}
class _CustomInputChip extends StatelessWidget {
const _CustomInputChip({required this.controller, required this.onSubmit});
final TextEditingController controller;
final VoidCallback onSubmit;
@override
Widget build(BuildContext context) {
final style = Theme.of(context).textTheme.bodyMedium!.copyWith(
fontSize: 13,
fontWeight: FontWeight.w600,
height: 1.2,
);
return Container(
padding: const EdgeInsets.fromLTRB(11, 3, 5, 3),
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: BorderRadius.circular(AppRadius.card),
border: Border.all(color: AppColors.primary, width: 1.5),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: _kCustomInputWidth,
child: TextField(
controller: controller,
autofocus: true,
maxLength: _kCustomMaxLength,
style: style,
cursorColor: AppColors.primary,
textInputAction: TextInputAction.done,
onSubmitted: (_) => onSubmit(),
decoration: InputDecoration(
isDense: true,
border: InputBorder.none,
counterText: '',
hintText: '직접 입력',
hintStyle: style.copyWith(color: AppColors.muted),
contentPadding: EdgeInsets.zero,
),
),
),
const SizedBox(width: 5),
Semantics(
button: true,
label: '추가',
child: GestureDetector(
onTap: onSubmit,
child: Container(
width: 26,
height: 26,
decoration: BoxDecoration(
color: AppColors.primary,
borderRadius: BorderRadius.circular(AppSpacing.sm),
),
alignment: Alignment.center,
child: const RupIcon(RupIconName.check, size: 15, stroke: 2.6, color: Colors.white),
),
),
),
],
),
);
}
}