feat(zipcode): compact column widths for popup; ensure no horizontal scroll within dialog
- ZipcodeTable: dynamic widths; shrink columns when target width < standard - Use computed widths for header and rows; actions width fixed - Works with tableMaxWidthFraction from ZipcodeSearchScreen],
This commit is contained in:
@@ -35,15 +35,51 @@ class ZipcodeTable extends StatelessWidget {
|
|||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
// 고정폭 + 마지막 filler
|
// 고정폭 + 마지막 filler
|
||||||
const double minW = 160 + 180 + 180 + 320 + 140 + 24;
|
// Standard widths
|
||||||
|
const double zipStd = 160.0;
|
||||||
|
const double sidoStd = 180.0;
|
||||||
|
const double guStd = 180.0;
|
||||||
|
const double etcStd = 320.0;
|
||||||
|
const double actionsStd = 140.0;
|
||||||
|
const double fudge = 24.0; // internal paddings/margins
|
||||||
|
|
||||||
final double viewportW = constraints.maxWidth.isFinite
|
final double viewportW = constraints.maxWidth.isFinite
|
||||||
? constraints.maxWidth
|
? constraints.maxWidth
|
||||||
: MediaQuery.sizeOf(context).width;
|
: MediaQuery.sizeOf(context).width;
|
||||||
final double cappedW = maxWidthCap != null
|
final double targetW = maxWidthCap != null
|
||||||
? math.min(viewportW, maxWidthCap!)
|
? math.min(viewportW, maxWidthCap!)
|
||||||
: viewportW;
|
: viewportW;
|
||||||
final double tableW = cappedW >= minW ? cappedW : minW;
|
|
||||||
const double etcW = 320.0;
|
final double stdMin = zipStd + sidoStd + guStd + etcStd + actionsStd + fudge;
|
||||||
|
final bool compact = targetW < stdMin;
|
||||||
|
|
||||||
|
// Effective widths
|
||||||
|
double zipW;
|
||||||
|
double sidoW;
|
||||||
|
double guW;
|
||||||
|
double etcW;
|
||||||
|
double actionsW;
|
||||||
|
bool includeFiller;
|
||||||
|
|
||||||
|
if (!compact) {
|
||||||
|
zipW = zipStd;
|
||||||
|
sidoW = sidoStd;
|
||||||
|
guW = guStd;
|
||||||
|
etcW = etcStd;
|
||||||
|
actionsW = actionsStd;
|
||||||
|
includeFiller = true; // use remaining width as filler column
|
||||||
|
} else {
|
||||||
|
// Compact widths to fit inside targetW without horizontal scroll
|
||||||
|
zipW = 110.0;
|
||||||
|
sidoW = 120.0;
|
||||||
|
guW = 120.0;
|
||||||
|
actionsW = 110.0;
|
||||||
|
final double remaining = targetW - fudge - (zipW + sidoW + guW + actionsW);
|
||||||
|
etcW = math.max(180.0, remaining);
|
||||||
|
includeFiller = false; // no filler in compact mode
|
||||||
|
}
|
||||||
|
|
||||||
|
final double tableW = targetW;
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
@@ -53,17 +89,19 @@ class ZipcodeTable extends StatelessWidget {
|
|||||||
columnSpanExtent: (index) {
|
columnSpanExtent: (index) {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 0:
|
case 0:
|
||||||
return const FixedTableSpanExtent(160); // 우편번호
|
return FixedTableSpanExtent(zipW); // 우편번호
|
||||||
case 1:
|
case 1:
|
||||||
return const FixedTableSpanExtent(180); // 시도
|
return FixedTableSpanExtent(sidoW); // 시도
|
||||||
case 2:
|
case 2:
|
||||||
return const FixedTableSpanExtent(180); // 구/군
|
return FixedTableSpanExtent(guW); // 구/군
|
||||||
case 3:
|
case 3:
|
||||||
return const FixedTableSpanExtent(etcW); // 상세주소
|
return FixedTableSpanExtent(etcW); // 상세주소
|
||||||
case 4:
|
case 4:
|
||||||
return const FixedTableSpanExtent(140); // 작업
|
return FixedTableSpanExtent(actionsW); // 작업
|
||||||
case 5:
|
case 5:
|
||||||
return const RemainingTableSpanExtent(); // filler
|
return includeFiller
|
||||||
|
? const RemainingTableSpanExtent()
|
||||||
|
: const FixedTableSpanExtent(0);
|
||||||
default:
|
default:
|
||||||
return const FixedTableSpanExtent(100);
|
return const FixedTableSpanExtent(100);
|
||||||
}
|
}
|
||||||
@@ -73,15 +111,15 @@ class ZipcodeTable extends StatelessWidget {
|
|||||||
const ShadTableCell.header(child: Text('시도')),
|
const ShadTableCell.header(child: Text('시도')),
|
||||||
const ShadTableCell.header(child: Text('구/군')),
|
const ShadTableCell.header(child: Text('구/군')),
|
||||||
ShadTableCell.header(child: SizedBox(width: etcW, child: const Text('상세주소'))),
|
ShadTableCell.header(child: SizedBox(width: etcW, child: const Text('상세주소'))),
|
||||||
const ShadTableCell.header(child: Text('작업')),
|
ShadTableCell.header(child: SizedBox(width: actionsW, child: const Text('작업'))),
|
||||||
const ShadTableCell.header(child: SizedBox.shrink()),
|
if (includeFiller) const ShadTableCell.header(child: SizedBox.shrink()),
|
||||||
],
|
],
|
||||||
children: zipcodes.map((zipcode) {
|
children: zipcodes.map((zipcode) {
|
||||||
return [
|
return [
|
||||||
// 우편번호 (오버플로우 방지)
|
// 우편번호 (오버플로우 방지)
|
||||||
ShadTableCell(
|
ShadTableCell(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 160,
|
width: zipW,
|
||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
@@ -138,18 +176,21 @@ class ZipcodeTable extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
// 작업
|
// 작업
|
||||||
ShadTableCell(
|
ShadTableCell(
|
||||||
child: ShadButton(
|
child: SizedBox(
|
||||||
onPressed: () => onSelect(zipcode),
|
width: actionsW,
|
||||||
size: ShadButtonSize.sm,
|
child: ShadButton(
|
||||||
child: const Text('선택', style: TextStyle(fontSize: 11)),
|
onPressed: () => onSelect(zipcode),
|
||||||
|
size: ShadButtonSize.sm,
|
||||||
|
child: const Text('선택', style: TextStyle(fontSize: 11)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const ShadTableCell(child: SizedBox.shrink()),
|
if (includeFiller) const ShadTableCell(child: SizedBox.shrink()),
|
||||||
];
|
];
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user