Flutter 앱 개발에서 자주 사용되는 asset과 로컬 폰트 설정 방법
asset이란?
- asset은 앱 구성에 필요한 리소스 파일들을 의미합니다. 예로 아이콘, 이미지, JSON 파일, 폰트 파일 등이 있다.
- 이러한 리소스는 앱 빌드 시 내부에 포함되어야 하며, 이를 위해 pubspec.yaml 파일에 등록이 필요하다.
Text 위젯에서 로컬 폰트(fontFamily) 설정하기
Flutter에서 Text 위젯의 fontFamily를 로컬 폰트로 설정하려면, asset으로 폰트를 등록한 후 사용해야 한다.
폰트 다운로드
fonts:
- family: Schyler
fonts:
- asset: assets/fonts/Sunflower-Bold.ttf
- asset: assets/fonts/Sunflower-Light.ttf
- asset: assets/fonts/Sunflower-Medium.ttf
# - family: Trajan Pro
# fonts:
# - asset: assets/fonts/TrajanPro-Regular.ttf
# - asset: assets/fonts/TrajanPro-Bold.ttf
# - asset: assets/fonts/TrajanPro-Italic.ttf
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/
# - images/a.png 하나씩 지정시 사용
전체코드
name: class_v02
description: "A new Flutter project."
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
environment:
sdk: '>=3.4.4 <4.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.6
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^3.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/
# - images/a.png 하나씩 지정시 사용
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
fonts:
- family: Sunflower
fonts:
- asset: assets/fonts/Sunflower-Bold.ttf
- asset: assets/fonts/Sunflower-Light.ttf
- asset: assets/fonts/Sunflower-Medium.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
Google Fonts의 Material Icons 사용 방법
1. 프로젝트 설정
Google Fonts의 Material Icons는 Flutter에 기본적으로 내장되어 있습니다. 별도의 추가 설정 없이 바로 사용할 수 있다.
2. 아이콘 찾기
Google Fonts Icons 사이트에서 원하는 아이콘을 검색하고 아이콘 이름을 확인하세요. 예를 들어, home, favorite, settings 등의 이름을 얻을 수 있다.
3. 사용 예제
Flutter에서 Icon 위젯을 활용하여 아이콘을 사용할 수 있다.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Google Fonts Icons Example'),
),
body: Center(
child: Icon(
Icons.star_half, // 사용할 아이콘 이름
size: 48.0,
color: Colors.blue,
),
),
),
);
}
}
4. 다른 아이콘 스타일 사용 (Outlined, Rounded 등)
Google Fonts Icons는 다양한 스타일(예: Outlined, Rounded, Sharp 등)을 제공한다. Flutter에서는 Icons 클래스에서 여러 스타일의 아이콘을 제공한다.
예를 들어
- Icons.home_outlined – 윤곽선 스타일 아이콘
- Icons.home_rounded – 둥근 스타일 아이콘
- Icons.home_sharp – 직선 스타일 아이콘
MaterialApp color theme와 Material 3 색상
- useMaterial3를 사용하면, Material 라이브러리의 최신 버전 사용 가능 (주류 대중성 가능성이 있으므로 참고하라고 함)
- useMaterial3와 seedColor, Theme.of(context).colorScheme를 사용하면, Material 3 디자인 가이드에 따른 색상값을 사용할 수 있음
- 주요 Theme.of(context).colorScheme
- primary/primaryContainer: 강조 요소
- secondary/secondaryContainer: 보조 요소
- tertiary/tertiaryContainer: 세부 요소
- colorScheme.primary와 colorScheme.primaryContainer 차이: primary는 강조 색상, primaryContainer는 강조 색상의 배경 색상
- 주요 Theme.of(context).colorScheme
import 'package:class_v02/main.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'MyApp3',
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange),
),
home: MyHomePage(),
));
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('My App 3', style: TextStyle(color: Theme.of(context).colorScheme.primary),),
),
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
body: Column(
children: [
Container(
color: Theme.of(context).colorScheme.primaryContainer,
width: 50,
height: 50,
),
Container(
color: Theme.of(context).colorScheme.secondary,
width: 50,
height: 50,
),
Container(
color: Theme.of(context).colorScheme.secondaryContainer,
width: 50,
height: 50,
),
Container(
color: Theme.of(context).colorScheme.tertiary,
width: 50,
height: 50,
),
Container(
color: Theme.of(context).colorScheme.tertiaryContainer,
width: 50,
height: 50,
),
],
),
),
);
}
}
TextField 사용법과 주요 property
- decoration: InputDecoration()을 사용해서, TextField 틀 장식
- InputDecoration() 주요 property
- focusedBorder: TextField가 선택되었을 때의 테두리 설정
- enabledBorder: TextField가 활성화가 되었을 때의 테두리 설정
- OutlineInputBorder(): 상하좌우 테두리
- UnderlineInputBorder(): 하단 테두리
- InputBorder.none: 테두리 없음
- labelText: TextField 선택에 상관없이 텍스트를 표시하는데 사용
- labelStyle로 레이블 텍스트 스타일 설정 가능 (예, labelStyle: TextStyle(color: Colors.amber))
- hintText: TextField 내부에 힌트 텍스트로 표시하는데 사용
- hintStyle로 힌트 텍스트 스타일 설정 가능 (예, hintStyle: TextStyle(color: Colors.amber))
- icon: 아이콘을 TextField에 직접 추가하는 데 사용 (예, icon: Icon(Icons.settings))
- prefixIcon: TextField 앞에 표시 아이콘, suffixIcon: TextField 뒤에 표시 아이콘
- keyboardType: TextField 입력 키보드 타입 설정
- TextInputType
- textInputAction: 키보드의 엔터키를 다음, 검색과 같은 표시로 변경 가능
- TextInputAction
- obscureText: true로 설정하면 입력한 문자가 보이지 않게할 수 있음 (예: 비밀번호)
- controller: TextField의 입력된 문자열을 가져올 수 있는 속성
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({super.key});
// TextEditingController로 TextField의 controller에 넣을 객체를 선언
final TextEditingController _emailController = TextEditingController();
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(30.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(),
const SizedBox(height: 30),
TextField(
decoration: InputDecoration(
labelText: '',
hintText: 'Enter your email1123123',
labelStyle: TextStyle(color: Colors.amber),
hintStyle: TextStyle(color: Colors.brown),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
borderSide: BorderSide(width: 1, color: Colors.black),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(width: 5, color: Colors.blue),
),
icon: Icon(Icons.settings),
prefixIcon: Icon(Icons.home),
suffixIcon: Icon(Icons.clear),
),
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.search,
obscureText: false,
// TextEditingController 객체를 controller에 설정
controller: _emailController,
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
// TextEditingController 객체의 text 속성으로 TextField의 입력된
// 문자열을 가져올 수 있음
print(_emailController.text);
},
child: Text('Click'),
),
],
),
),
),
),
);
}
}
풀이
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); // MyApp의 생성자
@override
Widget build(BuildContext context) {
// 위젯 트리를 빌드하고 반환하는 메서드
return MaterialApp(
debugShowCheckedModeBanner: false, // 디버그 배너 제거
theme: ThemeData(
useMaterial3: true, // 머티리얼 디자인 3 사용
colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange), // 시드 색상으로 색상 테마 생성
),
home: const LoginPage(), // 홈 화면으로 LoginPage 위젯을 사용
);
}
}
class LoginPage extends StatelessWidget {
// 로그인 페이지를 위한 StatelessWidget
const LoginPage({Key? key}) : super(key: key); // LoginPage의 생성자
@override
Widget build(BuildContext context) {
// 로그인 페이지의 위젯 트리를 빌드하고 반환
return Scaffold(
appBar: AppBar(
// 머티리얼 3 스타일의 앱바 추가
title: const Text('로그인'), // 앱바 제목 설정
),
body: Center(
// 내용물을 중앙에 배치
child: SingleChildScrollView(
// 화면이 작을 때 스크롤 가능하게 함
padding: const EdgeInsets.symmetric(horizontal: 24.0), // 좌우 패딩 설정
child: Column(
mainAxisAlignment: MainAxisAlignment.center, // 수직 중앙 정렬
children: [
const FlutterLogo(size: 100), // Flutter 로고 추가
const SizedBox(height: 48.0),
TextField(
// 이메일 입력 필드
decoration: InputDecoration(
labelText: '이메일',
prefixIcon: const Icon(Icons.email_outlined),
border: const OutlineInputBorder(),
filled: true, // 머티리얼 3의 채워진 입력 필드 스타일
fillColor: Theme.of(context).colorScheme.secondaryContainer,
),
),
const SizedBox(height: 16.0),
TextField(
// 비밀번호 입력 필드
obscureText: true, // 입력된 텍스트를 숨김 처리
decoration: InputDecoration(
labelText: '비밀번호',
prefixIcon: const Icon(Icons.lock_outline),
border: const OutlineInputBorder(),
filled: true,
fillColor: Theme.of(context).colorScheme.secondaryContainer,
),
),
const SizedBox(height: 24.0),
FilledButton(
// 머티리얼 3에서 새로 추가된 FilledButton 사용
onPressed: () {
// TODO: 로그인 로직 추가
},
child: const Text(
'로그인',
style: TextStyle(fontSize: 18.0),
),
),
const SizedBox(height: 12.0),
TextButton(
// 비밀번호 찾기 버튼
onPressed: () {
// TODO: 비밀번호 찾기 페이지로 이동
},
child: const Text(
'비밀번호를 잊으셨나요?',
style: TextStyle(fontSize: 16.0),
),
),
const SizedBox(height: 16.0),
Row(
// 회원가입 안내 문구와 버튼
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('계정이 없으신가요?'),
TextButton(
onPressed: () {
// TODO: 회원가입 페이지로 이동
},
child: const Text(
'회원가입',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.bold,
),
),
),
],
),
const SizedBox(height: 24.0),
const Divider(), // 구분선 추가
const SizedBox(height: 24.0),
ElevatedButton.icon(
// 머티리얼 3 스타일의 ElevatedButton.icon 사용
onPressed: () {
// TODO: 소셜 로그인 로직 추가
},
icon: const Icon(Icons.g_mobiledata),
label: const Text('Google로 로그인'),
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
foregroundColor: Theme.of(context).colorScheme.onPrimaryContainer,
),
),
],
),
),
),
);
}
}
머터리얼 디자인 3는 구글이 최신 디자인 트렌드와 사용자 경험을 반영하기 위해 도입한 새로운 디자인 시스템이다.
ThemeData에서 colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange)와 같이 시드 색상을 활용하면 다음과 같은 이점.
- 자동 색상 팔레트 생성: 시드 색상 하나만 지정하면 머터리얼 디자인 가이드라인에 따라 다양한 톤과 명도의 색상 팔레트가 자동으로 생성된다. 이를 통해 디자인의 일관성을 유지하면서도 손쉽게 테마를 설정할 수 있다.
- 유연한 테마 변경: 시드 색상만 변경하면 전체 앱의 색상 테마가 변경되므로, 다양한 테마를 쉽게 적용하거나 A/B 테스트를 수행할 수 있다.
- 접근성 보장: 자동으로 생성된 색상 팔레트는 명도 대비 등 접근성 기준을 충족하도록 설계되어, 추가적인 조정 없이도 접근성이 높은 디자인을 구현할 수 있다.
- 시간과 비용 절감: 수동으로 각 색상을 지정할 필요 없이, 시드 색상 하나로 전체 팔레트를 관리할 수 있어 개발 시간과 디자인 리소스를 절약할 수 있다.
StatefulWidget과 StatelessWidget 알아 보기
- StatelessWidget: 상태를 관리하지 않는 정적 위젯
- StatefulWidget: 상태를 관리하는 동적 위젯
StatefulWidget과 StatelessWidget 코드 작성의 차이
- StatelessWidget은 상속받은 위젯은 build() 함수를 재정의하여 위젯을 생성함
- Android Studio에서 stless라고 친 후, 텝키를 누르면 자동 템플릿 생성
- StatelessWidget: 상태를 관리하지 않는 정적 위젯
- StatefulWidget: 상태를 관리하는 동적 위젯
StatefulWidget과 StatelessWidget 코드 작성의 차이
- StatelessWidget은 상속받은 위젯은 build() 함수를 재정의하여 위젯을 생성함
- Android Studio에서 stless라고 친 후, 텝키를 누르면 자동 템플릿 생성
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
// 여기에 위젯 설정 추가
);
}
}
- StatefulWidget은 상속받는 위젯이 createState 메서드로 State 객체를 리턴하고,
- State는 상속받는 객체가 build 메서드로 Widget을 리턴하는 형태
- Android Studio에서 stf라고 친 후, 텝키를 누르면 자동 템플릿 생성
State 객체 이름 앞에 자동으로 언더바(_)를 붙이는데, dart에서 클래스나 프로퍼티, 메서드 앞에 언더바를 붙이면 private를 의미
private로 선언된 메서드/속성은 클래스의 경우 해당 파일에서만, 프로퍼티와 메서드는 해당 클래스에서만 접근할 수 있음!
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
} // end of MyApp
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}
setState 메서드
- StatefulWidget에게 상태가 변경되었다고 알리는 메서드
- 내부적으로 build 메서드를 다시 호출하여 화면 상태 업데이트
- 비동기 코드 실행할 수 없기 때문에 setState 실행 전 모든 비동기 코드를 완료해야 함
- 아래 코드에서는 버튼을 클릭하면 숫자가 올라가고, setState()가 이를 Flutter 프레임워크에 알려주면, build() 메서드를 재실행
- 변경된 _counter 값이 반영되어 화면에 표시됨
import 'package:flutter/material.dart';
void main() {
runApp(MyApp6());
}
class MyApp6 extends StatefulWidget {
const MyApp6({super.key});
@override
State<MyApp6> createState() => _MyApp6State();
} // end of MyApp6
// _ private 을 의미한다. --> dart public, private 만 사용
class _MyApp6State extends State<MyApp6> {
// 멤버 변수
int _count = 0;
int get count => _count; // private 변수
@override
void initState() {
super.initState();
// 객체가 메모리에 올라 올때 단 한번만 수행 시키는 메서드
print('initState() 메서드 호출');
}
// 멤버 메서드
@override
Widget build(BuildContext context) {
// 지역 변수
print('build() 메서드 호출');
return MaterialApp(
home: Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
setState(() {
_count++;
print("_count : $count");
});
},
child: Text('$_count'),
),
),
),
);
}
}
Checkbox, Radio, Slider, Switch
- 체크박스: 사용자에게 true 또는 false를 입력받는 기본 위젯
Checkbox(
value: _checkBoxValue,
onChanged: (value) {
setState(() {
_checkBoxValue = value!;
});
},
);
- 라디오: 라디오 버튼 인터페이스로 사용자에게 여러 항목 중 하나만 선택할 수 있도록 하는 위젯
Radio(
value: 'Option 1',
groupValue: _radioValue,
onChanged: (value) {
setState(() {
_radioValue = value.toString();
});
},
);
- Slider: 음량 조정 등에서 사용하는 막대를 밀어서 숫자값을 입력받는 위젯
Slider(
value: _sliderValue,
min: 0,
max: 100,
onChanged: (value) {
setState(() {
_sliderValue = value;
});
},
);
- Switch: 사용자에게 true 또는 false를 입력받을 수 있는 스위치 위젯
Switch(
value: _switchValue,
onChanged: (value) {
setState(() {
_switchValue = value;
});
},
);
위 위젯을 사용하여 만들기
import 'package:flutter/material.dart';
void main() {
runApp(MyApp7());
}
class MyApp7 extends StatefulWidget {
const MyApp7({super.key});
@override
State<MyApp7> createState() => _MyApp7State();
}
class _MyApp7State extends State<MyApp7> {
// null 값이 들어와도 된다는 의미
// bool 데이터 타입과 bool? 타입은 다른 것이다.
bool? _checkBoxValue = true;
String _radioValue = 'Option 1';
double _slideValue = 0.0;
bool _switchValue = true;
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(title: Text('MyApp')),
body: Container(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Center(
child: Text('$_checkBoxValue'),
),
const SizedBox(height: 16.0),
Checkbox(
value: _checkBoxValue,
onChanged: (value) {
setState(() {
_checkBoxValue = value;
});
}),
const SizedBox(height: 16.0),
Text('Radio Button'),
Row(
children: [
Radio(
value: "축구",
groupValue: _radioValue,
onChanged: (value) {
setState(() {
print("value : $value");
//print("_radioValue: $_radioValue");
_radioValue = value.toString();
});
},
),
Text('축구'),
Radio(
value: "수영",
groupValue: _radioValue,
onChanged: (value) {
setState(() {
print("value : $value");
//print("_radioValue: $_radioValue");
_radioValue = value.toString();
});
},
),
Text('수영'),
// Slider 위젯 사용
Slider(
min: 0,
max: 10,
value: _slideValue,
onChanged: (value) {
print(value);
setState(() {
print("value: $value");
_slideValue = value.toDouble();
});
}),
Switch(
value: _switchValue,
onChanged: (value){
setState(() {
_switchValue = value;
});
}
)
],
)
],
),
),
),
);
}
}
'Flutter' 카테고리의 다른 글
플러터 기본기 다지기 - 3 ( ListView 사용법과 주요 property) (0) | 2024.11.14 |
---|---|
플러터 기본기 다지기 - 3 (Form 위젯) (0) | 2024.11.13 |
SingleChildScrollView 위젯 (0) | 2024.11.07 |
기초적인 Flutter 화면을 구성하는 패턴 (1) | 2024.11.06 |
플러터 기본기 다지기 - 1 (1) | 2024.11.05 |