ResponsiveContainer | CoUI
LogoCoUI

ResponsiveContainer

최대 너비 제약과 수평 중앙 정렬을 제공하는 반응형 컨테이너

ResponsiveContainer#

ResponsiveContainer는 자식 콘텐츠에 최대 너비 제약 + 수평 중앙 정렬 + 기본 수평 패딩을 적용하는 레이아웃 래퍼입니다. Tailwind의 container 유틸리티, Flutter의 ConstrainedBox + Center 패턴을 한 번에 제공합니다. 긴 텍스트 영역, 페이지 본문, 카드 그리드 등의 바깥 래퍼로 사용합니다.

Live Preview#

사용 시기#

  • 본문 영역이 넓은 모니터에서 가독성 있게 너비가 제한되어야 할 때
  • 폼/카드 리스트/블로그 포스트의 바깥 경계
  • 좌우 여백을 일관되게 유지하고 싶을 때 (CoreSpace.space16 기본)

기본 사용법#

ResponsiveContainer(
  maxWidth: CoreResponsiveContainerMaxWidth.sm,
  child: Card(
    cardStyle: const CoreCardStyle(
      padding: CoreEdgeInsets.all(CoreSpace.space16),
    ),
    child: Text('Centred, max-width content.'),
  ),
)
ResponsiveContainer(
  maxWidth: CoreResponsiveContainerMaxWidth.sm,
  child: Card(
    child: div([Text('Centred, max-width content.').bodyMedium.onSurface]),
  ),
)

responsiveMaxWidth — tier별 max-width override#

정적인 maxWidth 하나만 쓰는 대신, Breakpoint의 3단계 tier(mobile/tablet/desktop)별로 서로 다른 max-width 프리셋을 지정할 수 있습니다. 예를 들어 mobile에서는 제약 없이(full) 화면을 꽉 채우고, tablet부터 점점 좁혀가는 레이아웃을 만들 때 유용합니다.

ResponsiveContainer(
  // 정적 maxWidth는 responsiveMaxWidth가 지정하지 않은 tier의 폴백으로 쓰입니다.
  maxWidth: CoreResponsiveContainerMaxWidth.lg,
  responsiveMaxWidth: const {
    CoreBreakpointTier.mobile: CoreResponsiveContainerMaxWidth.full,
    CoreBreakpointTier.tablet: CoreResponsiveContainerMaxWidth.md,
    CoreBreakpointTier.desktop: CoreResponsiveContainerMaxWidth.xl,
  },
  child: Card(
    cardStyle: const CoreCardStyle(
      padding: CoreEdgeInsets.all(CoreSpace.space16),
    ),
    child: Text('Max-width switches per breakpoint tier.'),
  ),
)
ResponsiveContainer(
  // 정적 maxWidth는 responsiveMaxWidth가 지정하지 않은 tier의 폴백으로 쓰입니다.
  maxWidth: CoreResponsiveContainerMaxWidth.lg,
  responsiveMaxWidth: const {
    CoreBreakpointTier.mobile: CoreResponsiveContainerMaxWidth.full,
    CoreBreakpointTier.tablet: CoreResponsiveContainerMaxWidth.md,
    CoreBreakpointTier.desktop: CoreResponsiveContainerMaxWidth.xl,
  },
  child: Card(
    child: div([
      Text('Max-width switches per breakpoint tier.').bodyMedium.onSurface,
    ]),
  ),
)

우선순위는 responsiveMaxWidth[현재 tier] > maxWidth > 디자인 시스템 기본값(xl)입니다 — 맵에 현재 tier가 없으면 정적 maxWidth로 개별 폴백하고(부분(partial) 맵 허용, Breakpointboundaries가 tier별로 개별 폴백하는 것과 동일한 패턴), responsiveMaxWidth 자체가 null(기본값)이면 breakpoint 관측 자체가 생략되어 기존 정적 maxWidth 동작이 그대로 유지됩니다 — non-breaking 확장입니다.

Props#

속성타입기본값설명
child Widget / Component 래핑할 자식 (required)
maxWidth CoreResponsiveContainerMaxWidth? xl 최대 너비 프리셋 ( sm =640 / md =768 / lg =1024 / xl =1280 / xxl =1536 / full =제약 없음)
responsiveMaxWidth Map<CoreBreakpointTier, CoreResponsiveContainerMaxWidth>? null (정적 maxWidth 유지) tier별 max-width override. 누락된 tier는 개별적으로 maxWidth로 폴백
centered bool? true 가로 중앙 정렬 여부. false이면 부모 왼쪽에 붙음
padding CoreEdgeInsets? CoreSpace.space16 (수평) 수평 패딩. Flutter / Web 동일하게 CoreEdgeInsets
responsiveContainerStyle CoreResponsiveContainerStyle? null 위 필드를 프로젝트·테마 단위로 override하는 슬롯

CoreResponsiveContainerStyle 필드#

필드타입설명
maxWidth CoreResponsiveContainerMaxWidth? Max-width preset.
centered bool? Whether the container is horizontally centred inside its parent.
horizontalPadding CoreEdgeInsets? Horizontal padding.

동작#

  • viewport ≥ maxWidth: 컨테이너는 maxWidth 제약으로 중앙에 고정, 좌우 여백은 균등
  • viewport < maxWidth: 컨테이너는 부모 전체 너비를 차지, 수평 패딩이 그대로 적용
  • maxWidth: .full — 제약 없음, 그냥 부모 너비 그대로 + 수평 패딩
  • responsiveMaxWidth가 지정되면 위 판정 전에 현재 CoreBreakpointTier(Breakpoint와 동일한 resolveBreakpoint 관측)로 먼저 유효 maxWidth 프리셋을 선택합니다

