Flutter.완성본

2주차 과제 완성본(shazam)

도슬 2023. 7. 1. 00:59

과제

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  //생성자 선언. : '이 클래스를 이렇게 생성하겠다' 라고 지정
  //이 생성자에서는 key값을 파라미터로 받아
  //MyApp 클래스가 상속받는 StatlessWidget 클래스에 key를 전달해줄 수 있다
  //선언하지 않으면 기본 생성자만 쓰입니다. MyApp()으로 선언해서 객체를 생성할 수 있다.
  //대신 key와 같은 인자를 선언하지 않았기 때문에 super 생성자에 인자를 넘겨줄 수 없다.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      //1. 앱의 시작은 MaterialApp으로 시작
      //그 다음 home에다가 띄워줄 HomePage위젯을 작성해서 불러오자.
      home: HomePage(),
    );
  }
}

//2. HomePage 클래스를 StatefulWidget으로 생성
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(
        //3. DefaultTabController위젯으로 반환하도록 만들자.
        //DefaultTabController를 이용해서 화면을 옆으로 넘길 수 있는 바를 구현가능
        //(그냥 TabBar만 사용해서 에러가 날 경우에 SizedBox를 이용)
        initialIndex: 1,
        length: 3, //3종류의 탭이므로 length를 3으로 줬다.
        child: Scaffold(
          body: Stack(
            children: [
              TabBarView(
                //3. TabBarView를 이용해 3종류의 탭을 만들었다.
                children: [
                  FirstTab(),
                  SecondTab(),
                  ThirdTab(),
                  //3종류의 탭 각각 밑에 내용이없으면 오류가남
                ],
              ),
              SafeArea(
                //SafeArea : 폰에따라 카메라, 버튼의 위치가 제각각이라 기종에 따라
                //내용이 가려지거나 사라지는 UI가 있을 수 있다.
                //디자인한 UI가 제대로 보여지기 위해서 사용한다.
                child: Padding(
                  padding:
                      const EdgeInsets.symmetric(vertical: 30, horizontal: 16),
                  child: Column(
                    children: [
                      Container(
                        alignment: Alignment.topCenter,
                        child: TabPage(),
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ));
  }
}

class TabPage extends StatefulWidget {
  const TabPage({super.key});
  //4. 'TabPage'를 stateful 위젯으로 생성

  @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.blue[400],
      //조건? 반환값1 : 반환값2
      //: 조건이 참일때 반환값1, 조건이 거짓일때 반환값2
      selectedColor: DefaultTabController.of(context).index == 1
          ? Colors.white
          : Colors.blue,
      indicatorSize: 8,
    );
  }
}

//첫번쨰 탭
class FirstTab extends StatelessWidget {
  const FirstTab({Key? key}) : super(key: key);
//1. TabBarView를 이용해 FirstTab만 만들고 FirstTab을 StatelessWidget으로 생성했다.
//위에서 TabBarView로 3종류의 탭 모두 선언했다면 오류가난다..
  @override
  Widget build(BuildContext context) {
    const songs = [
      {
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
      {
        'title': '가을밤에 든 생각',
        'artist': '잔나비',
      },
    ];
    return SafeArea(
        child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            child:
                Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
              //7. 칼럼 속 내부를 crossAxisAlignment: CrossAxisAlignment.start를 이용해
              //좌로 밀착 시켰다.
              Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  //8. 첫줄 'settings'아이콘과 '라이브러리'텍스트를
                  //mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  //crossAxisAlignment: CrossAxisAlignment.start,
                  //를 이용해서 가로로 떨어뜨리고 세로로 위로 밀착시켰다.
                  children: [
                    Padding(
                      //2. 아이콘과 텍스트를 Row를 이용해 가로로 배치
                      padding: const EdgeInsets.symmetric(vertical: 4),
                      child: Icon(Icons.settings),
                    ),
                    Text(
                      "라이브러리",
                      style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    Icon(null),
                    //9. 현재 8번에서 사용한 spaceBetween때문에 세팅아이콘과 라이브러리 글자가
                    //양쪽끝에 밀착해 있다. 이때 Icon(null)을 사용해서 라이브러리 뒤에
                    //가상의 아이콘을 배치해서 '라이브러리 텍스트'를 중간으로 오게한다.
                    //정리하면 우측 빈공간에 가상의 아이콘이 있는 상황이다.
                  ]),
              SizedBox(height: 8),
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 8.0),
                child: Row(
                  //3. 아이콘+텍스트 배치
                  children: [
                    ImageIcon(
                      NetworkImage("https://i.ibb.co/hxNbZ8p/shazam.png"),
                      size: 18,
                    ),
                    SizedBox(width: 12),
                    Text(
                      "Shazam",
                      style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.w600,
                      ),
                    ),
                  ],
                ),
              ),
              Divider(), //Divider를 이용해 선을 그어서 구분지어주었다.
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 8.0),
                child: Row(
                  //4. 아이콘+텍스트 배치
                  children: [
                    Icon(Icons.person_rounded),
                    SizedBox(width: 8),
                    Text(
                      "아티스트",
                      style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.w600,
                      ),
                    ),
                  ],
                ),
              ),
              Divider(),
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 8.0),
                child: Row(
                  //5. 아이콘+텍스트 배치
                  children: [
                    Icon(Icons.music_note),
                    SizedBox(width: 8),
                    Text(
                      "회원님을 위한 재생목록",
                      style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.w600,
                      ),
                    ),
                  ],
                ),
              ),
              SizedBox(height: 16),
              Text(
                //6. 텍스트 배치
                "최근 Shazam",
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.w700,
                ),
              ),
              SizedBox(height: 16),
              Expanded(
                  child: GridView.builder(
                //GridView.builder로 Grid레이아웃 만들기
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  //행에 보여줄 Item 갯수
                  childAspectRatio: 3 / 5,
                  //가로 : 세로 의 비율
                ),
                itemCount: songs.length,
                itemBuilder: (context, index) {
                  var song = songs[index];
                  String imageUrl = song['imageUrl']!;
                  String title = song['title']!;
                  String artist = song['artist']!;

                  index % 2 == 0;

                  return Container(
                    margin: EdgeInsets.all(4),
                    decoration: BoxDecoration(
                      color: Colors.white,
                      borderRadius: BorderRadius.all(
                        Radius.circular(8),
                      ),
                      boxShadow: [
                        BoxShadow(
                          color: Colors.grey.withOpacity(0.5),
                          blurRadius: 1,
                          spreadRadius: 1,
                        ),
                      ],
                    ),
                    child: Column(children: [
                      ClipRRect(
                        borderRadius: BorderRadius.only(
                          topLeft: Radius.circular(8),
                          topRight: Radius.circular(8),
                        ),
                        child: Image.network(
                          imageUrl,
                          fit: BoxFit.cover,
                          height: MediaQuery.of(context).size.width *
                              0.5 *
                              5 /
                              3 *
                              0.55,
                        ),
                      ),
                      Expanded(
                        child: Container(
                          padding: const EdgeInsets.all(8),
                          width: double.infinity,
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Text(
                                title,
                                style: TextStyle(
                                  fontSize: 18,
                                  fontWeight: FontWeight.bold,
                                ),
                              ),
                              Text(
                                artist,
                                style: TextStyle(
                                  fontSize: 14,
                                  color: Colors.grey[600],
                                ),
                              ),
                              Image.network(
                                "https://i.ibb.co/KG9m5QS/applemusic.png",
                                width: 60,
                              ),
                              SizedBox(height: 5),
                            ],
                          ),
                        ),
                      )
                    ]),
                  );
                },
              )),
            ])));
  }
}

