import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: 1,
length: 3,
child: Scaffold(
body: Stack(children: [
TabBarView(
children: [
First(),
Second(),
Third(),
],
),
SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 16),
child: Column(
children: [
Container(
alignment: Alignment.topCenter,
child: TabPage(),
)
],
),
))
]),
));
}
}
class TabPage extends StatefulWidget {
const TabPage({super.key});
@override
State<TabPage> createState() => _TabPageState();
}
class _TabPageState extends State<TabPage> {
@override
Widget build(BuildContext context) {
return TabPageSelector(
color: DefaultTabController.of(context)!.index == 1
? Colors.blue[300]
: Colors.grey[400],
selectedColor: DefaultTabController.of(context)!.index == 1
? Colors.white
: Colors.blue,
indicatorSize: 8,
);
}
}
class First extends StatelessWidget {
const First({super.key});
@override
Widget build(BuildContext context) {
const songs = [
{
'imageUrl': 'https://i.ibb.co/MRSqtP8/autumnnight.jpg',
'title': '가을밤에 든 생각',
'artist': '잔나비',
},
{
'imageUrl': 'https://i.ibb.co/MRSqtP8/autumnnight.jpg',
'title': '가을밤에 든 생각',
'artist': '잔나비',
},
{
'imageUrl': 'https://i.ibb.co/MRSqtP8/autumnnight.jpg',
'title': '가을밤에 든 생각',
'artist': '잔나비',
},
{
'imageUrl': 'https://i.ibb.co/MRSqtP8/autumnnight.jpg',
'title': '가을밤에 든 생각',
'artist': '잔나비',
},
{
'imageUrl': 'https://i.ibb.co/MRSqtP8/autumnnight.jpg',
'title': '가을밤에 든 생각',
'artist': '잔나비',
},
{
'imageUrl': 'https://i.ibb.co/MRSqtP8/autumnnight.jpg',
'title': '가을밤에 든 생각',
'artist': '잔나비',
},
];
return SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Icon(Icons.settings),
),
Text(
"라이브러리",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Icon(null),
],
),
SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
children: [
ImageIcon(
NetworkImage("https://i.ibb.co/hxNbZ8p/shazam.png"),
size: 18,
),
SizedBox(
width: 12,
),
Text(
"Shazam",
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
)
],
),
),
Divider(
height: 1,
color: Colors.grey,
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
Icon(
Icons.person,
size: 18,
),
SizedBox(
width: 12,
),
Text(
"아티스트",
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
)
],
)),
Divider(
height: 1,
color: Colors.grey,
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
Icon(
Icons.music_note,
size: 18,
),
SizedBox(
width: 12,
),
Text(
"회원님을 위한 재생목록",
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
)
],
)),
Divider(
height: 1,
color: Colors.grey,
),
SizedBox(
height: 16,
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
Text(
"최근 Shazam",
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
)
],
)),
SizedBox(
height: 8,
),
Expanded(
child: GridView.builder(
itemCount: songs.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, childAspectRatio: 3 / 5),
itemBuilder: ((context, index) {
String img = songs[index]["imageUrl"]!;
String artist = songs[index]["artist"]!;
String title = songs[index]["title"]!;
return Container(
margin: EdgeInsets.all(4),
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(8)),
),
child: Column(children: [
Image.network(
img,
fit: BoxFit.cover,
height: 180,
),
Expanded(
child: Container(
padding: const EdgeInsets.all(8),
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold),
),
Text(
artist,
style: TextStyle(
fontSize: 12, color: Colors.grey),
),
Spacer(),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0),
child: Image.network(
"https://i.ibb.co/KG9m5QS/applemusic.png",
width: 60,
),
),
],
),
))
]),
);
})))
],
)));
}
}
class Second extends StatelessWidget {
const Second({super.key});
@override
Widget build(BuildContext context) {
double pheight = MediaQuery.of(context).size.height;
double pwidth = MediaQuery.of(context).size.width;
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue[300]!, Colors.blue[900]!],
begin: Alignment.topCenter,
end: Alignment.bottomCenter)),
child: Column(children: [
SizedBox(
height: 20,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
children: [
Icon(
Icons.person,
color: Colors.white,
),
Text(
"라이브러리",
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold),
)
],
),
Spacer(),
Column(
children: [
Icon(
Icons.show_chart,
color: Colors.white,
),
Text(
"차트",
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold),
)
],
),
],
),
),
SizedBox(
height: pheight * 0.1,
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 30.0),
child: Text(
"Shazam하려면 탭하세요",
style: TextStyle(
color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold),
),
),
Container(
alignment: Alignment.center,
height: pheight * 0.3,
width: pheight * 0.3,
decoration:
BoxDecoration(color: Colors.blue[300], shape: BoxShape.circle),
child: Image.network(
"https://i.ibb.co/hxNbZ8p/shazam.png",
color: Colors.white,
height: pheight * 0.2,
width: pheight * 0.2,
),
),
SizedBox(
height: pheight * 0.1,
),
Container(
alignment: Alignment.center,
height: pwidth * 0.12,
width: pwidth * 0.12,
decoration:
BoxDecoration(color: Colors.blue[300], shape: BoxShape.circle),
child: Icon(
Icons.search,
color: Colors.white,
size: pwidth * 0.08,
),
)
]),
);
}
}
class Third extends StatelessWidget {
const Third({super.key});
@override
Widget build(BuildContext context) {
const chartData = {
'korea': [
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
],
'global': [
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
],
'newyork': [
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
{
'imageUrl': 'https://i.ibb.co/xf2HpfG/dynamite.jpg',
'name': 'Dynamite',
'artist': 'BTS',
},
],
};
double pheight = MediaQuery.of(context).size.height;
double pwidth = MediaQuery.of(context).size.width;
return SafeArea(
child: Column(
children: [
Text(
"차트",
style: TextStyle(
fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black),
),
SizedBox(
height: 16,
),
Expanded(
child: ListView(
children: [
Container(
width: double.infinity,
height: pheight * 0.3,
alignment: Alignment.center,
decoration: BoxDecoration(color: Colors.blue[900]),
child: Column(children: [
Spacer(),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(8)),
),
width: pwidth * 0.7,
height: pheight * 0.05,
alignment: Alignment.center,
child: Text(
"국가 및 도시별 차트",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.blue[900]),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"전 세계",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
color: Colors.white),
),
),
Spacer()
]),
),
Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text(
"대한민국 차트",
style: TextStyle(fontSize: 16),
),
Spacer(),
Text(
"모두 보기",
style: TextStyle(fontSize: 14, color: Colors.blue[300]),
),
],
),
),
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
chartData['korea']![0]["imageUrl"]!,
width: pwidth * 0.28,
),
Text(
"${chartData['korea']![0]["name"]!}\n${chartData['korea']![0]["artist"]!}")
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
chartData['korea']![1]["imageUrl"]!,
width: pwidth * 0.28,
),
Text(
"${chartData['korea']![1]["name"]!}\n${chartData['korea']![1]["artist"]!}")
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
chartData['korea']![2]["imageUrl"]!,
width: pwidth * 0.28,
),
Text(
"${chartData['korea']![2]["name"]!}\n${chartData['korea']![2]["artist"]!}")
],
),
),
],
)
],
),
Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text(
"글로벌 차트",
style: TextStyle(fontSize: 16),
),
Spacer(),
Text(
"모두 보기",
style: TextStyle(fontSize: 14, color: Colors.blue[300]),
),
],
),
),
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
chartData['global']![0]["imageUrl"]!,
width: pwidth * 0.28,
),
Text(
"${chartData['global']![0]["name"]!}\n${chartData['global']![0]["artist"]!}")
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
chartData['global']![1]["imageUrl"]!,
width: pwidth * 0.28,
),
Text(
"${chartData['global']![1]["name"]!}\n${chartData['global']![1]["artist"]!}")
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
chartData['global']![2]["imageUrl"]!,
width: pwidth * 0.28,
),
Text(
"${chartData['global']![2]["name"]!}\n${chartData['global']![2]["artist"]!}")
],
),
),
],
)
],
),
Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text(
"뉴욕 차트",
style: TextStyle(fontSize: 16),
),
Spacer(),
Text(
"모두 보기",
style: TextStyle(fontSize: 14, color: Colors.blue[300]),
),
],
),
),
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
chartData['newyork']![0]["imageUrl"]!,
width: pwidth * 0.28,
),
Text(
"${chartData['newyork']![0]["name"]!}\n${chartData['newyork']![0]["artist"]!}")
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
chartData['newyork']![1]["imageUrl"]!,
width: pwidth * 0.28,
),
Text(
"${chartData['newyork']![1]["name"]!}\n${chartData['newyork']![1]["artist"]!}")
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
chartData['newyork']![2]["imageUrl"]!,
width: pwidth * 0.28,
),
Text(
"${chartData['newyork']![2]["name"]!}\n${chartData['newyork']![2]["artist"]!}")
],
),
),
],
)
],
),
],
)),
],
));
}
}
1. DefaultTabController
페이지를 넘겨가며 볼 수 있게 해주는 위젯
2.SafeArea
휴대기기 기종이나 OS에 따라 필요한 텍스트가 가려지는 경우를 방지하기위해 자동으로 패딩을 주는 위젯.
[플러터] SafeArea 에 대해서
들어가며 이번 글에서는 Flutter(이하 플러터)에서 가장 기본이 되는 위젯 "SafeArea"에 대해 살펴보고 사용법에 대해서 알아보도록 하겠습니다. 그런 후에 적용 예시를 통해 어떤 옵션이 있는지 또
all-dev-kang.tistory.com
3.TabPageSelector
1의 DefaultTabController의 현재 페이지를 나타내는 indicator역할
4.Spacer()
남은 공간을 최대로 차지해서 밀어주는 위젯
5.Divider()
구분선을 그려주는 위젯
6.GridView.builder()
그리드 뷰 위젯. 리스트뷰와 사용법은 유사함
7.MediaQuery.of(context).size.height, MediaQuery.of(context).size.width
현재 기기 화면 사이즈를 구해주는 코드. 각각 높이와 너비
8.BoxDecoration( gradient: ~)
그라데이션 형태로 색을 칠해주는 것
9.Expanded()
남는 공간을 최대로 차지할수록 늘려주는것. 예를들어 1000이란 공간에서 컨테이너 두개가 각각 200씩 차지한타면 expanded안에 listview를 넣어줄 경우 listview는 최대 600까지 차지할 수 있도록 공간을 배정해준다.
[플러터]Column 안에 ListView
Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Column( children: [ MyListView(), ], ), ); } 그리고 class MyListView extends StatelessWidget { @override Widget build(BuildContext context) { retur
mike123789-dev.tistory.com
또한 Expanded안에 row나 column을 넣는 경우 MainAxis부분만 최대로 늘려준다.