Files
superport/lib/screens/zipcode/zipcode_search_screen.dart
JiWoong Sul 2bbef9defa
Some checks failed
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled
Flutter Test & Quality Check / Build APK (push) Has been cancelled
fix(zipcode): cap table width in dialog to half screen via optional maxWidthCap; pass 0.5 fraction when used in Company/Warehouse dialogs
- ZipcodeTable: add maxWidthCap and use min(viewport, cap) to compute width
- ZipcodeSearchScreen: allow tableMaxWidthFraction and forward to table
- Apply 0.5 in CompanyForm and WarehouseLocationForm dialogs

flutter analyze: 0 errors, warnings unaffected
2025-09-09 22:47:29 +09:00

258 lines
11 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:superport/data/models/zipcode_dto.dart';
import 'package:superport/screens/zipcode/controllers/zipcode_controller.dart';
import 'package:superport/screens/zipcode/components/zipcode_search_filter.dart';
import 'package:superport/screens/zipcode/components/zipcode_table.dart';
class ZipcodeSearchScreen extends StatefulWidget {
final Function(ZipcodeDto)? onSelect;
// When used in a dialog/popup, you can cap the table width
// by setting this fraction (0.01.0) of the screen width.
final double? tableMaxWidthFraction;
const ZipcodeSearchScreen({super.key, this.onSelect, this.tableMaxWidthFraction});
@override
State<ZipcodeSearchScreen> createState() => _ZipcodeSearchScreenState();
}
class _ZipcodeSearchScreenState extends State<ZipcodeSearchScreen> {
late ZipcodeController _controller;
@override
void initState() {
super.initState();
_controller = context.read<ZipcodeController>();
// 위젯이 완전히 빌드된 후에 초기화 실행
WidgetsBinding.instance.addPostFrameCallback((_) {
_controller.initialize();
});
}
void _showSuccessToast(String message) {
if (mounted) {
ShadToaster.of(context).show(
ShadToast(
title: const Text('성공'),
description: Text(message),
duration: const Duration(seconds: 3),
),
);
}
}
void _showErrorToast(String message) {
if (mounted) {
ShadToaster.of(context).show(
ShadToast.destructive(
title: Text('오류'),
description: Text(message),
duration: const Duration(seconds: 5),
),
);
}
}
@override
Widget build(BuildContext context) {
final theme = ShadTheme.of(context);
return Consumer<ZipcodeController>(
builder: (context, controller, child) {
// 에러 메시지 표시
if (controller.errorMessage != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_showErrorToast(controller.errorMessage!);
});
}
return Material(
color: theme.colorScheme.background,
child: Column(
children: [
// 헤더 섹션
Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: theme.colorScheme.card,
border: Border(
bottom: BorderSide(
color: theme.colorScheme.border,
width: 1,
),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 제목 및 설명
Row(
children: [
Icon(
Icons.location_on,
size: 28,
color: theme.colorScheme.primary,
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'우편번호 검색',
style: theme.textTheme.h3,
),
const SizedBox(height: 4),
Text(
'전국의 우편번호를 검색하고 정확한 주소를 찾을 수 있습니다',
style: theme.textTheme.muted,
),
],
),
const Spacer(),
// 통계 정보
Container(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
decoration: BoxDecoration(
color: theme.colorScheme.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Text(
'${controller.totalCount}개 우편번호',
style: TextStyle(
color: theme.colorScheme.primary,
fontWeight: FontWeight.w600,
),
),
),
],
),
const SizedBox(height: 24),
// 검색 및 필터 영역
ZipcodeSearchFilter(
onSearch: controller.setSearchQuery,
onSidoChanged: controller.setSido,
onGuChanged: controller.setGu,
sidoList: controller.sidoList,
guList: controller.guList,
selectedSido: controller.selectedSido,
selectedGu: controller.selectedGu,
),
],
),
),
// 메인 컨텐츠 영역
Expanded(
child: Container(
padding: const EdgeInsets.all(24),
child: controller.isLoading && controller.zipcodes.isEmpty
? const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('우편번호를 검색하고 있습니다...'),
],
),
)
: controller.zipcodes.isEmpty
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.search_off,
size: 64,
color: theme.colorScheme.mutedForeground,
),
const SizedBox(height: 16),
Text(
'검색 결과가 없습니다',
style: theme.textTheme.h4,
),
const SizedBox(height: 8),
Text(
'다른 검색어나 필터를 사용해 보세요',
style: theme.textTheme.muted,
),
const SizedBox(height: 24),
ShadButton.outline(
onPressed: controller.clearFilters,
child: const Text('필터 초기화'),
),
],
),
)
: Column(
children: [
// 결과 정보
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: theme.colorScheme.muted.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Icon(
Icons.info_outline,
size: 16,
color: theme.colorScheme.mutedForeground,
),
const SizedBox(width: 8),
Text(
'${controller.totalCount}개 중 ${controller.zipcodes.length}개 표시',
style: theme.textTheme.small,
),
const Spacer(),
if (controller.isLoading)
const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
),
],
),
),
const SizedBox(height: 16),
// 테이블
Expanded(
child: ZipcodeTable(
zipcodes: controller.zipcodes,
currentPage: controller.currentPage,
totalPages: controller.totalPages,
onPageChanged: controller.goToPage,
onSelect: (zipcode) {
controller.selectZipcode(zipcode);
if (widget.onSelect != null) {
// 다이얼로그로 사용될 때
widget.onSelect!(zipcode);
} else {
// 일반 화면으로 사용될 때
_showSuccessToast('우편번호 ${zipcode.zipcode}를 선택했습니다');
}
},
maxWidthCap: widget.tableMaxWidthFraction != null
? MediaQuery.sizeOf(context).width * widget.tableMaxWidthFraction!
: null,
),
),
],
),
),
),
],
),
);
},
);
}
}