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 CoMockupPhone(
child: text('App Screen'),
);
}
}
class MockupPhoneExample extends StatefulWidget {
const MockupPhoneExample({super.key});
@override
State<MockupPhoneExample> createState() => _MockupPhoneExampleState();
}
class _MockupPhoneExampleState extends State<MockupPhoneExample> {
@override
Widget build(BuildContext context) {
return const CoMockupPhone(
child: Text('App Screen'),
);
}
}
class MockupBrowserExample extends StatefulComponent {
const MockupBrowserExample({super.key});
@override
State<MockupBrowserExample> createState() => _MockupBrowserExampleState();
}
class _MockupBrowserExampleState extends State<MockupBrowserExample> {
@override
Component build(BuildContext context) {
return CoMockupBrowser(
addressBar: 'https://coui.dev',
child: text('Page content'),
);
}
}
class MockupBrowserExample extends StatefulWidget {
const MockupBrowserExample({super.key});
@override
State<MockupBrowserExample> createState() => _MockupBrowserExampleState();
}
class _MockupBrowserExampleState extends State<MockupBrowserExample> {
@override
Widget build(BuildContext context) {
return CoMockupBrowser(
addressBar: 'https://dev',
child: const Text('Page content'),
);
}
}
class MockupCodeExample extends StatefulComponent {
const MockupCodeExample({super.key});
@override
State<MockupCodeExample> createState() => _MockupCodeExampleState();
}
class _MockupCodeExampleState extends State<MockupCodeExample> {
@override
Component build(BuildContext context) {
return CoMockupCode.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 CoMockupCode.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 CoMockupWindow(
child: text('Window content'),
);
}
}
class MockupWindowExample extends StatefulWidget {
const MockupWindowExample({super.key});
@override
State<MockupWindowExample> createState() => _MockupWindowExampleState();
}
class _MockupWindowExampleState extends State<MockupWindowExample> {
@override
Widget build(BuildContext context) {
return const CoMockupWindow(
child: Text('Window content'),
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 랜딩 페이지에서 앱/웹 화면을 디바이스 프레임 안에 표시할 때
- 기술 문서에서 코드 예제를 코드 에디터 스타일로 표시할 때
- 데모나 쇼케이스에서 스크린샷을 실제 기기처럼 보여줄 때
대신 다른 컴포넌트를 사용하세요:
Card: 단순한 콘텐츠 카드 레이아웃에CodeSnippet: 코드 하이라이팅이 필요한 코드 블록에
기본 사용법 (Basic Usage)#
// 브라우저 목업
CoMockupBrowser(
url: 'https://coui.cocode.im',
title: 'CoUI 디자인 시스템',
child: Image.asset('assets/screenshots/dashboard.png'),
)
// 폰 목업
CoMockupPhone(
child: Scaffold(
appBar: AppBar(title: Text('앱 화면')),
body: Center(child: Text('콘텐츠')),
),
)
// 코드 에디터 목업
CoMockupCode(
child: Text(
'''void main() {
runApp(const MyApp());
}''',
style: TextStyle(fontFamily: 'monospace'),
),
)
// 윈도우 목업
CoMockupWindow(
title: '내 앱',
child: Container(
padding: EdgeInsets.all(kDefaultPadding),
child: Text('윈도우 내용'),
),
)
// 브라우저 목업 (addressBar + content 파라미터 사용)
CoMockupBrowser(
addressBar: text('https://coui.cocode.im'),
content: img(src: 'screenshot.png', alt: 'CoUI 디자인 시스템'),
)
// 폰 목업 (content 파라미터 사용)
CoMockupPhone(
child: img(src: 'app-screenshot.png', alt: '앱 화면'),
)
// 코드 목업 (fromString 팩토리 생성자)
CoMockupCode.fromString(
'npm i coui\ninstalling...\nDone!',
showLineNumbers: true,
)
// 윈도우 목업 (children 위치 인수 방식)
CoMockupWindow(
[
div(
[text('윈도우 내용')],
classes: 'border-t border-base-300 p-4',
),
],
)
Props / Parameters#
Mockup (공통)#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
variant |
MockupVariant |
필수 | 목업 종류 (browser/phone/code/window) |
child | Widget | 필수 | 목업 내부에 표시할 위젯 |
MockupBrowser#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
child | Widget | 필수 | 브라우저 뷰포트 내용 |
url |
String? |
null |
주소창에 표시할 URL |
title |
String? |
null |
탭에 표시할 페이지 제목 |
MockupPhone#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
child | Widget | 필수 | 폰 화면 내용 |
MockupCode#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
child | Widget | 필수 | 코드 에디터 내용 |
MockupWindow#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
child | Widget | 필수 | 윈도우 내용 |
title |
String? |
null |
윈도우 타이틀 바 텍스트 |
변형 (Variants)#
Browser#
상단 탭 바, 주소창, 네비게이션 버튼이 있는 브라우저 프레임입니다.
CoMockupBrowser(
url: 'https://example.com/dashboard',
title: '대시보드',
child: DashboardScreenshot(),
)
Phone#
모바일 기기 외형(노치, 홈 버튼 등)을 갖춘 스마트폰 프레임입니다.
CoMockupPhone(
child: MobileAppPreview(),
)
Code#
코드 에디터 스타일의 프레임입니다. 상단에 파일명 탭이 표시됩니다.
CoMockupCode(
child: SyntaxHighlighter(
code: 'print("Hello, World!");',
language: 'dart',
),
)
Window#
데스크탑 앱 윈도우 스타일의 프레임입니다. 타이틀 바와 컨트롤 버튼이 포함됩니다.
CoMockupWindow(
title: '내 데스크탑 앱',
child: AppContent(),
)
동작 스펙 (Behavior)#
인터랙션#
- 기본적으로 정적 프레임으로 인터랙션 없음
child에 인터랙션 가능한 위젯을 넣으면 프레임 안에서 동작
상태 전환#
- 정적 컴포넌트로 별도 상태 전환 없음
애니메이션#
- 별도 애니메이션 없음 (자식 위젯의 애니메이션은 그대로 동작)
사용 가이드라인 (Usage Guidelines)#
✅ Do#
랜딩 페이지 히어로 섹션에 실제 앱 스크린샷을 Phone 목업으로 표시
CoMockupPhone(
child: Image.asset(
'assets/screenshots/app_home.png',
fit: BoxFit.cover,
),
)
디바이스 프레임 안의 스크린샷은 앱의 실제 UI를 자연스럽게 보여줘 사용자 신뢰를 높인다.
❌ Don't#
실제 동작하는 복잡한 앱을 Phone 목업 안에 렌더링
// ❌ 성능 문제 야기할 수 있음
CoMockupPhone(
child: MaterialApp(
home: ComplexAppWithManyAnimations(),
),
)
목업 안에 무거운 위젯을 렌더링하면 성능 문제가 발생한다. 스크린샷 이미지로 대체하는 것이 좋다.
✅ Do#
문서에서 코드 예제를 Code 목업으로 표시해 가독성 향상
CoMockupCode(
child: CouiCodeSnippet(
code: '''
CoButton(
onPressed: handlePressed,
child: const Text('클릭'),
)''',
language: 'dart',
),
)
코드 에디터 스타일 프레임이 코드 블록을 더 전문적이고 읽기 쉽게 만든다.
❌ Don't#
실제 사용자 인터페이스에 Mockup을 UI 컨테이너로 사용
// ❌ 실제 앱 UI에 Phone 목업 사용
Scaffold(
body: CoMockupPhone( // 실제 앱인데 Phone 프레임?
child: actualAppContent,
),
)
Mockup은 프레젠테이션/문서 목적이며 실제 앱 UI 컨테이너로 사용하면 혼란스럽고 불필요한 비주얼 레이어를 추가한다.
✅ Do#
목업에는 실제와 유사한 예시 콘텐츠를 사용하세요.
CoMockup(
type: MockupType.phone,
child: AppScreenPreview(
// 실제 앱 화면과 유사한 내용
content: RealisticDemoContent(),
),
)
실제와 유사한 콘텐츠로 목업을 채우면 디자인 리뷰나 문서에서 컴포넌트의 실제 사용 모습을 효과적으로 전달할 수 있습니다.
❌ Don't#
목업을 실제 프로덕션 레이아웃의 컨테이너로 사용하지 마세요.
// ❌ 실제 앱에서 Mockup을 레이아웃으로 사용
Scaffold(
body: CoMockup(
type: MockupType.browser,
child: actualAppContent, // 실제 콘텐츠
),
)
Mockup은 문서화, 프레젠테이션, Widgetbook 등 데모 목적으로만 사용합니다. 실제 앱 레이아웃으로는 사용하지 마세요.
접근성 (Accessibility)#
키보드 인터랙션#
| 키 | 동작 |
|---|---|
| 해당 없음 | 정적 프레임 컴포넌트; 내부 자식 위젯의 키보드 인터랙션은 그대로 동작 |
스크린 리더#
- Flutter:
Semantics(label: '브라우저 목업: CoUI 문서')형태로 프레임 설명 추가 권장 -
Web:
role="img"+aria-label로 목업 프레임을 장식적 요소로 표시; 내부 콘텐츠 접근성은 자식 위젯이 처리
터치 타겟#
- 목업 프레임 자체는 터치 타겟 없음
- 내부 인터랙션 요소는 자식 위젯의 타겟 크기를 따름
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | CoMockupBrowser / CoMockupPhone 등 |
CoMockupBrowser / CoMockupPhone 등 |
| 렌더링 | 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(),
CoButton(
onPressed: handleGetStarted,
child: const Text('시작하기'),
),
],
),
),
const Gap.xl(),
Expanded(
child: CoMockupBrowser(
url: 'https://coui.cocode.im',
title: 'CoUI 디자인 시스템',
child: Image.asset('assets/screenshots/landing.png'),
),
),
],
)