매일 땡기는 마라 코딩

[9주 완성 프로젝트 캠프 : 플러터(유데미x스나이퍼팩토리)] 3일차 과제 - 유튜브 뮤직 클론코딩 본문

Flutter

[9주 완성 프로젝트 캠프 : 플러터(유데미x스나이퍼팩토리)] 3일차 과제 - 유튜브 뮤직 클론코딩

cmkoi1 2023. 10. 22. 13:43

요구사항

  • 음악명은 최대 2줄까지만 가능하다.
  • 가수명과 플레이시간은 최대 1줄까지만 가능하며 필요한 경우 가수명을 줄인다.
  • 음악의 정보를 보여주는 위젯을 만들고, 이름은 MusicTile로 한다.

 

 

사전 지식

"theme"

  • MaterialApp의 theme 속성을 사용하면 앱 전체의 테마를 지정해 줄 수 있다.
  • theme: ThemeData.from(colorScheme: ColorScheme.dart())

 

 

 

"shape"

모서리의 형태를 지정해 줄 수 있다.

 

 

 

"ClipRRect"

  • 모서리를 둥글게 만들 때 사용한다.
  • borderRadius 속성을 사용하여 borderRadius: BorderRadius.circular(4)와 같이 작성한다.

 

 

"Axis"

  • mainAxis는 위젯의 기본 방향, crossAxis는 반대 방향에 대한 속성이다.
  • size를 지정할 경우, max는 남은 공간을 모두 차지하고, min은 포함된 자식 콘텐츠의 크기만큼 차지한다.

 

 

 

그 외

  • trailing은 원칙적으로 하나이다.
  • Row는 자식을 확장시키려는 성질이 있어 제한이 필요함.

 

 

 

 

코드

main.dart

import 'package:assigment2/MusicTile.dart';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.from(colorScheme: ColorScheme.dark()),
      home: Scaffold(
        appBar: AppBar(
          // backgroundColor: Colors.black,
          // foregroundColor: Colors.white,
          elevation: 0,
          leading: Icon(Icons.navigate_before),
          title: Text('아워리스트'),
          centerTitle: false,
          actions: [
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: Icon(Icons.airplay),
            ),
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: Icon(Icons.search),
            ),
          ],
          shape: Border(
            bottom: BorderSide(
              color: Colors.white12,
              width: 1,
            ),
          ),
        ),
        body: Padding(
          padding: const EdgeInsets.only(top: 8),
          child: ListView(
            children: [
              MusicTile(
                imgUrl: 'assets/images/music_come_with_me.png',
                title: 'Come with me',
                artist: 'Surfaces 및 salem ilese',
                playTime: '3:00',
              ),
              MusicTile(
                imgUrl: 'assets/images/music_Good_day.png',
                title: 'Good day',
                artist: 'Surfaces',
                playTime: '3:09',
              ),
              MusicTile(
                imgUrl: 'assets/images/music_Honesty.png',
                title: 'Honesty',
                artist: 'Pink Sweat\$',
                playTime: '3:24',
              ),
              MusicTile(
                imgUrl: 'assets/images/music_I_Wish_I_Missed_My_Ex.png',
                title: 'I Wish I Missed My Ex',
                artist: '마할리아 버크마',
                playTime: '3:20',
              ),
              MusicTile(
                imgUrl: 'assets/images/music_Plastic_Plants.png',
                title: 'Plastic Plants',
                artist: '마할리아 버크마',
                playTime: '3:24',
              ),
              MusicTile(
                imgUrl: 'assets/images/music_Sucker_for_you.png',
                title: 'Sucker for you',
                artist: '맷 테리',
                playTime: '3:00',
              ),
              MusicTile(
                imgUrl: 'assets/images/music_Summer_is_for_falling_in_love.png',
                title: 'Summer is for falling in love',
                artist: 'Sarah Kang & Eye Love Brandon',
                playTime: '3:00',
              ),
              MusicTile(
                imgUrl: 'assets/images/music_these_days.png',
                title: 'These days(feat. Jess Glynne, Macklemore & Dan Caplen)',
                artist: 'Rudimental',
                playTime: '3:00',
              ),
              MusicTile(
                imgUrl: 'assets/images/music_You_Make_Me.png',
                title: 'You Make Me',
                artist: 'DAY6',
                playTime: '3:00',
              ),
              MusicTile(
                imgUrl: 'assets/images/music_Zombie_Pop.png',
                title: 'Zombie Pop',
                artist: 'DRP IAN',
                playTime: '3:00',
              ),
            ],
          ),
        ),
        bottomSheet: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Container(
              height: 64,
              color: Colors.white12,
              child: ListTile(
                leading: ClipRRect(
                  borderRadius: BorderRadius.circular(4),
                  child: Image.asset('assets/images/music_You_Make_Me.png'),
                ),
                title: Text('You Make Me'),
                subtitle: Text('Day6'),
                trailing: Row(
                  //trailing은 원칙적으로 하나, Row는 자식을 확장시키려는 성질이 있음. -> 확장하지 못하게 크기 제한
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Icon(Icons.play_arrow),
                    ),
                    Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Icon(Icons.skip_next),
                    ),
                  ],
                ),
              ),
            ),
            Container(
              height: 1.5,
              alignment: Alignment.centerLeft,
              color: Colors.white54,
              child: Container(
                width: 14,
                color: Colors.white70,
              ),
            )
          ],
        ),
        bottomNavigationBar: BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          fixedColor: Colors.white,
          currentIndex: 2,
          items: [
            BottomNavigationBarItem(icon: Icon(Icons.home), label: ('홈')),
            BottomNavigationBarItem(icon: Icon(Icons.search), label: ('둘러보기')),
            BottomNavigationBarItem(
                icon: Icon(Icons.library_music), label: ('보관함')),
          ],
        ),
      ),
      // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

 

MusicTile.dart

import 'package:flutter/material.dart';

class MusicTile extends StatelessWidget {
  const MusicTile(
      {super.key,
      required this.imgUrl,
      required this.title,
      required this.artist,
      required this.playTime});

  final String imgUrl;
  final String title;
  final String artist;
  final String playTime;

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: ClipRRect(
        //자르기
        borderRadius: BorderRadius.circular(4),
        child: Image.asset(
          imgUrl,
        ),
      ),
      title: Text(
        title,
        maxLines: 2, //두 줄 이상인 경우 생략
        overflow: TextOverflow.ellipsis, //자를 경우 어떻게 보여 줄 것인지
        style: TextStyle(
          fontWeight: FontWeight.w400,
        ),
      ),
      subtitle: Row(
        children: [
          Icon(
            Icons.check_circle,
            size: 16,
          ),
          SizedBox(width: 4),
          Flexible(
              child: Text(
            artist,
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
          )),
          SizedBox(width: 4),
          Text('·'),
          SizedBox(width: 4),
          Text(playTime),
        ],
      ),
      trailing: Icon(Icons.more_vert),
    );
  }
}

 

 

 

결과

 

 

회고

하단의 bottomSheet가 body의 위젯들을 가리는 오류가 있었다. 사이즈 박스를 한 칸 넣으면 해결된다고는 하는데, 다른 방법이 있나 찾아 보면 좋을 것 같다.

 

 

 

 

참고 자료

 

본 후기는 유데미-스나이퍼팩토리 9주 완성 프로젝트캠프 학습 일지 후기로 작성 되었습니다.

 

#유데미 #udemy #스나이퍼팩토리 #웅진씽크빅 #인사이드아웃 #IT개발캠프 #개발자부트캠프 #웹개발 #앱개발 #플러터 #flutter #개발 #안드로이드 #ios #단기캠프

728x90