//두번쨰 탭
class SecondTab extends StatelessWidget {
  const SecondTab({Key? key}) : super(key: key); //SecondTab을 Stateless위젯으로 생성

  @override
  Widget build(BuildContext context) {
    return Container(
      //배경1. 전체 박스를 컨테이너로 묶음
      decoration: BoxDecoration(
          gradient: LinearGradient(
        //배경2. 배경에 그라디언트 효과를 주는 컴포넌트. 2가지 이상의 색상으로 그라디언트 효과를 나타낼 수 있다.
        begin: Alignment.topCenter, //탑센터부터
        end: Alignment.bottomCenter, //바텀센터까지
        colors: [
          Colors.blue[300]!,
          Colors.blue[900]!
        ], //배경3. 블루300~ 블루900 점점 진해진다.
      )),
      child: SafeArea(
        //7. SafeArea를 이용. 여백을 확보해서 가려지는부분 없애줌.
        child: Column(
          //6. 전체를 Cloumn으로 묶는다.
          children: [
            Padding(
              // 5. 라이브러리와 차트 위치조정을 위해 패딩으로 묶어줌
              padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
              child: Row(
                //3. 라이브러리와 차트를 가로배치하기위해서 Row 사용
                children: [
                  GestureDetector(
                    // 2. 라이브러리 아이콘+텍스트를 버튼으로 만들어줌
                    onTap: () {
                      DefaultTabController.of(context).animateTo(0);
                    },
                    child: Column(
                      //1. 라이브러리 아이콘+텍스트를 만들어줌
                      children: [
                        Icon(
                          Icons.person,
                          color: Colors.white,
                        ),
                        Text(
                          "라이브러리",
                          style: TextStyle(
                              color: Colors.white, fontWeight: FontWeight.bold),
                        )
                      ],
                    ),
                  ),
                  Spacer(), //Spacer()은 위젯과 위젯사이의 간격 조절. (예) Spacer(flex: 2)
                  GestureDetector(
                    // 4. 1~3번과정과 동일하게 차트의 아이콘+텍스트를 만들어줌
                    onTap: () {
                      DefaultTabController.of(context).animateTo(2);
                    },
                    child: Column(
                      children: [
                        Icon(
                          Icons.show_chart,
                          color: Colors.white,
                        ),
                        Text(
                          "차트",
                          style: TextStyle(
                            color: Colors.white,
                            fontWeight: FontWeight.bold,
                          ),
                        )
                      ],
                    ),
                  ),
                ],
              ),
            ),
            SizedBox(height: MediaQuery.of(context).size.height * 0.1),
            // MediaQuery클래스의 of(context)를 사용하면
            //현재 화면의 MedaQueryData가 변경될때마다 위젯이 자동으로 리빌딩 된다.
            //빌드 위젯 내에 위치하여야한다. 그렇지 않을경우 null값을 반환한다.
            // MediaQuery.of(context).size              앱 화면 크기
            // MediaQuery.of(context).size.height       앱 화면 높이
            // MediaQuery.of(context).size.width        앱 화면 넓이
            // MediaQuery.of(context).devicePixelRatio  앱 화면 비율
            Text(
              //8. 텍스트 만들기
              "Shazam하려면 탭하세요",
              style: TextStyle(
                fontSize: 24,
                fontWeight: FontWeight.bold,
                color: Colors.white,
              ),
            ),
            SizedBox(height: MediaQuery.of(context).size.height * 0.06),
            Container(
              //9. 시야를 좀 크게보자. 순서대로 배경과 이미지불러오기
              alignment: Alignment.center, // Alignment: 정렬 , 즉 중앙정렬
              width: 200,
              height: 200,
              decoration: BoxDecoration(
                color: Colors.blue[300],
                shape: BoxShape.circle,
              ),
              child: Image.network(
                "https://i.ibb.co/hxNbZ8p/shazam.png",
                color: Colors.white,
                width: 130,
                height: 130,
              ),
            ),
            SizedBox(
              height: MediaQuery.of(context).size.height * 0.12,
            ),
            Container(
              //10. 9번과 동일하게 작성
              width: 50,
              height: 50,
              alignment: Alignment.center,
              decoration: BoxDecoration(
                color: Colors.blue[400],
                shape: BoxShape.circle,
              ),
              child: Icon(
                Icons.search,
                color: Colors.white,
                size: 30,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

//세번째 탭
class ThirdTab extends StatelessWidget {
  const ThirdTab({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    //1. 소환될 데이터 작성
    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',
        },
      ],
    };
    return SafeArea(
        //2. 크게보고 SafeArea -> Column -> Text 까지 작성
        child: Column(
      children: [
        Text(
          "차트",
          style: TextStyle(
            fontSize: 18,
            fontWeight: FontWeight.bold,
          ),
        ),
        SizedBox(
          height: 20,
        ),
        Expanded(
          //8. Expanded를 이용해 오버플로우를 없애줌
          child: ListView(
            //7. ListView를 이용해 스크롤 만들어줌
            children: [
              Stack(
                //5. Stack으로 컨테이너와 패딩을 묶고 배경 설정
                alignment: Alignment.center,
                children: [
                  Container(
                    width: double.infinity,
                    height: 180,
                    color: Colors.purple[900],
                  ),
                  Column(
                    children: [
                      Container(
                        //3. 컨테이너로 묶고 엘레베이터버튼과 텍스트를 만듬.
                        width: MediaQuery.of(context).size.width * 0.8,
                        child: ElevatedButton(
                          onPressed: () {},
                          style: ButtonStyle(
                            backgroundColor:
                                MaterialStateProperty.all(Colors.white),
                          ),
                          child: Text(
                            "국가 및 도시별 차트",
                            style: TextStyle(
                              fontWeight: FontWeight.bold,
                              color: Colors.purple[900],
                            ),
                          ),
                        ),
                      ),
                      Padding(
                        //4. 패딩으로 묶고 텍스트 작성
                        padding: const EdgeInsets.all(8),
                        child: Text(
                          "전 세계",
                          style: TextStyle(
                            fontWeight: FontWeight.bold,
                            color: Colors.white,
                          ),
                        ),
                      )
                    ],
                  ),
                ],
              ),
              Divider(
                //6. 선긋고 동일하게 작성
                color: Colors.grey[400],
                thickness: 10,
                height: 10,
              ),
              Column(
                children: [
                  Padding(
                    padding: const EdgeInsets.all(8),
                    child: Row(
                      children: [
                        Text(
                          "대한민국 차트",
                          style: TextStyle(
                            fontSize: 16,
                          ),
                        ),
                        Spacer(),
                        Text(
                          "모두 보기",
                          style: TextStyle(color: Colors.blue),
                        ),
                      ],
                    ),
                  ),
                  Row(
                    children: [
                      ...chartData['korea']!.map((song) {
                        String imageUrl = song['imageUrl']!;
                        String name = song['name']!;
                        String artist = song['artist']!;
                        // 솔직히 불러오는건 잘 모르겠다.. 눈에만 익혀보자.

                        return Padding(
                          padding: const EdgeInsets.all(8),
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Image.network(
                                imageUrl,
                                width: MediaQuery.of(context).size.width * 0.29,
                              ),
                              Text(
                                name,
                                style: TextStyle(fontWeight: FontWeight.bold),
                              ),
                              Text(artist),
                            ],
                          ),
                        );
                      }),
                    ],
                  ),
                ],
              ),
              Divider(
                color: Colors.grey[400],
                thickness: 10,
                height: 10,
              ),
              Column(
                children: [
                  Padding(
                    padding: const EdgeInsets.all(8),
                    child: Row(
                      children: [
                        Text(
                          "글로벌 차트",
                          style: TextStyle(
                            fontSize: 16,
                          ),
                        ),
                        Spacer(),
                        Text(
                          "모두 보기",
                          style: TextStyle(color: Colors.blue),
                        ),
                      ],
                    ),
                  ),
                  Row(
                    children: [
                      ...chartData['global']!.map((song) {
                        String imageUrl = song['imageUrl']!;
                        String name = song['name']!;
                        String artist = song['artist']!;

                        return Padding(
                          padding: const EdgeInsets.all(8),
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Image.network(
                                imageUrl,
                                width: MediaQuery.of(context).size.width * 0.29,
                              ),
                              Text(
                                name,
                                style: TextStyle(fontWeight: FontWeight.bold),
                              ),
                              Text(artist),
                            ],
                          ),
                        );
                      }),
                    ],
                  ),
                ],
              ),
              Divider(
                color: Colors.grey[400],
                thickness: 10,
                height: 10,
              ),
              Column(
                children: [
                  Padding(
                    padding: const EdgeInsets.all(8),
                    child: Row(
                      children: [
                        Text(
                          "뉴욕 차트",
                          style: TextStyle(
                            fontSize: 16,
                          ),
                        ),
                        Spacer(),
                        Text(
                          "모두 보기",
                          style: TextStyle(color: Colors.blue),
                        ),
                      ],
                    ),
                  ),
                  Row(
                    children: [
                      ...chartData['newyork']!.map((song) {
                        String imageUrl = song['imageUrl']!;
                        String name = song['name']!;
                        String artist = song['artist']!;

                        return Padding(
                          padding: const EdgeInsets.all(8),
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Image.network(
                                imageUrl,
                                width: MediaQuery.of(context).size.width * 0.29,
                              ),
                              Text(
                                name,
                                style: TextStyle(fontWeight: FontWeight.bold),
                              ),
                              Text(artist),
                            ],
                          ),
                        );
                      }),
                    ],
                  ),
                ],
              ),
            ],
          ),
        ),
      ],
    ));
  }
}