플랫폼 차이#

  • Flutter: ConstrainedBox(maxWidth) + SizedBox(width: infinity) + Padding + (옵션) Center. maxWidththeme.scaling을 곱해 논리 픽셀로 확대됨
  • Web: div + w-full + mx-auto + max-w-screen-{size} Tailwind 클래스. 수평 패딩은 var(--coui-space-{n}) CSS 변수로 주입되어 브라우저 text-scaling에 자동 반응

두 플랫폼 모두 동일한 CoreBreakpoint 값(640/768/1024/1280/1536)에 결합되어 픽셀 폭이 동일합니다.

사용 가이드라인 (Usage Guidelines)#

✅ Do#

콘텐츠 성격에 맞는 maxWidth 프리셋 선택

// 텍스트 위주 폼/본문 — 가독성을 위해 좁게
ResponsiveContainer(
  maxWidth: CoreResponsiveContainerMaxWidth.sm,
  child: form,
)

// 카드 그리드/대시보드 — 기본값(xl)보다 넓게
ResponsiveContainer(
  maxWidth: CoreResponsiveContainerMaxWidth.xxl,
  child: cardGrid,
)

기본값 xl(1280px)은 일반적인 페이지 폭에 맞춰져 있지만, 긴 텍스트 줄 길이를 줄여야 하는 폼/본문에는 sm/md가, 카드 그리드처럼 가로 공간이 많이 필요한 콘텐츠에는 xl보다 넓은 프리셋이 더 잘 맞습니다.


❌ Don't#

centered: false로 두고 중앙 정렬된 페이지 본문을 기대하지 않기

// ❌ centered: false면 부모 왼쪽에 붙어 좌측 정렬된 컬럼처럼 보임
ResponsiveContainer(
  maxWidth: CoreResponsiveContainerMaxWidth.md,
  centered: false,
  child: pageBody,
)

centered(기본값 true)를 끄면 컨테이너가 maxWidth로 제약되되 부모의 왼쪽 가장자리에 붙습니다 — 넓은 화면에서 중앙 정렬된 본문을 원한다면 centered를 명시적으로 끄지 마세요.

접근성 (Accessibility)#

역할 / Semantics#

양 플랫폼 모두 아무 역할도 내보내지 않습니다. Flutter는 ConstrainedBox > SizedBox > Padding(옵션으로 Align)만 반환하고 Semantics 호출이 없으며, Web은 <main>/<section> 같은 시맨틱 요소가 아니라 평범한 <div>를 렌더하고 호출자가 넘긴 attributes만 통과시킵니다.

읽는 입장에서 이것은 max-width · 중앙 정렬 · 패딩만 담당하는 투명한 상자라는 뜻입니다. 페이지 셸로 쓰더라도 랜드마크 목록에는 나타나지 않으므로, 이 영역을 랜드마크로 만들 책임은 호출자에게 있습니다.

키보드#

처리하는 키가 없습니다.

포커스#

포커스를 받지 않습니다. Flutter에 FocusNode/Focus/FocusScope가 없고 Web에 tabindex가 없어 탭 순서에 끼어들지 않습니다. "반응형"이라는 이름과 달리 tier 판정은 빌드 시점의 resolveBreakpoint 조회일 뿐, 인터랙티브 표면이 아닙니다.

스크린 리더#

스스로는 아무것도 읽히지 않고 리더는 곧장 child로 통과합니다. 브레이크포인트 tier가 바뀌어 max-width가 달라져도 아무런 안내가 없습니다 — 레이아웃 변경은 시각적으로만 일어납니다.

알려진 제약#

  • 랜드마크 시맨틱이 없습니다. 페이지 본문 래퍼로 쓴다면 랜드마크는 직접 붙여야 합니다. Web은 attributes: {'role': 'main'} 패스스루로 주입할 수 있지만, Flutter 생성자에는 attributes/시맨틱 파라미터가 아예 없어 호출자가 바깥에서 Semantics로 감싸야 합니다 — 이 비대칭은 코드 그대로입니다.
  • 포커스 관리·키보드 조작·레이아웃 변경 안내가 전부 없습니다.

전역으로 보장되는 항목(동작 줄이기·고대비·색 강제 모드 등)은 전역 접근성 축을 참고하세요.

레거시 vs 통일 비교 (Migration Notes)#

이전 버전의 Web CoUI 를 참조하는 코드에는 이 컴포넌트가 Container 라는 이름이었을 수 있습니다. Flutter 자체 위젯인 Container 와 이름이 겹쳐 프레임워크 심볼과 충돌하므로, Web 도 Flutter-first 원칙에 따라 ResponsiveContainer 로 개명되었습니다.

항목레거시 (구 Web Container)통일 ResponsiveContainer
API동일 named properties동일 named properties
이름 충돌 Flutter/Dart Container 와 겹침 겹치지 않음, 반응형 max-width 성격을 이름에 명시

마이그레이션: Container(...)ResponsiveContainer(...) — 파라미터는 그대로이므로 클래스명만 바꾸면 됩니다.

관련 컴포넌트#

  • Basic — 여러 슬롯을 세로로 쌓는 레이아웃
  • Hidden — breakpoint 기반 조건부 렌더링