Flutter Design
플러터(Flutter)에는 두 가지 디자인 시스템이 있다.
Material Widget
구글 스타일의 머티리얼(Material) 디자인 위젯이다.
Cupertino Widget
애플 스타일의 쿠퍼티노(Cupertino) 디자인 위젯이다.
Stateful Widget의 setState({callback function}) 함수
setState() 함수는 인자로 콜백(callback) 함수를 받는다.
콜백(callback) 함수에 클래스가 가진 속성들을 변경하면 해당 코드가 반영되어 build() 함수가 실행된다.
setState() 함수 형태에 대한 예시
setState(()
x++;
})
이와 같이 하면 ‘x++’ 수행 후 build() 함수가 실행된다.
showCupertinoDialog() 함수
showCupertino() 함수는 애플(Apple) iOS 스타일의 다이얼로그를 실행하는 함수이다. 실행 시 모든 애니메이션과 작동이 iOS 스타일로 적용 됩니다.
import 'package:flutter/cupertino.dart';
showCupertinoDialog(
context: context,
barrierDismissible: true, // 외부 탭 시 다이얼로그 닫히는 옵션
builder: (BuildContext context) { // 다이얼로그 내부 위젯들 작성
return Text('Dlg');
},
);
플러터(Flutter)에서 모든 showDialog() 형태의 함수들은 BuildContext를 필수로 입력해야 한다.
플러터(Flutter)에서 다이얼로그 위젯 외에 흐림 처리가 된 부분을 배리어(barrier)라고 부른다. barrierDismissible 옵션이 바로 그 배리어(barrier)영역을 탭 했을 때 다이얼로그를 닫을지 여부를 결정하는 옵션이다.
예제 : D-Day APP
특정 일을 설정하면 지금까지 며칠이나 흘렀는지 계산해주는 앱을 만들며 Cupertino Widget을 사용해 보자.
asset에 이미지와 폰트 추가 및 pubspec.yaml 설정
이미지와 폰트 파일 추가

위와 같이 asset 폴더를 만들고 그 하위 폴더로 font, img 폴더를 생성 후, 폰트 파일과 이미지 파일을 추가해 주었다.
pubspec.yaml
flutter:
uses-material-design: true
assets:
- asset/img/
fonts:
- family: parisienne
fonts:
- asset: asset/font/Parisienne-Regular.ttf
- family: sunflower
fonts:
- asset: asset/font/Sunflower-Light.ttf
- asset: asset/font/Sunflower-Medium.ttf
weight: 500
- asset: asset/font/Sunflower-Bold.ttf
weight: 700
예제 코드와 파일 구조
component와 screen 그리고 main.dart로 폴더 구조를 잡아보았다.

couple_image.dart
import 'package:flutter/material.dart';
class CoupleImage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Expanded(
child: Center(
child: Image.asset(
'asset/img/middle_image.png',
// 이 위젯 트리에서 가장 가까운 MediaQuery 높이 값의 반을 높이로 지정
// --> Expanded로 대체
// height: MediaQuery.of(context).size.height / 2,
),
),
);
}
}
dday.dart
import 'package:flutter/material.dart';
class DDay extends StatelessWidget {
final GestureTapCallback onHeartPressed;
final DateTime firstDay;
const DDay({required this.onHeartPressed, required this.firstDay});
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
final now = DateTime.now();
return Column(
children: [
const SizedBox(height: 16.0),
Text('U&I', style: textTheme.displayLarge),
const SizedBox(height: 16.0),
Text('우리 처음 만난 날', style: textTheme.bodyLarge),
Text(
'${firstDay.year}.${firstDay.month}.${firstDay.day}',
style: textTheme.bodyMedium,
),
const SizedBox(height: 16.0),
IconButton(
onPressed: onHeartPressed,
icon: Icon(Icons.favorite),
color: Colors.red,
iconSize: 60.0,
),
const SizedBox(height: 16.0),
Text(
'D+${DateTime(now.year, now.month, now.day).difference(firstDay).inDays + 1}',
style: textTheme.displayMedium,
),
],
);
}
}
home_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../component/couple_image.dart';
import '../component/dday.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<StatefulWidget> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
DateTime firstDay = DateTime.now();
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.pink[100], // 배경색 분홍색으로
body: SafeArea(
top: true,
bottom: false,
child: Column(
// 위, 아래 끝에 위젯 배치
mainAxisAlignment: MainAxisAlignment.spaceBetween,
// 반대축 늘리기
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
DDay(onHeartPressed: onHeartPressed, firstDay: firstDay),
CoupleImage(),
],
),
),
);
}
void onHeartPressed() {
showCupertinoDialog(
context: context,
builder: (BuildContext context) {
return Align(
alignment: Alignment.bottomCenter,
child: Container(
color: Colors.white,
height: 300,
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.date,
onDateTimeChanged: (DateTime date) {
setState(() {
firstDay = date;
});
},
),
),
);
},
barrierDismissible: true,
);
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:u_and_i/screen/home_screen.dart';
void main() {
runApp(
MaterialApp(
theme: ThemeData(
fontFamily: 'sunflower', // 기본 글씨체
textTheme: TextTheme(
displayLarge: TextStyle( // H1 스타일 정의
color: Colors.white, // 글자 색상
fontSize: 80.0, // 글자 크기
fontWeight: FontWeight.w700, // 글자 두께
fontFamily: 'parisienne', // 글씨체
),
displayMedium: TextStyle(
color: Colors.white,
fontSize: 50.0,
fontWeight: FontWeight.w700,
),
bodyLarge: TextStyle(color: Colors.white, fontSize: 30.0),
bodyMedium: TextStyle(color: Colors.white, fontSize: 20.0),
),
),
home: HomeScreen(),
),
);
}