Mockup#
콘텐츠를 실제 디바이스나 앱 화면처럼 감싸는 목업 프레임 컴포넌트입니다. 문서, 랜딩 페이지, 스크린샷 등 시각적 프레젠테이션에 활용됩니다.
Live Preview#
class MockupPhoneExample extends StatefulComponent {
const MockupPhoneExample({super.key});
@override
State<MockupPhoneExample> createState() => _MockupPhoneExampleState();
}
class _MockupPhoneExampleState extends State<MockupPhoneExample> {
@override
Component build(BuildContext context) {
return MockupPhone(
child: Text('App Screen').bodyMedium.onSurface,
);
}
}
class MockupPhoneExample extends StatefulWidget {
const MockupPhoneExample({super.key});
@override
State<MockupPhoneExample> createState() => _MockupPhoneExampleState();
}
class _MockupPhoneExampleState extends State<MockupPhoneExample> {
@override
Widget build(BuildContext context) {
return MockupPhone(
child: Text('App Screen').bodyMedium.onSurface,
);
}
}
class MockupBrowserExample extends StatefulComponent {
const MockupBrowserExample({super.key});
@override
State<MockupBrowserExample> createState() => _MockupBrowserExampleState();
}
class _MockupBrowserExampleState extends State<MockupBrowserExample> {
@override
Component build(BuildContext context) {
return MockupBrowser(
addressBar: 'https://coui.dev',
child: Text('Page content').bodyMedium.onSurface,
);
}
}
class MockupBrowserExample extends StatefulWidget {
const MockupBrowserExample({super.key});
@override
State<MockupBrowserExample> createState() => _MockupBrowserExampleState();
}
class _MockupBrowserExampleState extends State<MockupBrowserExample> {
@override
Widget build(BuildContext context) {
return MockupBrowser(
addressBar: 'https://coui.dev',
child: Text('Page content').bodyMedium.onSurface,
);
}
}
class MockupCodeExample extends StatefulComponent {
const MockupCodeExample({super.key});
@override
State<MockupCodeExample> createState() => _MockupCodeExampleState();
}
class _MockupCodeExampleState extends State<MockupCodeExample> {
@override
Component build(BuildContext context) {
return MockupCode.fromString(
"void main() {\n print('Hello!');\n}",
showLineNumbers: true,
);
}
}
class MockupCodeExample extends StatefulWidget {
const MockupCodeExample({super.key});
@override
State<MockupCodeExample> createState() => _MockupCodeExampleState();
}
class _MockupCodeExampleState extends State<MockupCodeExample> {
@override
Widget build(BuildContext context) {
return MockupCode.fromString(
"void main() {\n print('Hello!');\n}",
);
}
}
class MockupWindowExample extends StatefulComponent {
const MockupWindowExample({super.key});
@override
State<MockupWindowExample> createState() => _MockupWindowExampleState();
}
class _MockupWindowExampleState extends State<MockupWindowExample> {
@override
Component build(BuildContext context) {
return MockupWindow(
child: Text('Window content').bodyMedium.onSurface,
);
}
}
class MockupWindowExample extends StatefulWidget {
const MockupWindowExample({super.key});
@override
State<MockupWindowExample> createState() => _MockupWindowExampleState();
}
class _MockupWindowExampleState extends State<MockupWindowExample> {
@override
Widget build(BuildContext context) {
return MockupWindow(
child: Text('Window content').bodyMedium.onSurface,
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 랜딩 페이지에서 앱/웹 화면을 디바이스 프레임 안에 표시할 때
- 기술 문서에서 코드 예제를 코드 에디터 스타일로 표시할 때
- 데모나 쇼케이스에서 스크린샷을 실제 기기처럼 보여줄 때
대신 다른 컴포넌트를 사용하세요:
Card: 단순한 콘텐츠 카드 레이아웃에CodeSnippet: 코드 하이라이팅이 필요한 코드 블록에
기본 사용법 (Basic Usage)#
// 브라우저 목업
MockupBrowser(
addressBar: 'https://coui.cocode.im',
child: Image.asset('assets/screenshots/dashboard.png'),
)
// 폰 목업
MockupPhone(
child: Image.asset('assets/screenshots/app_home.png'),
)
// 코드 에디터 목업
MockupCode.fromString('void main() {\n runApp(const MyApp());\n}')
// 윈도우 목업
MockupWindow(
child: Text('윈도우 내용'),
)
// 브라우저 목업
MockupBrowser(
addressBar: 'https://coui.cocode.im',
child: img(src: 'screenshot.png', alt: 'CoUI 디자인 시스템'),
)
// 폰 목업
MockupPhone(
child: img(src: 'app-screenshot.png', alt: '앱 화면'),
)
// 코드 목업 (fromString 팩토리 생성자)
MockupCode.fromString(
'npm i coui\ninstalling...\nDone!',
showLineNumbers: true,
)
// 윈도우 목업
MockupWindow(
child: Text('윈도우 내용'),
)
Props / Parameters#
각 목업은 별도의 통일 컴포넌트 (MockupBrowser / MockupPhone /
MockupCode / MockupWindow) 로 제공됩니다. chrome / colour /
border-radius override 는 모두 각 컴포넌트의 단일 *Style 슬롯으로
흐릅니다 (Epic #1302).
MockupBrowser#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
addressBar | String | 필수 | 주소창에 표시할 URL 텍스트 |
child |
Widget / Component |
필수 | 브라우저 뷰포트 내용 |
mockupBrowserStyle |
CoreMockupBrowserStyle? |
null |
브라우저 frame chrome / colour / border-radius override (Epic #1302 nested Style API) |
MockupPhone#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
child | Widget | 필수 | 폰 화면 내용 |
width |
double? |
320 |
폰 프레임 폭 (logical px / Web px) |
mockupPhoneStyle |
CoreMockupPhoneStyle? |
null |
폰 프레임 colour / border-radius chrome override (Epic #1302 nested Style API) |
MockupCode#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
lines |
List<CoreCodeLine> |
필수 | 표시할 코드 라인 (factory 생성자로 문자열에서 변환 가능) |
mockupCodeStyle |
CoreMockupCodeStyle? |
null |
코드 블록 배경 / 텍스트 colour / prefix colour / border-radius / padding / line spacing / line-number width chrome override (Epic #1302 nested Style API) |
스타일 시스템 (Style System)#
Epic #1302 nested Style API. MockupPhone 과 MockupCode 는
각자 전용 Style 슬롯을 갖습니다. 공유 CoreMockupTheme 의 legacy
flat 필드는 백업 fallback 으로 유지됩니다.
CoreMockupPhoneStyle#
| 슬롯 | 타입 | 비고 |
|---|---|---|
backgroundColor | CoreColor? | 폰 화면 배경 colour. |
frameColor | CoreColor? | 폰 외부 shell 프레임 fill colour. |
borderColor |
CoreColor? |
폰 외부 shell border / outline colour. |
borderRadius |
CoreBorderRadius? |
외부 프레임 border radius. |
CoreMockupCodeStyle#
| 슬롯 | 타입 | 비고 |
|---|---|---|
backgroundColor | CoreColor? | 코드 블록 배경 colour. |
textColor | CoreColor? | 코드 텍스트 colour. |
borderRadius |
CoreBorderRadius? |
외부 컨테이너 border radius (logical px). |
Resolve chain#
design system default
→ CoreMockupTheme (legacy shared chrome fields)
→ CoreMockupCodeTheme.style / CoreMockupPhoneTheme.style // 프로젝트 공통
→ parent component slot override
→ widget.mockupCodeStyle / widget.mockupPhoneStyle // 인스턴스별
MockupWindow#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
child |
Widget / Component |
null |
윈도우 내용 |
mockupWindowStyle |
CoreMockupWindowStyle? |
null |
윈도우 frame chrome / colour / border-radius override (Epic #1302 nested Style API) |
변형 (Variants)#
Browser#
주소창이 있는 브라우저 프레임입니다.
MockupBrowser(
addressBar: 'https://example.com/dashboard',
child: DashboardScreenshot(),
)
Phone#
모바일 기기 외형(카메라 노치 등)을 갖춘 스마트폰 프레임입니다.
MockupPhone(
child: MobileAppPreview(),
)
Code#
코드 에디터 스타일의 프레임입니다. 선택적으로 줄 번호가 표시됩니다.
MockupCode.fromString(
'print("Hello, World!");',
showLineNumbers: true,
)
Window#
데스크탑 앱 윈도우 스타일의 프레임입니다. 트래픽라이트 컨트롤이 포함됩니다.
MockupWindow(
child: AppContent(),
)
동작 스펙 (Behavior)#
인터랙션#
- 기본적으로 정적 프레임으로 인터랙션 없음
child에 인터랙션 가능한 위젯을 넣으면 프레임 안에서 동작
상태 전환#
- 정적 컴포넌트로 별도 상태 전환 없음
애니메이션#
- 별도 애니메이션 없음 (자식 위젯의 애니메이션은 그대로 동작)
사용 가이드라인 (Usage Guidelines)#
✅ Do#
랜딩 페이지 히어로 섹션에 실제 앱 스크린샷을 Phone 목업으로 표시
MockupPhone(
child: Image.asset(
'assets/screenshots/app_home.png',
fit: BoxFit.cover,
),
)
디바이스 프레임 안의 스크린샷은 앱의 실제 UI를 자연스럽게 보여줘 사용자 신뢰를 높인다.
❌ Don't#
실제 동작하는 복잡한 앱을 Phone 목업 안에 렌더링
// ❌ 성능 문제 야기할 수 있음
MockupPhone(
child: MaterialApp(
home: ComplexAppWithManyAnimations(),
),
)
목업 안에 무거운 위젯을 렌더링하면 성능 문제가 발생한다. 스크린샷 이미지로 대체하는 것이 좋다.
✅ Do#
문서에서 코드 예제를 Code 목업으로 표시해 가독성 향상
MockupCode.fromString(
"Button(\n onPressed: handlePressed,\n child: const Text('클릭'),\n)",
)
코드 에디터 스타일 프레임이 코드 블록을 더 전문적이고 읽기 쉽게 만든다.
❌ Don't#
실제 사용자 인터페이스에 Mockup을 UI 컨테이너로 사용
// ❌ 실제 앱 UI에 Phone 목업 사용
Scaffold(
body: MockupPhone( // 실제 앱인데 Phone 프레임?
child: actualAppContent,
),
)
Mockup은 프레젠테이션/문서 목적이며 실제 앱 UI 컨테이너로 사용하면 혼란스럽고 불필요한 비주얼 레이어를 추가한다.
✅ Do#
목업에는 실제와 유사한 예시 콘텐츠를 사용하세요.
MockupPhone(
child: AppScreenPreview(
// 실제 앱 화면과 유사한 내용
content: RealisticDemoContent(),
),
)
실제와 유사한 콘텐츠로 목업을 채우면 디자인 리뷰나 문서에서 컴포넌트의 실제 사용 모습을 효과적으로 전달할 수 있습니다.
❌ Don't#
목업을 실제 프로덕션 레이아웃의 컨테이너로 사용하지 마세요.
// ❌ 실제 앱에서 Mockup을 레이아웃으로 사용
Scaffold(
body: MockupBrowser(
addressBar: 'https://example.com',
child: actualAppContent, // 실제 콘텐츠
),
)
Mockup은 문서화, 프레젠테이션, Widgetbook 등 데모 목적으로만 사용합니다. 실제 앱 레이아웃으로는 사용하지 마세요.
접근성 (Accessibility)#
키보드 인터랙션#
| 키 | 동작 |
|---|---|
| 해당 없음 | 정적 프레임 컴포넌트; 내부 자식 위젯의 키보드 인터랙션은 그대로 동작 |
스크린 리더#
- Flutter:
Semantics(label: '브라우저 목업: CoUI 문서')형태로 프레임 설명 추가 권장 -
Web:
role="img"+aria-label로 목업 프레임을 장식적 요소로 표시; 내부 콘텐츠 접근성은 자식 위젯이 처리
터치 타겟#
- 목업 프레임 자체는 터치 타겟 없음
- 내부 인터랙션 요소는 자식 위젯의 타겟 크기를 따름
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | MockupBrowser / MockupPhone 등 |
MockupBrowser / MockupPhone 등 |
| 렌더링 | CustomPaint 기반 프레임 | CSS box-shadow + 보더 기반 |
| 폰 노치 | ClipRRect + 노치 페인터 | CSS pseudo 요소 |
| 브라우저 주소창 | 커스텀 위젯 | HTML + CSS |
관련 컴포넌트 (Related Components)#
- Card: 단순한 콘텐츠 컨테이너가 필요할 때 사용
- CodeSnippet: 코드 하이라이팅이 필요한 코드 블록에 사용
조합 예제#
// 랜딩 페이지 히어로 섹션
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('CoUI로 아름다운 UI 만들기',
style: Theme.of(context).textTheme.headlineLarge),
const Gap.md(),
Text('Flutter와 Web을 동시에 지원하는 디자인 시스템'),
const Gap.lg(),
Button(
onPressed: handleGetStarted,
child: const Text('시작하기'),
),
],
),
),
const Gap.xl(),
Expanded(
child: MockupBrowser(
addressBar: 'https://coui.cocode.im',
child: Image.asset('assets/screenshots/landing.png'),
),
),
],
)