바텀시트 아래 화살표

2026-09-23 (KST). 아이폰 앱 day_plan_appl_app 에 반영된 수정이다. 윈도우에서 연 안드로이드 폰앱 day_plan_app(또는 auto_schedule_app)의 lib/screens/calendar_tab.dart 에 같은 조각을 넣는다. 달력 파일 전체를 덮어쓰지 않는다. 이 문서를 쓰는 시점에는 안드로이드에 코드를 넣지 않았다.

1. 규칙

접힌 시트에서 날짜+건수를 눌러 최대 높이(0.9)까지 올렸는데도 아래에 안 보이는 항목이 더 있으면, 목록 바로 아래에 아래 화살표를 붙인다. 더 있다는 표시다. 화살표를 눌러 스크롤하지는 않는다.

상태화살표
시트 높이가 최대(0.9, 여유 0.02)이고, 목록을 8px보다 더 내릴 수 있다 보인다
최대가 아니다(기본 0.6, 내용에 맞춘 중간 높이, 접힘) 안 보인다
최대인데 항목이 화면에 다 들어간다 안 보인다
최대에서 맨 아래까지 스크롤했다 사라진다
화살표를 목록 위에 겹쳐 그리지 않는다. 글자를 가린다. 목록이 끝나는 다음 줄에 시트 배경색으로 한 줄을 두고, 그 가운데에 화살표를 둔다.

판정:

최대인가 = sheetSize >= 0.9 - 0.02
아래에 남음 = maxScrollExtent - scrollOffset > 8
화살표 = 최대인가 그리고 아래에 남음

2. 먼저 들어가 있어야 하는 것

아이폰 day_plan_appl_app 에는 2026-09-23에 반영됨.

안드로이드에 아래 두 문서가 이미 들어가 있어야 이 화살표를 붙일 수 있다.

화살표는 그 0.9 상태에서만 나온다. 목록 컨트롤러 이름이 _listCtl, 시트 컨트롤러가 _sheetCtl인 1050 구조를 기준으로 한다.

3. 넣을 코드

파일: 안드로이드 lib/screens/calendar_tab.dart. 핫 리로드 대신 앱을 완전히 다시 실행한다.

3.1 톱레벨 함수

calendarSheetSizeOnCountTap 바로 아래에 둔다. 클래스 밖이어야 테스트에서 부를 수 있다.

/// 최대 높이인데 목록 아래에 아직 안 보이는 항목이 있으면 아래 화살표를 보인다.
bool calendarSheetShowsMoreBelow({
  required double sheetSize,
  required double scrollOffset,
  required double maxScrollExtent,
}) {
  final atMax = sheetSize >= kCalendarSheetMax - 0.02;
  if (!atMax) return false;
  return maxScrollExtent - scrollOffset > 8;
}

3.2 파일 맨 끝 위젯

CalendarTab 클래스가 끝난 다음, 파일 끝에 붙인다. 시트와 같은 배경 #F7F9FC라 글자와 한 덩어리로 보이지 않고, 목록 밖에 있다.

/// 최대 높이 시트의 맨 아래. 스크롤하면 더 보이는 항목이 있을 때만 화살표.
class _SheetMoreHint extends StatefulWidget {
  const _SheetMoreHint({
    required this.sheet,
    required this.list,
  });

  final DraggableScrollableController sheet;
  final ScrollController list;

  @override
  State<_SheetMoreHint> createState() => _SheetMoreHintState();
}

class _SheetMoreHintState extends State<_SheetMoreHint> {
  @override
  void initState() {
    super.initState();
    widget.sheet.addListener(_onChange);
    widget.list.addListener(_onChange);
    WidgetsBinding.instance.addPostFrameCallback((_) => _onChange());
  }

  @override
  void didUpdateWidget(covariant _SheetMoreHint oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.sheet != widget.sheet) {
      oldWidget.sheet.removeListener(_onChange);
      widget.sheet.addListener(_onChange);
    }
    if (oldWidget.list != widget.list) {
      oldWidget.list.removeListener(_onChange);
      widget.list.addListener(_onChange);
    }
  }

  @override
  void dispose() {
    widget.sheet.removeListener(_onChange);
    widget.list.removeListener(_onChange);
    super.dispose();
  }

  void _onChange() {
    if (mounted) setState(() {});
  }

  bool get _visible {
    if (!widget.sheet.isAttached || !widget.list.hasClients) return false;
    final pos = widget.list.position;
    if (!pos.hasContentDimensions) return false;
    return calendarSheetShowsMoreBelow(
      sheetSize: widget.sheet.size,
      scrollOffset: pos.pixels,
      maxScrollExtent: pos.maxScrollExtent,
    );
  }

  @override
  Widget build(BuildContext context) {
    if (!_visible) return const SizedBox.shrink();
    return const ColoredBox(
      color: Color(0xFFF7F9FC),
      child: SizedBox(
        width: double.infinity,
        child: Padding(
          padding: EdgeInsets.only(bottom: 4),
          child: Icon(
            Icons.keyboard_arrow_down,
            size: 22,
            color: Color(0xFF3F6FB0),
          ),
        ),
      ),
    );
  }
}

4. 시트 안에 넣는 자리

Stack 위에 Positioned로 올리지 않는다. Column 안에서 목록 Expanded의 다음 자식으로 둔다. 안 보일 때는 높이가 0이라 목록이 그 자리를 쓴다. 보일 때는 목록이 한 줄 줄고, 화살표는 그 아래 빈 줄에만 있다.

목록 아래 여백은 28이 아니라 8로 줄인다. 화살표 줄이 아래 여백을 대신한다.

Expanded(
  child: ListView(
    controller: _listCtl,
    primary: false,
    padding: const EdgeInsets.fromLTRB(16, 4, 16, 8),
    children: [
      // 기존 목록: 공휴일, 구글, 일정, 기록, 할일. 건드리지 않는다.
    ],
  ),
),
_SheetMoreHint(sheet: _sheetCtl, list: _listCtl),
Column의 자식 순서는 핸들, 날짜+건수, Expanded(목록), 화살표다. 닫기(X)와 1px 시트 컨트롤러는 Stack의 Positioned로 그대로 둔다.

5. 테스트

test/calendar_sheet_test.dart에 추가한다. 패키지 이름은 안드로이드 앱의 이름을 쓴다.

test('최대 높이인데 아래가 남으면 화살표, 맨 아래면 숨김', () {
  expect(
    calendarSheetShowsMoreBelow(
      sheetSize: 0.9,
      scrollOffset: 0,
      maxScrollExtent: 200,
    ),
    isTrue,
  );
  expect(
    calendarSheetShowsMoreBelow(
      sheetSize: 0.6,
      scrollOffset: 0,
      maxScrollExtent: 200,
    ),
    isFalse,
  );
  expect(
    calendarSheetShowsMoreBelow(
      sheetSize: 0.9,
      scrollOffset: 200,
      maxScrollExtent: 200,
    ),
    isFalse,
  );
});

실행: flutter test test/calendar_sheet_test.dart

6. 확인

  1. 항목이 많아 최대 높이로도 아래가 남는 날에서, 접힌 시트의 날짜+건수를 누른다. 시트가 0.9까지 올라가고, 목록 맨 아래 글자 밑에 아래 화살표가 있다. 글자 위에 겹치지 않는다.
  2. 목록을 맨 아래까지 내리면 화살표가 사라진다. 다시 올리면 나타난다.
  3. 항목이 최대 높이에 다 들어가면 화살표가 없다.
  4. 날짜칸만 눌러 0.6이나 중간 높이로 열린 상태에서는 화살표가 없다.