일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- ModalBottomSheet
- 더보기창
- 백준
- flutter
- image classification
- K-nearest neighbor
- 고려대학교
- OSAM
- 다익스트라
- 앱 개발
- Dijkstra
- 고려대학교 응원가
- 이미지 분류
- 뮤직플레이어
- 앱 출시
- 알고리즘
- optimization
- linear classifier
- google_ml_kit
- Firebase
- text recognition
- CS231n
- 해커톤
- 앱
- image_picker
- 고연전
- loss function
- KUsic
- 응원가
- 국방오픈소스아카데미
- Today
- Total
영주머니의 개발주머니
고려대학교 응원가 뮤직플레이어 Flutter 앱 제작기(3) - 기능 구현 본문
플레이스토어 다운로드 링크
쿠직(KUsic) - 고려대학교 응원가 - Google Play 앱
고려대학교 응원가 전용 뮤직 플레이어 앱입니다.
play.google.com
기본적인 UI를 완성한 지 거의 2주가 지났다.. 앱 페이지 레이아웃을 2일 만에 완성하고서 이제 Flutter에 좀 익숙해졌나 싶었지만 어디까지나 껍데기 만들기에 한정된 이야기였다. 그래도 어찌어찌해서 최소한의 기능을 하는 앱을 완성했다. 내가 구현하고자 했던 기능들은 다음과 같다.
- Melon, Flo와 같은 음악 재생 기능 (백그라운드 재생, 반복 재생, 랜덤 재생 등)
- 나만의 응원가 플레이리스트 기능
- 좋아요 기능
- 가사 조회 기능 (응원가 싱크에 맞춰서 표출되는 가사 변경)
- 응원곡 mp3 및 lrc 파일 서버에 저장해서 불러오기
- 각 응원가마다 응원동작이 잘 보이는 YouTube 영상 링크로 연결하기
사실 대단한 기능이 있는 것은 아니지만 플러그인들을 사용하는데 애를 많이 먹어서 예상보다 개발 기간이 많이 길어졌다. 개발 기간이 2주를 넘어가니까 지쳐서 서버랑 Youtube 링크는 구현을 못했다...
사용한 플러그인은 다음과 같다.
- provider
- just_audio
- just_audio_background
- lrc
- shared_preferences
원래 처음에는 "audioplayers"라는 플러그인을 사용했는데 다 완성하고 백그라운드 재생 기능 구현만 남겨둔 상태에서 3일 동안 시도해보다가 실패했다. "audio_services" 플러그인을 사용하면 될 것 같았는데 개발 문서랑 stackoverflow를 아무리 찾아봐도 내 문제를 해결할 수 없었다. 그래서 충분한 사전조사 후 간단하게 백그라운드 재생을 구현할 수 있게 만들어진 "just_audio"로 지금까지 구현한 모든 기능들을 다시 구현했다. 그래도 다른 플러그인으로 같은 기능을 한번 구현해봐서 하루 안에 완성할 수 있었다. "just_audio"로 코드를 수정한 이후에는 "just_audio_background"를 사용하니 MediaTag를 추가해주는 것으로 간단히 백그라운드 재생 기능 구현에 성공했다. 5분도 안걸려서 상당히 허무했다.. 플러그인을 바꾸기로 큰 결심을 했을 때 힘든 여정이 될 것 같아서 심호흡 한번 크게 하고 시작했는데 생각보다 쉽게 끝나서 다행이다 ㅋㅋ
Provider도 이번에 처음 사용해봤다. Provider는 상태 관리 플러그인으로 데이터 공유를 쉽게 할 수 있도록 도와준다. Provider 기초 지식은 코딩파파님의 YouTube 영상(https://www.youtube.com/watch?v=ikV-Ub9XgO4&t=2222s)을 많이 참고했다. 사실 좋아요 페이지는 딱히 만들 생각이 없었는데 이 영상에서 예제로 좋아요 기능을 만들길래 연습 겸 따라서 만들어봤다. 맨날 전역변수 설정하고 setstate()로 rebuild 하다 보니 조금만 데이터 전달 구조가 복잡해져도 뭐가 어떤 변수였고 어디서 setstate()를 해야 하는지 헷갈렸는데 이런 문제를 해결할 수 있는게 Provider였다. ChangeNotifier에 공유할 데이터들을 변수로 설정하고 관련된 기능들을 함수로 설정한 뒤 notifylisteners()를 실행해주면 ChangeNotifier에서 사용한 변수들과 관련된 모든 위젯들이 알아서 rebuild 된다. 예를 들어서 likedSongList를 변수로 정의하고 addLikedSong() 함수를 정의하면 ChangeNotifier 밖의 모든 페이지에서 likedSongList의 데이터에 접근하거나 addLikedSong() 함수를 호출해서 likedSongList의 데이터를 변화시킬 수 있다.
class LikedNotifier extends ChangeNotifier {
List<String> _likedSongList = [];
void addLikedSong(String likedSong) {
_likedSongList.add(likedSong);
notifyListeners(); //변경사항을 관련있는 위젯들에게 알려줘서 rebuild하게 만듦
}
List<String> get likedSongList => _likedSongList; //getter를 설정해서 외부에서 list 접근 가능
}
좋아요 기능 구현으로 대략적인 감을 잡은 다음에 Provider를 just_audio와 결합해서 사용하는 것을 시도했다. 뮤직 플레이어 앱들을 보면 홈페이지, 검색 페이지, 플레이리스트, 가사 조회 페이지 등 모든 페이지에서 AudioPlayer에 접근해서 재생, 일시정지, 재생 시간 변경 등의 기능을 사용할 수 있게 되어있다. 따라서 ChangeNotifier 내에서 하나의 AudioPlayer를 정의하고 duration, position, currentSong 등의 재생 정보들을 담은 변수들과 playSong(), pauseSong() 같은 함수들에 접근해서 모든 페이지에서 AudioPlayer의 동작을 제어할 수 있도록 구현했다. 쉽게 말하면 좋아요 페이지에서 likedSongList를 AudioPlayer로 바꾼 것 뿐이다. 차이점이 있다면 AudioPlayer에서는 음원이 재생되면서 계속해서 position이나 duration 변수 값이 바뀌기 때문에 just_audio 플러그인에서 제공해주는 positionStream, durationStream을 listen 하면서 변수들을 업데이트해줬다.
class AudioPlayerNotifier extends ChangeNotifier {
final AudioPlayer _audioPlayer = AudioPlayer();
Duration _duration = Duration();
Duration _position = Duration();
AudioPlayerNotifier() {
initAudio();
}
initAudio() {
...
_audioPlayer.durationStream.listen((d) {
if (d != null) {
_duration = d;
notifyListeners();
}
});
_audioPlayer.positionStream.listen((p) {
_position = p;
notifyListeners();
});
}
}
가사랑 응원가를 싱크하는 작업은 LRC 파일을 직접 만들어서 구현했다. 고려대학교 응원가는 이미 만들어진 LRC 파일이 없어서 직접 하나하나 timestamp 찍어가면서 만들었다.. LRC 파일만 넣으면 기존의 뮤직 플레이어들처럼 싱크에 맞춰서 가사를 표출해주는 플러그인이 있을 것이라고 기대하고 열심히 찾아봤는데 비슷한 것들은 있어도 딱 내가 필요로 하는 기능을 구현해주는 플러그인은 없었다. 그나마 비슷하던 플러그인은 개발 문서도 빈약하고 설명도 중국어가 섞여있어서 변형해서 사용하는 것도 포기했다. 결국 lrc 플러그인을 사용해서 LRC 파일을 String으로 바꿔서 가져온 뒤 가사의 각 줄을 timestamp와 lyric을 가지고 있는 Class Instance로 만들어주는 것까지 도움을 받았다. 그러고 나서는 아까 만든 AudioPlayerNotifier에서 position값을 가지고 와서 (해당 가사의 timestamp 값) <= (position 값) < (다음 가사의 timestamp 값)이 true면 해당 가사가 표출되도록 간단하게 구현했다. 그런데 너무 간단하게 구현한 것인지 가사 업데이트가 1초에 한 번씩만 이뤄지는데 이것 때문에 몇몇 가사들의 싱크가 살짝 안 맞는다. 하지만 아직 해결책을 찾지는 못했다... 싱크가 대충은 맞기에 그럭저럭 만족하고 일단은 나중에 수정할 예정이다.
shared_preference는 사용자의 단말기에 간단한 데이터를 저장할 수 있도록 해주는 플러그인이다. shared_preference를 사용하기 전에는 플레이리스트에 응원가를 추가한 뒤 앱을 종료하면 플레이리스트가 초기화되어서 재실행하면 다시 플레이리스트가 비어있다. 이때 단말기에 플레이리스트 정보를 저장함으로써 앱을 종료 후 재실행해도 플레이리스트에 담아 놓은 곡들이 그대로 남아있을 수 있다. 사실 서버를 따로 공부해서 앱과 연동시키는 방식으로 구현하려고 했는데 그러기에는 앱 출시까지 너무 시간이 오래 걸릴 것 같아서 일단은 shared_preference를 이용해서 임시방편으로 구현해두었다.
아쉬운 부분도 많고 마음에 안 드는 부분도 아직 많지만 더 이상 개발을 했다가는 마음이 꺾여버릴 것 같아서 일단은 지금 상태로 플레이스토어 출시를 한 뒤 업데이트를 하는 형식으로 보완하고자 한다. 그래도 이제 플레이스토어 출시가 임박했다고 생각하니 설렌다.
GitHub - yjjo8136/Korea-Univ-Cheer-Song-Player
Contribute to yjjo8136/Korea-Univ-Cheer-Song-Player development by creating an account on GitHub.
github.com
'Flutter > Flutter 앱 개발' 카테고리의 다른 글
고려대학교 응원가 뮤직플레이어 Flutter 앱 제작기(5) - 애플 앱스토어 출시 (0) | 2022.12.13 |
---|---|
Flutter 앱 App Store에 제출하는 방법 (2) | 2022.12.10 |
고려대학교 응원가 뮤직플레이어 Flutter 앱 제작기(4) - 구글 플레이스토어 출시 (0) | 2022.12.05 |
고려대학교 응원가 뮤직플레이어 Flutter 앱 제작기(2) - UI/UX 개발 (0) | 2022.11.19 |
고려대학교 응원가 뮤직플레이어 Flutter 앱 제작기(1) - 기획 (0) | 2022.11.15 |