Refactor/appbar home widgets - #4
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthrough새로운 DetailHeader 위젯을 추가하고, 홈 화면의 날짜 관련 내부 구현을 Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25분 Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
lib/presentation/home/utils/home_date_utils.dart (1)
1-39: 정적 유틸 클래스는 인스턴스화를 막아 두는 편이 좋습니다.지금 구조상 상태가 전혀 없는데 생성은 가능해서 의도가 흐려집니다. private constructor 하나만 추가해 두면 오용을 막을 수 있습니다.
가능한 수정 방향
class HomeDateUtils { + const HomeDateUtils._(); + static DateTime dateOnly(DateTime date) => DateTime(date.year, date.month, date.day);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/presentation/home/utils/home_date_utils.dart` around lines 1 - 39, HomeDateUtils is a purely static utility class but can be instantiated; add a private constructor to prevent instantiation by declaring a private named constructor (e.g., HomeDateUtils._();) inside the HomeDateUtils class so the class cannot be constructed and only its static members (dateOnly, startOfWeek, weekDates, isSameDate, weekdayLabel) are usable.lib/core/presentation/components/detail_header.dart (1)
31-44: 기본 뒤로가기는canPop가드가 있는 편이 안전합니다.공용 헤더인데 기본 분기에서 바로
context.pop()을 호출하고 있습니다. 루트 화면이나 pop 불가 컨텍스트에 붙으면GoError: There is nothing to pop예외가 발생하므로, 최소한 pop 가능 여부를 확인한 뒤 호출하는 쪽이 안전합니다.수정 방향
leading: showBackButton ? IconButton( padding: EdgeInsets.zero, constraints: const BoxConstraints(minWidth: 44, minHeight: 44), visualDensity: VisualDensity.compact, - onPressed: onBack ?? () => context.pop(), + onPressed: onBack ?? + () { + if (context.canPop()) { + context.pop(); + } + }, highlightColor: Colors.transparent, icon: const Icon( Icons.navigate_before,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/core/presentation/components/detail_header.dart` around lines 31 - 44, The onPressed handler for the back IconButton currently calls context.pop() directly (when onBack is null) which can throw if there is nothing to pop; change the default handler in the IconButton (where showBackButton, onBack and context.pop are used) to check pop capability first—e.g., replace the direct context.pop() call with a guard that uses context.canPop() or Navigator.canPop(context) (or GoRouter.of(context).canPop()) and only calls context.pop() when true; keep onBack override behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/presentation/home/home_screen.dart`:
- Around line 19-23: The build recalculates today causing page/date mapping to
drift; freeze the initial basis date by computing today once with useMemoized
(no keys) and use that cached value for selectedDate, pageController
(_initialPage), and weekDates to ensure jumpToPage and onPageChanged use the
same fixed reference; update usages of today, selectedDate, pageController, and
weekDates to rely on the memoized initial date so page indices consistently map
to the correct dates across rebuilds.
In `@lib/presentation/home/widgets/empty_ootd_view.dart`:
- Around line 6-57: EmptyOotdView currently renders a tappable-looking "이미지 등록"
CTA but has no gesture handler; modify EmptyOotdView by adding an optional
VoidCallback (e.g., onTap) to its constructor, wrap the CTA Row (or the
Text/Icon pair) with an InkWell or GestureDetector inside build, and invoke
onTap when tapped; then update the caller (home_screen's const EmptyOotdView())
to pass the upload/start flow callback so tapping the CTA triggers the upload
flow.
---
Nitpick comments:
In `@lib/core/presentation/components/detail_header.dart`:
- Around line 31-44: The onPressed handler for the back IconButton currently
calls context.pop() directly (when onBack is null) which can throw if there is
nothing to pop; change the default handler in the IconButton (where
showBackButton, onBack and context.pop are used) to check pop capability
first—e.g., replace the direct context.pop() call with a guard that uses
context.canPop() or Navigator.canPop(context) (or GoRouter.of(context).canPop())
and only calls context.pop() when true; keep onBack override behavior unchanged.
In `@lib/presentation/home/utils/home_date_utils.dart`:
- Around line 1-39: HomeDateUtils is a purely static utility class but can be
instantiated; add a private constructor to prevent instantiation by declaring a
private named constructor (e.g., HomeDateUtils._();) inside the HomeDateUtils
class so the class cannot be constructed and only its static members (dateOnly,
startOfWeek, weekDates, isSameDate, weekdayLabel) are usable.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3add4099-bd6e-49bd-a2e8-bee515ac18f4
📒 Files selected for processing (6)
lib/core/presentation/components/detail_header.dartlib/presentation/home/home_screen.dartlib/presentation/home/utils/home_date_utils.dartlib/presentation/home/widgets/date_item.dartlib/presentation/home/widgets/empty_ootd_view.dartlib/presentation/setting/setting_screen.dart
| class EmptyOotdView extends StatelessWidget { | ||
| const EmptyOotdView({super.key}); | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| return DottedBorder( | ||
| options: RoundedRectDottedBorderOptions( | ||
| color: ColorStyles.grayB8, | ||
| strokeWidth: 2, | ||
| dashPattern: const [6, 4], | ||
| radius: const Radius.circular(8), | ||
| ), | ||
| child: Container( | ||
| decoration: BoxDecoration( | ||
| color: ColorStyles.white, | ||
| borderRadius: BorderRadius.circular(8), | ||
| ), | ||
| child: Column( | ||
| mainAxisSize: MainAxisSize.max, | ||
| mainAxisAlignment: MainAxisAlignment.center, | ||
| crossAxisAlignment: CrossAxisAlignment.center, | ||
| spacing: 16, | ||
| children: [ | ||
| Text( | ||
| '오늘의 OOTD를 등록하고\n룩티커를 만나보세요!', | ||
| textAlign: TextAlign.center, | ||
| style: TextStyles.normalTextMedium.copyWith( | ||
| color: ColorStyles.gray6B, | ||
| height: 1.4, | ||
| ), | ||
| ), | ||
| Row( | ||
| mainAxisAlignment: MainAxisAlignment.center, | ||
| children: [ | ||
| Text( | ||
| '이미지 등록', | ||
| style: TextStyles.smallTextBold.copyWith( | ||
| color: ColorStyles.pointC9, | ||
| ), | ||
| ), | ||
| const Icon( | ||
| Icons.chevron_right, | ||
| size: 16, | ||
| color: ColorStyles.pointC9, | ||
| ), | ||
| ], | ||
| ), | ||
| ], | ||
| ), | ||
| ), | ||
| ); | ||
| } |
There was a problem hiding this comment.
이미지 등록 CTA에 실제 탭 동작을 연결해 주세요.
지금 UI는 액션처럼 보이지만 콜백/제스처가 없어서 lib/presentation/home/home_screen.dart Line 100의 const EmptyOotdView()에서는 업로드 플로우로 진입할 수 없습니다. 빈 상태의 핵심 액션이 막혀 있습니다.
가능한 수정 방향
class EmptyOotdView extends StatelessWidget {
- const EmptyOotdView({super.key});
+ final VoidCallback onTap;
+
+ const EmptyOotdView({
+ super.key,
+ required this.onTap,
+ });
`@override`
Widget build(BuildContext context) {
- return DottedBorder(
- options: RoundedRectDottedBorderOptions(
- color: ColorStyles.grayB8,
- strokeWidth: 2,
- dashPattern: const [6, 4],
- radius: const Radius.circular(8),
- ),
- child: Container(
- decoration: BoxDecoration(
- color: ColorStyles.white,
- borderRadius: BorderRadius.circular(8),
- ),
- child: Column(
- mainAxisSize: MainAxisSize.max,
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.center,
- spacing: 16,
- children: [
- ...
- ],
- ),
- ),
+ return Material(
+ color: Colors.transparent,
+ child: InkWell(
+ onTap: onTap,
+ borderRadius: BorderRadius.circular(8),
+ child: DottedBorder(
+ options: RoundedRectDottedBorderOptions(
+ color: ColorStyles.grayB8,
+ strokeWidth: 2,
+ dashPattern: const [6, 4],
+ radius: const Radius.circular(8),
+ ),
+ child: Container(
+ decoration: BoxDecoration(
+ color: ColorStyles.white,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Column(
+ mainAxisSize: MainAxisSize.max,
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ spacing: 16,
+ children: [
+ ...
+ ],
+ ),
+ ),
+ ),
+ ),
);
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/presentation/home/widgets/empty_ootd_view.dart` around lines 6 - 57,
EmptyOotdView currently renders a tappable-looking "이미지 등록" CTA but has no
gesture handler; modify EmptyOotdView by adding an optional VoidCallback (e.g.,
onTap) to its constructor, wrap the CTA Row (or the Text/Icon pair) with an
InkWell or GestureDetector inside build, and invoke onTap when tapped; then
update the caller (home_screen's const EmptyOotdView()) to pass the upload/start
flow callback so tapping the CTA triggers the upload flow.
반영 브랜치
변경 요약
홈 화면의 날짜 관련 로직과 위젯을 별도 모듈로 분리하고, 공통 AppBar인 DetailHeader를 추가해 UI 컴포넌트 재사용성과 코드 구조를 개선했습니다.
주요 변경점
DetailHeader 컴포넌트 신규 추가(PreferredSizeWidget, title/트레일링/백버튼/배경색 지원)
홈 화면에서 DateItem, EmptyOotdView 위젯을 별도 파일로 분리 및 사용
HomeDateUtils 유틸 추가: dateOnly, startOfWeek, weekDates, isSameDate, weekdayLabel 제공
home_screen.dart 내부의 사설 날짜 헬퍼/위젯 제거로 코드 간결화(+151줄 제거)
SettingScreen의 AppBar를 DetailHeader로 교체
주의/리스크
DetailHeader의 높이(preferredSize)가 고정(48)되어 있어 화면별 디자인 요구에 따라 조정 필요
HomeDateUtils.weekdayLabel의 미매핑 입력은 빈 문자열을 반환해 UI 표시 이상 가능성
새로운 위젯/유틸로 인한 import 경로 변경으로 다른 파일에서의 사용 누락 여부 점검 필요
다음 액션
프로젝트 전반에 DetailHeader 적용 대상 확인 및 일관성 적용
홈 화면 날짜 동작 관련 단위/UI 테스트 추가 및 확인
변경된 import/사용처(특히 홈 관련 위젯들)에서 빌드/런 타임 에러 없는지 검증