Flutter 튜토리얼 5편: Material과 Cupertino 위젯
요약
핵심 요지
- 문제 정의: Android와 iOS 사용자는 각 플랫폼에 익숙한 UI를 기대한다.
- 핵심 주장: Flutter는
Material Design1과Cupertino2 두 가지 디자인 시스템을 제공한다. - 주요 근거: Material은 Android 스타일, Cupertino는 iOS 스타일의 위젯을 각각 제공하여 플랫폼에 맞는 UX를 구현할 수 있다.
- 실무 기준: 대부분의 앱은 Material Design을 기본으로 사용하고, iOS 네이티브 경험이 중요하면 Cupertino를 활용한다.
- 한계: 두 시스템을 혼용하면 코드가 복잡해질 수 있다.
문서가 설명하는 범위
- Material Design 3의 핵심 개념과 주요 위젯
- Cupertino 디자인의 특징과 주요 위젯
- 플랫폼별 위젯 선택 전략
읽는 시간: 14분 | 난이도: 초급
참고 자료
- Material widgets - Material 위젯 카탈로그
- Cupertino widgets - Cupertino 위젯 카탈로그
- Material Design - Material Design 구현 가이드
문제 상황
크로스플랫폼 앱을 만들 때 한 가지 디자인만 사용하면 어떻게 될까요?
특정 플랫폼 사용자에게 어색한 경험을 줄 수 있습니다.
Android 사용자는 Material Design에 익숙하고, iOS 사용자는 Apple의 Human Interface Guidelines에 익숙하기 때문입니다.
플랫폼별 기대
Android 사용자:- 화면 하단의 네비게이션 바- FloatingActionButton- 카드와 그림자 효과
iOS 사용자:- 화면 상단의 큰 제목- 하단 탭 바와 액션 시트- 블러 효과와 반투명 요소문제는 다음과 같습니다.
- 하나의 디자인으로 두 플랫폼을 만족시키기 어렵다.
- 플랫폼별 코드를 직접 분기하면 유지보수가 복잡해진다.
- 네이티브 느낌을 주지 못하면 사용자 경험이 저하된다.
해결 방법
Flutter는 Material과 Cupertino라는 두 가지 디자인 시스템을 제공합니다.
Material은 Android 스타일, Cupertino는 iOS 스타일입니다.
각 시스템은 해당 플랫폼의 디자인 가이드라인을 충실히 구현합니다.
챕터 1: Material Design 이해하기
Why
NOTEMaterial Design은 Google이 만든 디자인 시스템입니다.
Android뿐 아니라 웹, 데스크톱에서도 널리 사용됩니다.
Flutter는 Material Design을 기본으로 채택하고 있어, 별도 설정 없이 바로 사용할 수 있습니다.// Material Design이 기본MaterialApp(home: Scaffold(...),)
What
NOTE
Material Design 33는 Google이 2021년 발표한 최신 디자인 시스템입니다.
Flutter 3.16부터 Material 3가 기본으로 활성화되어 있습니다.
새 프로젝트를 만들면 자동으로 적용됩니다.
How
TIPMaterial Design의 핵심 요소는 다음과 같습니다.
요소 설명 예시 색상 시스템 동적 색상과 테마 ColorScheme, ThemeData 타이포그래피 일관된 텍스트 스타일 TextTheme 컴포넌트 미리 정의된 위젯 Button, Card, AppBar 음영과 깊이 계층 표현 elevation 속성 Material 3 활성화 확인
MaterialApp(theme: ThemeData(useMaterial3: true, // 기본값 (Flutter 3.16+)colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue,),),home: MyApp(),)기본 Material 앱 구조
MaterialApp(home: Scaffold(appBar: AppBar(title: Text('Material 앱'),),body: Center(child: ElevatedButton(onPressed: () {},child: Text('버튼'),),),floatingActionButton: FloatingActionButton(onPressed: () {},child: Icon(Icons.add),),),)
Watch out
WARNINGMaterial 2에서 Material 3로 마이그레이션할 때 일부 위젯의 외관이 변경됩니다. 기존 앱의 디자인이 변경될 수 있으므로 확인이 필요합니다.
// Material 2로 유지하려면 (권장하지 않음)ThemeData(useMaterial3: false, // 향후 지원 중단 예정)
결론: Flutter는 Material 3를 기본으로 사용하며, 모던하고 일관된 디자인을 제공합니다.
챕터 2: Material 주요 위젯
Why
NOTEMaterial Design은 6가지 카테고리로 위젯을 분류합니다.
버튼, 네비게이션, 선택, 컨테이너 등으로 나뉘어 있습니다.
각 카테고리의 대표 위젯을 알면 필요한 위젯을 빠르게 찾을 수 있습니다.
What
NOTEMaterial 위젯은 Actions, Communication, Containment, Navigation, Selection, Text inputs로 분류됩니다.
How
TIPActions (작업) 위젯
사용자 동작을 시작하는 버튼류 위젯입니다.
위젯 용도 예시 ElevatedButton주요 작업 저장, 전송 FilledButton강조 작업 확인 OutlinedButton보조 작업 취소 TextButton덜 중요한 작업 더 보기 FloatingActionButton4화면의 주요 작업 새 글 작성 IconButton아이콘 클릭 좋아요, 공유 Column(children: [ElevatedButton(onPressed: () {}, child: Text('Elevated')),FilledButton(onPressed: () {}, child: Text('Filled')),OutlinedButton(onPressed: () {}, child: Text('Outlined')),TextButton(onPressed: () {}, child: Text('Text')),],)Navigation (네비게이션) 위젯
앱 내 이동을 담당하는 위젯입니다.
위젯 용도 위치 AppBar화면 제목과 작업 상단 NavigationBar주요 목적지 전환 하단 NavigationRail태블릿/데스크톱 네비게이션 좌측 NavigationDrawer메뉴 목록 슬라이드 TabBar탭 전환 상단/하단 Scaffold(appBar: AppBar(title: Text('제목')),body: content,bottomNavigationBar: NavigationBar(destinations: [NavigationDestination(icon: Icon(Icons.home), label: '홈'),NavigationDestination(icon: Icon(Icons.search), label: '검색'),NavigationDestination(icon: Icon(Icons.person), label: '프로필'),],selectedIndex: 0,onDestinationSelected: (index) {},),)Selection (선택) 위젯
사용자가 옵션을 선택하는 폼 컨트롤입니다.
위젯 용도 선택 방식 Checkbox다중 선택 체크 Radio단일 선택 라디오 SwitchON/OFF 토글 스위치 Slider범위 선택 슬라이더 Chip필터링, 선택 칩 DropdownMenu목록 선택 드롭다운 Column(children: [Checkbox(value: true, onChanged: (v) {}),Switch(value: false, onChanged: (v) {}),Slider(value: 0.5, onChanged: (v) {}),],)Containment (컨테이너) 위젯
콘텐츠를 담는 컨테이너 위젯입니다.
위젯 용도 Card관련 정보 그룹화 AlertDialog사용자 확인 요청 BottomSheet추가 옵션 표시 ListTile목록 항목 Divider구분선 Card(child: ListTile(leading: Icon(Icons.album),title: Text('제목'),subtitle: Text('부제목'),trailing: Icon(Icons.more_vert),),)
Watch out
WARNINGMaterial 위젯은 Material ancestor가 필요합니다.
MaterialApp없이 Material 위젯을 사용하면 오류가 발생합니다.// 오류: Material ancestor 없음runApp(ElevatedButton(...)); // 오류!// 올바른 사용runApp(MaterialApp(home: ElevatedButton(...),));
결론: Material 위젯은 카테고리별로 정리되어 있어 목적에 맞는 위젯을 쉽게 찾을 수 있습니다.
챕터 3: Cupertino 디자인 이해하기
Why
NOTEiOS 사용자는 Apple의 Human Interface Guidelines에 익숙합니다.
iOS 앱다운 네이티브 경험을 제공하려면 어떻게 해야 할까요?
Cupertino 위젯을 사용하면 됩니다.Material: 그림자, 물결 효과, 밝은 색상Cupertino: 반투명, 블러 효과, 미니멀
What
NOTECupertino는 Flutter에서 iOS/macOS 스타일의 위젯을 제공합니다.
Apple의 Human Interface Guidelines를 따르는 디자인을 구현합니다.
반투명 효과, 블러, 미니멀한 디자인이 특징입니다.
How
TIPCupertino 앱 기본 구조
import 'package:flutter/cupertino.dart';CupertinoApp(home: CupertinoPageScaffold(navigationBar: CupertinoNavigationBar(middle: Text('Cupertino 앱'),),child: Center(child: CupertinoButton(onPressed: () {},child: Text('버튼'),),),),)Material vs Cupertino 비교
기능 Material Cupertino 앱 루트 MaterialApp CupertinoApp 페이지 구조 Scaffold CupertinoPageScaffold 앱 바 AppBar CupertinoNavigationBar 버튼 ElevatedButton CupertinoButton 스위치 Switch CupertinoSwitch 알림창 AlertDialog CupertinoAlertDialog 탭 바 BottomNavigationBar CupertinoTabBar 텍스트 필드 TextField CupertinoTextField 시각적 차이
graph LR subgraph Material A[AppBar] --> B[그림자와 elevation] C[Button] --> D[물결 효과 ripple] E[Switch] --> F[둥근 사각형] end subgraph Cupertino G[NavigationBar] --> H[반투명 블러] I[Button] --> J[하이라이트 효과] K[Switch] --> L[캡슐 모양] end
Watch out
WARNINGCupertino 위젯은
CupertinoApp또는CupertinoTheme이 필요합니다. Material 앱에서 Cupertino 위젯을 사용하려면 추가 설정이 필요합니다.// Material 앱에서 Cupertino 위젯 사용MaterialApp(home: Scaffold(body: CupertinoButton( // 일부 위젯은 동작하지만child: Text('버튼'), // 테마가 적용되지 않을 수 있음onPressed: () {},),),)
결론: iOS 네이티브 경험이 중요한 앱에서는 Cupertino 위젯을 활용합니다.
챕터 4: Cupertino 주요 위젯
Why
NOTECupertino 위젯은 iOS 특유의 UX 패턴을 구현합니다.
피커, 액션 시트, 슬라이딩 세그먼트 등이 있습니다.
이런 컴포넌트는 iOS에서만 볼 수 있는 것들입니다.
What
NOTECupertino 위젯은 네비게이션, 입력, 선택, 피드백 등의 카테고리로 분류됩니다.
How
TIP네비게이션 위젯
위젯 용도 특징 CupertinoNavigationBar상단 네비게이션 자동 뒤로가기 CupertinoSliverNavigationBar큰 제목 지원 iOS 11 스타일 CupertinoTabBar하단 탭 바 아이콘 + 라벨 CupertinoTabScaffold탭 기반 레이아웃 TabBar + 콘텐츠 CupertinoTabScaffold(tabBar: CupertinoTabBar(items: [BottomNavigationBarItem(icon: Icon(CupertinoIcons.home), label: '홈'),BottomNavigationBarItem(icon: Icon(CupertinoIcons.search), label: '검색'),BottomNavigationBarItem(icon: Icon(CupertinoIcons.settings), label: '설정'),],),tabBuilder: (context, index) {return CupertinoPageScaffold(child: Center(child: Text('탭 $index')),);},)선택 위젯 (Picker)
iOS의 특징적인 휠 스타일 피커입니다.
위젯 용도 CupertinoPicker일반 항목 선택 CupertinoDatePicker날짜/시간 선택 CupertinoTimerPicker타이머 설정 CupertinoSlidingSegmentedControl세그먼트 선택 CupertinoDatePicker(mode: CupertinoDatePickerMode.date,onDateTimeChanged: (DateTime newDate) {print(newDate);},)대화상자 위젯
위젯 용도 등장 방식 CupertinoAlertDialog확인/취소 선택 중앙 팝업 CupertinoActionSheet여러 옵션 선택 하단 슬라이드 CupertinoContextMenu컨텍스트 메뉴 롱프레스 // 알림창 표시showCupertinoDialog(context: context,builder: (context) => CupertinoAlertDialog(title: Text('제목'),content: Text('내용'),actions: [CupertinoDialogAction(child: Text('취소'),onPressed: () => Navigator.pop(context),),CupertinoDialogAction(isDefaultAction: true,child: Text('확인'),onPressed: () => Navigator.pop(context),),],),);입력 위젯
위젯 용도 CupertinoTextField텍스트 입력 CupertinoSearchTextField검색 입력 CupertinoSwitchON/OFF 토글 CupertinoSlider범위 선택 CupertinoTextField(placeholder: '이름을 입력하세요',prefix: Icon(CupertinoIcons.person),padding: EdgeInsets.all(12),)
Watch out
WARNINGCupertino 아이콘은 별도 패키지인
cupertino_icons가 필요합니다.pubspec.yaml에서 의존성을 확인하세요.dependencies:flutter:sdk: fluttercupertino_icons: ^1.0.2 # 필수
결론: Cupertino 위젯으로 피커, 액션시트 등 iOS 특유의 UX를 구현합니다.
챕터 5: 플랫폼별 위젯 선택 전략
Why
NOTE크로스플랫폼 앱에서 어떤 디자인 시스템을 사용해야 할까요?
모든 플랫폼에서 동일한 디자인을 쓸지, 플랫폼별로 다르게 할지 선택이 필요합니다.
세 가지 전략이 있습니다.
What
NOTE세 가지 전략이 있습니다: Material 통일, 플랫폼별 분기, 하이브리드 방식입니다.
How
TIP전략 1: Material 통일
모든 플랫폼에서 Material Design을 사용합니다.
// 단순하고 일관된 코드MaterialApp(home: Scaffold(appBar: AppBar(title: Text('앱')),body: MyContent(),),)
장점 단점 코드 단순화 iOS에서 어색할 수 있음 일관된 디자인 플랫폼 기대와 다름 유지보수 용이 네이티브 경험 부족 전략 2: 플랫폼별 분기
Platform에 따라 다른 위젯을 사용합니다.
import 'dart:io';Widget build(BuildContext context) {if (Platform.isIOS) {return CupertinoApp(home: CupertinoPageScaffold(...),);}return MaterialApp(home: Scaffold(...),);}
장점 단점 네이티브 경험 코드 복잡도 증가 플랫폼 기대 충족 유지보수 부담 최적화된 UX 두 배의 위젯 코드 전략 3: 하이브리드 방식 (권장)
기본은 Material을 사용하고, 필요한 부분만 플랫폼별로 분기합니다.
MaterialApp(home: Scaffold(appBar: AppBar(title: Text('앱')),body: Column(children: [// 공통 콘텐츠Text('콘텐츠'),// 플랫폼별 위젯Platform.isIOS? CupertinoButton(onPressed: () {}, child: Text('버튼')): ElevatedButton(onPressed: () {}, child: Text('버튼')),],),),)플랫폼 적응형 위젯 사용
일부 위젯은 자동으로 플랫폼에 적응합니다.
// 자동 적응 예시showDialog(...) // Material AlertDialogshowCupertinoDialog(...) // Cupertino AlertDialog// 적응형 위젯Switch.adaptive(...) // 플랫폼별 자동 적응Slider.adaptive(...)
Watch out
WARNING
Platform.isIOS는 웹에서 사용할 수 없습니다. 웹을 지원하려면Theme.of(context).platform을 사용하세요.// 웹에서도 동작하는 방식final isIOS = Theme.of(context).platform == TargetPlatform.iOS;
결론: 대부분의 앱은 Material을 기본으로 하고, 필요한 곳에서만 플랫폼별 분기를 사용합니다.
한계
디자인 시스템 선택에는 트레이드오프가 있습니다.
어떤 방식이든 장단점이 있습니다.
- 코드 복잡도: 플랫폼별 분기가 많아지면 유지보수가 어려워집니다.
- 일관성 vs 네이티브: 모든 플랫폼에서 동일한 경험과 플랫폼별 최적화 사이의 균형이 필요합니다.
- 학습 비용: 두 디자인 시스템을 모두 익혀야 합니다.
Footnotes
-
Material Design(머티리얼 디자인): Google이 만든 디자인 시스템으로 그림자, 깊이, 물결 효과 등이 특징이다. ↩
-
Cupertino(쿠퍼티노): Apple의 Human Interface Guidelines를 따르는 iOS/macOS 스타일 디자인이다. Apple 본사가 있는 도시 이름에서 유래했다. ↩
-
Material 3(머티리얼 3): 2021년 발표된 Material Design의 최신 버전으로 동적 색상, 개선된 접근성이 특징이다. ↩
-
FloatingActionButton(플로팅 액션 버튼): 화면 위에 떠 있는 원형 버튼으로 앱의 주요 작업을 나타낸다. ↩
공유
이 글이 도움이 되었다면 다른 사람과 공유해주세요!