2023. 9. 2. 14:42ㆍ모바일어플개발/Flutter
안녕하세요~ totally 개발자입니다.
Material 3 Design
보통 플러터 앱 개발을 하실 때 로직 부분을 개발하는데에도 시간이 많이 소요되지만 디자인적인 측면에서도 시간이 많이 소요되는 것을 느끼실 것입니다. 그 때 구글의 머터리얼 (Material) 3의 디자인을 적용하면 디자인적으로 통일성과 자연스러움을 간단하게 적용할 수 있습니다. 사용을 위해서는 당연히 플러터는 업그레이드가 되어 있어야 합니다.
상세한 내용을 아래를 통해 가이드를 확인할 수 있습니다.
Material Design
Build beautiful, usable products faster. Material Design is an adaptable system—backed by open-source code—that helps teams build high quality digital experiences.
m3.material.io
간단한 실습을 통해 Material 3 디자인을 적용해보도록 하겠습니다.
Step 1: 디자인 적용에 앞서 카드 리스트가 구현된 페이지를 만들어보겠습니다. 먼저 클래스 내에 다음처럼 간단한 String 리스트를 선언합니다. (전체 코드는 맨 아래에 첨부하였습니다)

Step 2: 다음처럼 Scaffold 뼈대를 만들고 appBar, body, floatingActionButton 부분을 구성하고 body에는 ListView.builder를 통해서 리스트를 보여주도록 하겠습니다.

Step 3: cardContainer에 들어갈 위젯을 만들어줍니다. 참고로 추후에 디자인 적용을 위해 색상은 넣지 않습니다. FilledButton를 사용하면 버튼 배경에 색상을 적용할 수 있으며 tonal를 사용하시면 FilledButton과 OutlinedButton의 중간단계처럼 활용할 수 있습니다. 즉 색이 옅어집니다.

Step 4: FloatingActionButton(+ 버튼)을 클릭했을 때 팝업창(Dialog)을 만들어줍니다.

Step 5: 실행해서 테스틀해봅니다.

여기서 확인해보시면 아시겠지만 기본적으로 적용되는 색상으로만 앱 디자인을 마무리하기에는 한계가 있습니다. 그래서 여기에 직접 Material 3 디자인을 적용하고 비교해보도록 하겠습니다.
먼저 아래 웹사이트를 통해서 어떤 색상이 있고 어떻게 보여지는지 알 수 있습니다.
https://flutter.github.io/samples/web/material_3_demo/
material_3_demo
flutter.github.io
기본적으로 indigo, blue, teal, green, yellow, orange, deepOrange, pink 중에 선택해서 사용하시면 됩니다.
Step 6: MaterialApp쪽에서 15-17번째 줄처럼 ThemeData를 넣어주고 useMaterial3을 true로 변경해주시면 되며 colorScheme.fromSeed를 넣어주면 됩니다.

Step 7: 저장하고 다시 확인해보시면 아래와 같습니다.
왼쪽이 적용전, 오른쪽이 적용 후 인데 직접 보셔서 비교해보시면 아시겠지만 확실히 다른 것을 알 수 있습니다.
![]() |
![]() |
![]() |
![]() |
몇 가지 추가적으로 색상을 적용해본 모습입니다.
green | yellow | orange | pink |
![]() |
![]() |
![]() |
![]() |
어려운 세팅 없이, 앱이 깔끔하고 고급스러워진 것을 알 수 있습니다. 특히 소규모로 개발하시는 개발자 분들은 디자인 측면에서 시간을 많이 단축할 수 있을 것으로 생각합니다.
[전체 소스 코드]
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(const MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
const MyApp({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter App', | |
home: const MainPage(), | |
theme: ThemeData( | |
useMaterial3: true, | |
colorScheme: ColorScheme.fromSeed(seedColor: Colors.pink), | |
), | |
); | |
} | |
} | |
class MainPage extends StatefulWidget { | |
const MainPage({Key? key}) : super(key: key); | |
@override | |
State<MainPage> createState() => _MainPageState(); | |
} | |
class _MainPageState extends State<MainPage> { | |
List<String> friendList = [ | |
"Andrew", | |
"Brian", | |
"Catherine", | |
"Wilson", | |
"Raul", | |
"Daniel", | |
"John", | |
]; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: const Text("Material 3 Design"), | |
centerTitle: true, | |
), | |
body: ListView.builder( | |
itemCount: friendList.length, | |
itemBuilder: (context, index) { | |
return cardContainer(name: friendList[index], number: index + 1); | |
}), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () { | |
showFloatingDialog(); | |
}, | |
child: const Icon(Icons.add), | |
), | |
); | |
} | |
Widget cardContainer({String name = "", int number = 0}) { | |
return Card( | |
margin: const EdgeInsets.all(10), | |
child: ListTile( | |
title: Container( | |
width: MediaQuery.of(context).size.width, | |
padding: const EdgeInsets.all(12), | |
child: Text("$number. $name", style: const TextStyle(fontSize: 20)), | |
), | |
subtitle: Row( | |
children: [ | |
TextButton( | |
onPressed: () {}, | |
child: const Text("Details"), | |
), | |
FilledButton.tonal( | |
onPressed: () {}, | |
child: const Text("Follow"), | |
), | |
], | |
), | |
), | |
); | |
} | |
void showFloatingDialog() { | |
showDialog( | |
context: context, | |
barrierDismissible: true, | |
builder: (context) { | |
return AlertDialog( | |
content: const Column( | |
mainAxisSize: MainAxisSize.min, | |
children: [ | |
Center(child: Text("Do you want to add a friend?")), | |
], | |
), | |
actions: [ | |
TextButton( | |
onPressed: () => Navigator.of(context).pop(), | |
child: const Text("No"), | |
), | |
FilledButton(onPressed: () {}, child: const Text("Yes")), | |
], | |
); | |
}, | |
); | |
} | |
} |
[유튜브 강좌 영상]