핵심 코드/개념 정리
1) Form과 검증
- GlobalKey<FormState>로 폼 전체를 제어하고, TextFormField/FormField의 validator로 필드별 검증을 수행.
- 제출 전 FocusScope.of(context).unfocus()로 키보드 내리고, formKey.currentState?.validate()로 일괄 검사.
final _formKey = GlobalKey<FormState>();
// ...
final valid = _formKey.currentState?.validate() ?? false;
if (!valid) return;
2) 커스텀 FormField로 이미지 필수 처리
- 이미지 선택은 일반 위젯이기 때문에, **별도의 FormField<String?>**로 감싸 validator를 붙임.
- GlobalKey<FormFieldState<String?>>를 사용해 다이얼로그에서 선택 시 didChange로 값 변화를 폼에 반영.
final _imageFieldKey = GlobalKey<FormFieldState<String?>>();
setState(() => _imageSelection = 'network');
_imageFieldKey.currentState?.didChange(_imageSelection);
3) 가격 입력 포맷팅과 숫자 검증
- FilteringTextInputFormatter.digitsOnly로 숫자만 허용.
- TextInputFormatter.withFunction에서 intl의 NumberFormat('#,###')로 1,000 단위 콤마를 실시간 적용.
- validator에서는 숫자 외 문자를 제거 후 범위(100 ~ 10,000,000) 검사.
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
TextInputFormatter.withFunction((oldValue, newValue) {
if (newValue.text.isEmpty) return newValue;
final number = int.parse(newValue.text.replaceAll(',', ''));
final formatted = NumberFormat('#,###').format(number);
return TextEditingValue(
text: formatted,
selection: TextSelection.collapsed(offset: formatted.length),
);
}),
],
4) Uuid로 엔터티 ID 생성 + 결과 전달
- uuid.v4()로 고유 ID 생성, Navigator.pop(context, product)로 상위에 결과 반환.
- 완료 전 미리 결과 요약을 AlertDialog로 보여줘 피드백 제공.
final product = ProductEntity(
id: const Uuid().v4(),
name: _nameController.text.trim(),
price: int.parse(_priceController.text.replaceAll(',', '')),
description: _descController.text.trim(),
imageUrl: imageUrl,
);
Navigator.pop(context, product);
5) 이미지 네트워크 로딩 UX
- Image.network의 loadingBuilder로 로딩 인디케이터, errorBuilder로 에러 메세지 제공.
- ClipRRect + Card로 라운드 처리 및 테두리 스타일 일관화.
예외처리/검증 포인트 정리
- 이미지: 선택 안 하면 “이미지를 등록해 주세요.”
- 상품명: 공백/빈 문자열 금지, 최대 30자 제한.
- 가격: 비어있음/음수/숫자 아님/범위 외(100 미만, 10,000,000 초과) 모두 차단.
- 설명: 공백/빈 문자열 금지, 최대 1000자 제한.
상태/네비게이션/리소스 관리
- TextEditingController는 dispose()에서 모두 해제 → 메모리 누수 방지.
- 다이얼로그 사용 시 Navigator.pop(ctx) vs Navigator.pop(context, product)로 레이어 구분 주의.
- SingleChildScrollView + SafeArea + padding으로 작은 화면/키보드 환경에서도 레이아웃 깨지지 않게 처리.
UI 구성 체크리스트
- FilledButton.icon으로 이미지 선택 CTA 명확화.
- OutlineInputBorder와 maxLength로 입력 경계/가이드 제공.
- suffixText: '원', keyboardType: TextInputType.number로 입력 의도 전달.
- TextInputAction.next로 필드 간 포커스 이동 자연스럽게.
최종 정리 끝! 내일 발표 준비 잘하자!