GoToLearn 4주차
GoToLearn 4주차 CodeLab과제인 MDC-103 Flutter:Material Theming with Color, Shape, Elevation, and Type을 진행해보겠습니다. 코드랩 제목을 보니 머터리얼 테마 관련 설정을 배워보는 시간인 것 같습니다. 그럼 시작해보도록 하겠습니다.
MDC-103 Flutter:Material Theming with Color, Shape, Elevation, and Type
https://codelabs.developers.google.com/codelabs/mdc-103-flutter#0
MDC-103 Flutter: 색상, 형태, 고도, 활자를 사용한 Material Theming | Google Codelabs
Flutter의 머티리얼 구성요소를 사용해 디자인으로 얼마나 쉽게 제품을 차별화하고 브랜드를 표현할 수 있는지 알아보세요.
codelabs.developers.google.com
소개
이번 코드랩에서 빌드할 항목
이번 코드랩은 MDC-102에 이어 Shirne 전자상거래 앱을 Color, Typography, Elevation, Shape, Layout을 사용해서 커스텀해봅니다. 아래 사진은 완성 시 나타나는 화면입니다.
사용하는 머터리얼 컴포넌트 및 하위 위젯
- Theme - 테마
- Typography - 서체
- Elevation - 고도
- Image list - 이미지 리스트
Flutter 환경 설정 및 Starter App
이번주도 환경설정은 패스하고, 지난 주에 진행했던 MDC-102에 이어 진행하도록 하겠습니다.
색상 변경
Colors.dart 파일 추가
Shirne 앱에서 사용할 Color를 정의할 colors.dart 파일을 생성하고 각 색상을 입력합니다.
import 'package:flutter/material.dart';
const kShrinePink50 = Color(0xFFFEEAE6);
const kShrinePink100 = Color(0xFFFEDBD0);
const kShrinePink300 = Color(0xFFFBB8AC);
const kShrinePink400 = Color(0xFFEAA4A4);
const kShrineBrown900 = Color(0xFF442B2D);
const kShrineErrorRed = Color(0xFFC5032B);
const kShrineSurfaceWhite = Color(0xFFFFFBFA);
const kShrineBackgroundWhite = Colors.white;
맞춤 색상 팔레트
아래 색상 테마는 디자이너가 만든 샘플이며 풍부한 팔레트를 위해 확장되었다고합니다. Shirne 앱의 색상은 2014 머터리얼 색상의 팔레트 색깔이 아니라고 하는데 어떤 의미인지 찾아보니 사이트(https://m2.material.io/design/color/the-color-system.html#tools-for-picking-colors) 가장 아래에 2014 머터리얼 디자인 컬러 팔레트가 나와있는것을 확인할 수 있었습니다. 해당 색에 일치하지 않는다는 의미인것 같습니다.
그리고 색상 기준으로 50, 100, 200, 300, 400... 900까지 Shade를 줄 수 있고 값이 올라 갈 수록 진해지는것을 확인할 수 있습니다. Shrine앱은 Pink 색상 기준 50, 100, 900, Brown 색상 기준 900을 사용한다고 하네요.
해당 색상들은 themeData 위젯으로 MaterialApp의 필드로 넣어주면 사용할 수 있습니다.
ThemeData.light() 설정
밝은 테마는 Flutter에 내장되어 있는 테마들 중 하나입니다. copyWith를 통해 밝은 테마의 값을 변경하여 사용하도록 해보겠습니다.
class ShrineApp extends StatelessWidget {
const ShrineApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Shrine',
initialRoute: '/login',
routes: {
'/login': (BuildContext context) => const LoginPage(),
'/': (BuildContext context) => const HomePage(),
},
// 변경된 테마 적용
theme: _kShrineTheme,
);
}
}
final ThemeData _kShrineTheme = _buildShrineTheme();
ThemeData _buildShrineTheme() {
final ThemeData base = ThemeData.light();
// copyWith를 사용해서 아래 정의된 값 이외에는 변경하지 않고 그대로 사용
return base.copyWith(
colorScheme: base.colorScheme.copyWith(
primary: kShrinePink100,
onPrimary: kShrineBrown900,
secondary: kShrineBrown900,
error: kShrineErrorRed,
),
);
}
위 코드랩까지 진행했을때 코드랩에 표시되는 결과와 색깔이 동일하지 않은데 뒤에서 맞추도록 해보고 일단은 진행하겠습니다.
서체 및 라벨 스타일 설정
사용자 정의 테마 설정
Rubik 글꼴을 추가하기 위해 pubspec.yaml 파일에 아래 코드를 추가 시킵니다. pub get을 시켜줍니다.
fonts:
- family: Rubik
fonts:
- asset: fonts/Rubik-Regular.ttf
- asset: fonts/Rubik-Medium.ttf
weight: 500
이후 login.dart 에서 Shrine 텍스트에 Text 테마를 추가 시켜 줍니다.
healine5는 더이상 사용하지 않아 headlineSmall로 대체하겠습니다.
Column(
children: <Widget>[
Image.asset('assets/diamond.png'),
const SizedBox(height: 16.0),
// style 추가
Text('SHRINE', style: Theme.of(context).textTheme.headlineSmall,),
],
),
TextTheme을 반환하는 함수를 만들어 줍니다. deprecated 된 값들이 많아 변경해주도록 하겠습니다.
TextTheme.apply 함수를 통해 텍스트 테마의 특정 속성을 변경할 수 있는 방법을 사용합니다.
TextTheme _buildShrineTextTheme(TextTheme base) {
return base.copyWith(
// headline5
headlineSmall: base.headlineSmall!.copyWith(
fontWeight: FontWeight.w500,
),
// headline6
titleLarge: base.titleLarge!.copyWith(
fontSize: 18.0,
),
// caption
bodySmall: base.bodySmall!.copyWith(
fontWeight: FontWeight.w400,
fontSize: 14.0,
),
// bodyText1
bodyLarge: base.bodyLarge!.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16.0,
),
).apply(
fontFamily: 'Rubik',
displayColor: kShrineBrown900,
bodyColor: kShrineBrown900,
);
}
새로운 텍스트 테마 사용
새로운 텍스트 테마를 사용하기 위해 _buildShrineTheme() 함수에 테마에 대한 값을 넣어주도록 합니다.
ThemeData _buildShrineTheme() {
final ThemeData base = ThemeData.light();
return base.copyWith(
colorScheme: base.colorScheme.copyWith(
primary: kShrinePink100,
onPrimary: kShrineBrown900,
secondary: kShrineBrown900,
error: kShrineErrorRed,
),
// 텍스트 테마 추가
textTheme: _buildShrineTextTheme(base.textTheme),
textSelectionTheme: const TextSelectionThemeData(
selectionColor: kShrinePink100,
),
);
}
텍스트 축소 및 라벨 아래로 위치
라벨이 너무 커서 텍스트 크기를 줄여주고, 라벨이 아래에 위치하도록 수정해줍니다.
Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
product.name,
style: theme.textTheme.titleLarge,
softWrap: true,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
const SizedBox(height: 4.0),
Text(
formatter.format(product.price),
style: theme.textTheme.bodySmall,
),
],
),
텍스트 필드 테마 지정
InputDecorationTheme을 활용하여 텍스트 필드의 테마를 지정해주고, 기존에 있던 텍스트 필드의 fill 효과를 제거해줍니다.
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
),
TextField(
controller: _usernameController,
decoration: const InputDecoration(
labelText: 'Username',
),
),
const SizedBox(height: 12.0),
TextField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: 'Password',
),
obscureText: true,
),
텍스트 필드에 텍스트를 입력 시 테투디와 플로팅 라벨이 기본색으로 렌더링되어 쉽게 눈에 띄지 않습니다. 이를 변경해보겠습니다.
ThemeData _buildShrineTheme() {
final ThemeData base = ThemeData.light();
return base.copyWith(
colorScheme: base.colorScheme.copyWith(
primary: kShrinePink100,
onPrimary: kShrineBrown900,
secondary: kShrineBrown900,
error: kShrineErrorRed,
),
textTheme: _buildShrineTextTheme(base.textTheme),
textSelectionTheme: const TextSelectionThemeData(
selectionColor: kShrinePink100,
),
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
// 추가
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
// 추가
floatingLabelStyle: TextStyle(
color: kShrineBrown900,
),
),
);
}
또한 취소 버튼도 대비가 잘 되도록 변경을 진행합니다.
TextButton(
onPressed: () {
_usernameController.clear();
_passwordController.clear();
},
child: const Text('CANCEL'),
style: TextButton.styleFrom(
foregroundColor: Theme.of(context).colorScheme.secondary,
),
),
고도 조정
Next 버튼 고도 변경
ElevatedButton의 기본 고도는 2.0입니다. 8.0으로 변경을 진행합니다.
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('NEXT'),
style: TextButton.styleFrom(elevation: 8.0),
);
카드의 고도 변경
카드의 고도는 0으로 변경을 진행합니다.
elevation: 0.0,
Shape 추가
Shirne 앱은 8각 또는 직사각형 형태의 기하학 스타일로 되어있다고합니다. 그 모양을 추가해보도록 하겠습니다.
텍스트 필드의 모양 변경
const InputDecorationTheme(
// CutCornerBorder 추가
border: CutCornersBorder(),
focusedBorder: CutCornersBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
floatingLabelStyle: TextStyle(
color: kShrineBrown900,
),
),
취소, 다음 버튼에 모양 추가
TextButton(
onPressed: () {
_usernameController.clear();
_passwordController.clear();
},
child: const Text('CANCEL'),
style: TextButton.styleFrom(
foregroundColor: Theme.of(context).colorScheme.secondary,
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0)),
)),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('NEXT'),
style: ElevatedButton.styleFrom(
foregroundColor: kShrineBrown900,
backgroundColor: kShrinePink100,
elevation: 8.0,
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0)),
),
),
),
레이아웃 변경
GridView를 AsymmetricView로 대체
코드랩에서 제공되는 AsymmetriceView를 사용하여 기존 GridView를 대체합니다.
body: AsymmetricView(
products: ProductsRepository.loadProducts(Category.all),
),
다른 테마 사용해보기
색상 수정
색상을 보라색으로 변경한 뒤 테마 데이터만 변경하여 Shrine 앱이 어떻게 변경되는지 확인합니다.
const kShrinePurple = Color(0xFF5D1049);
ThemeData _buildShrineTheme() {
final ThemeData base = ThemeData.light();
return base.copyWith(
colorScheme: base.colorScheme.copyWith(
primary: kShrinePurple,
secondary: kShrinePurple,
error: kShrineErrorRed,
),
scaffoldBackgroundColor: kShrineSurfaceWhite,
textSelectionTheme: const TextSelectionThemeData(
selectionColor: kShrinePurple,
),
inputDecorationTheme: const InputDecorationTheme(
border: CutCornersBorder(),
focusedBorder: CutCornersBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrinePurple,
),
),
floatingLabelStyle: TextStyle(
color: kShrinePurple,
),
),
);
}
수정 진행
코드랩의 완성 결과물이 코드랩에 있는 이미지와 다름을 확인하여 수정을 진행합니다.
AppBarTheme 지정
코드랩을 진행하다보니 Appbar의 색상이 들어가지 않는것을 확인했습니다.
appbarTheme를 추가하여 appbar 색상을 추가합니다.
appBarTheme: const AppBarTheme(
color: kShrinePink100,
),
완성
코드랩을 완성화면 아래와 같은 결과물을 얻을 수 있습니다. 확실히 이뻐진 UI를 확인할 수 있네요.
후기
이번 코드랩에서는 머터리얼 디자인에서 Theme를 활용해서 화면을 꾸미는 방법을 배웠던 것 같습니다. 평소에 많이 보긴했지만 어떤식으로 사용하는지 어떻게 사용하는지 확인할 수 있는 계기였던 것 같습니다. 재밌네요 !
실습코드
flutter_codelabs/material-components-flutter-codelabs-101-starter at main · wonyong-park/flutter_codelabs
flutter_codelabs_repo. Contribute to wonyong-park/flutter_codelabs development by creating an account on GitHub.
github.com
'개발 > Study' 카테고리의 다른 글
[GoToLearn] Flutter Riverpod을 사용한 CounterApp 만들기 (1) | 2024.06.13 |
---|---|
[GoToLearn] Flutter CodeLab - MDC-104 Flutter:Material Advanced Components (0) | 2024.06.04 |
[GoToLearn] Flutter CodeLab - MDC-102 Flutter:Material Structure and Layout (0) | 2024.05.21 |
[GoToLearn] Flutter CodeLab - MDC-101 Flutter:Material Components Basics (0) | 2024.05.13 |
[GoToLearn] Flutter CodeLab - First Flutter App (0) | 2024.05.09 |