맥북에서 iOS 앱(day_plan_appl_app)에 적용 — Flutter 앱 반영 코드 기준
편집 시트의 생성일(기준일) 피커를 여는 지점에서, 반복이면 안내 후 중단. 비반복만 날짜 이동.
위치: lib/widgets/todo_edit_sheet.dart → _pickCreated() 진입부
Future<void> _pickCreated() async {
// 반복 항목은 날짜(기준일) 변경 불가 — 안내 후 중단. 비반복만 이동 허용.
if (t.isRepeating) {
await showDialog<void>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('반복 항목'),
content: const Text(
'반복 항목은 날짜 변경이 안됩니다.\n대신 삭제 후에 할일을 재생성하세요.'),
actions: [
TextButton(onPressed: () => Navigator.pop(ctx), child: const Text('확인')),
],
),
);
return;
}
// ... (이하 기존 생성일 피커 로직: 비반복만 실행)
}
Swift: 생성일 변경 핸들러 맨 앞에서 todo.isRepeating이면 UIAlertController(확인)로 안내 후 return.
| 반복 | 기본 기한(생성일 기준) |
|---|---|
| 매일 day | +7일 |
| 매주 week | +21일(3주) |
| 매월 month | +3개월 |
| 매년 year | +3년 |
무기한 선택 가능(기존 기한 피커의 '무기한' 그대로 = 기한 nil). 단위 바꾸면 재계산하되 수동 지정 기한은 보존.
위치: lib/widgets/todo_edit_sheet.dart → _defaultRepeatUntil(), 반복 단위 DropdownButton.onChanged
/// 반복 기한 기본값: 생성일 기준, 단위별(매일+7·매주+21·매월+3개월·매년+3년).
String _defaultRepeatUntil([String? unit]) {
final u = unit ?? t.repeatUnit;
final base = DateTime.tryParse(_dateOnly(t.createdAt)) ?? DateTime.now();
switch (u) {
case 'day':
return isoOf(base.add(const Duration(days: 7)));
case 'week':
return isoOf(base.add(const Duration(days: 21)));
case 'year':
return isoOf(DateTime(base.year + 3, base.month, base.day));
default: // 매월
return isoOf(DateTime(base.year, base.month + 3, base.day));
}
}
onChanged: t.isRecurring
? null
: (v) => setState(() {
final wasRepeat = t.isRepeating;
final oldUnit = t.repeatUnit;
t.repeatUnit = (v == null || v.isEmpty) ? null : v;
if (t.repeatUnit == null) {
t.repeatUntil = null; // 안함 → 기한 제거
} else {
final cur = t.repeatUntil;
// 비어 있거나 '이전 단위 자동 기본값'이면 새 단위 기본값으로 재계산(수동 지정 보존).
if (cur == null || cur.isEmpty || cur == _defaultRepeatUntil(oldUnit)) {
t.repeatUntil = _defaultRepeatUntil(t.repeatUnit);
}
}
if (!wasRepeat && t.isRepeating) {
t.calVisible = false;
} else if (wasRepeat && !t.isRepeating) {
t.calVisible = true;
}
}),
저장 시에도 반복인데 기한 비면 t.repeatUntil = _defaultRepeatUntil();(현재 단위). Swift: Calendar.date(byAdding:)로 day=+7/week=+21/month=+3M/year=+3Y.
cal_visible=false 반복은 모든 회차가 아니라 현재 회차 한 곳만 달력에 표시.
현재 회차 = 오늘 이하 최근 회차(시작 전이면 기준일). 과거가 되어도 계속 표시, 다음 회차가 되면 그 칸으로 이동(누적 아님).
| 오늘(매달 7일, OFF) | 표시 위치 |
|---|---|
| 11/8 ~ 12/6 | 11/7 (계속) |
| 12/7 | 12/7 (11/7 사라짐) |
위치: lib/screens/calendar_tab.dart → 월 셀 전개(반복 처리부)
// 표시(cal_visible=true) 반복: 모든 회차를 셀에 전개.
occurrencesIn(repeating.where((t) => t.calVisible).toList(),
gridStart, gridEnd, today: now, expandAll: true)
.forEach((iso, list) {
for (final t in list) { addRep(t, iso); }
});
// 숨김(cal_visible=false) 반복: 셀엔 '현재 회차(생성/최근 발생) 한 곳'만.
// 시작일이 미래면 '만든 날짜(기준일)' 칸에. 과거가 되어도 계속 표시(다음 회차 전까지).
for (final t in repeating.where((t) => !t.calVisible)) {
final base = DateTime.tryParse(t.repeatAnchor ?? '');
if (base == null) continue;
final until = t.repeatUntil == null ? null : DateTime.tryParse(t.repeatUntil!);
final cur = latestOccurrenceOnOrBefore(
base, t.repeatUnit, t.repeatInterval, now, until: until) ??
dOnly(base);
final at = t.isDelayed ? DateTime.tryParse(t.delayDate!) ?? cur : cur;
if (at.isBefore(gridStart) || at.isAfter(gridEnd)) continue;
addRep(t, isoOf(at)); // 과거여도 숨기지 않음(현재 회차에 계속 표시)
}