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에 따라 필요한 텍스트가 가려지는 경우를 방지하기위해 자동으로 패딩을 주는 위젯.
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까지 차지할 수 있도록 공간을 배정해준다.
또한 Expanded안에 row나 column을 넣는 경우 MainAxis부분만 최대로 늘려준다.