mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
AlbumView Added
This commit is contained in:
parent
b7a2b16bb6
commit
58148f3493
@ -1,9 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/components/Album/AlbumView.dart';
|
||||
import 'package:spotube/components/Shared/PlaybuttonCard.dart';
|
||||
import 'package:spotube/helpers/artist-to-string.dart';
|
||||
import 'package:spotube/helpers/simple-track-to-track.dart';
|
||||
import 'package:spotube/provider/Playback.dart';
|
||||
import 'package:spotube/provider/SpotifyDI.dart';
|
||||
|
||||
class AlbumCard extends StatelessWidget {
|
||||
final Album album;
|
||||
@ -12,6 +15,8 @@ class AlbumCard extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Playback playback = context.watch<Playback>();
|
||||
bool isPlaylistPlaying = playback.currentPlaylist != null &&
|
||||
playback.currentPlaylist!.id == album.id;
|
||||
|
||||
return PlaybuttonCard(
|
||||
imageUrl: album.images!.first.url!,
|
||||
@ -20,8 +25,29 @@ class AlbumCard extends StatelessWidget {
|
||||
title: album.name!,
|
||||
description:
|
||||
"Alubm • ${artistsToString<ArtistSimple>(album.artists ?? [])}",
|
||||
onTap: () {},
|
||||
onPlaybuttonPressed: () => {},
|
||||
onTap: () {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return AlbumView(album);
|
||||
},
|
||||
));
|
||||
},
|
||||
onPlaybuttonPressed: () async {
|
||||
SpotifyApi spotify = context.read<SpotifyDI>().spotifyApi;
|
||||
if (isPlaylistPlaying) return;
|
||||
List<Track> tracks = (await spotify.albums.getTracks(album.id!).all())
|
||||
.map((track) => simpleTrackToTrack(track, album))
|
||||
.toList();
|
||||
if (tracks.isEmpty) return;
|
||||
|
||||
playback.setCurrentPlaylist = CurrentPlaylist(
|
||||
tracks: tracks,
|
||||
id: album.id!,
|
||||
name: album.name!,
|
||||
thumbnail: album.images!.first.url!,
|
||||
);
|
||||
playback.setCurrentTrack = tracks.first;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
97
lib/components/Album/AlbumView.dart
Normal file
97
lib/components/Album/AlbumView.dart
Normal file
@ -0,0 +1,97 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
|
||||
import 'package:spotube/components/Shared/TracksTableView.dart';
|
||||
import 'package:spotube/helpers/simple-track-to-track.dart';
|
||||
import 'package:spotube/provider/Playback.dart';
|
||||
import 'package:spotube/provider/SpotifyDI.dart';
|
||||
|
||||
class AlbumView extends StatelessWidget {
|
||||
final Album album;
|
||||
const AlbumView(this.album, {Key? key}) : super(key: key);
|
||||
|
||||
playPlaylist(Playback playback, List<Track> tracks, {Track? currentTrack}) {
|
||||
currentTrack ??= tracks.first;
|
||||
var isPlaylistPlaying = playback.currentPlaylist?.id == album.id;
|
||||
if (!isPlaylistPlaying) {
|
||||
playback.setCurrentPlaylist = CurrentPlaylist(
|
||||
tracks: tracks,
|
||||
id: album.id!,
|
||||
name: album.name!,
|
||||
thumbnail: album.images!.first.url!,
|
||||
);
|
||||
playback.setCurrentTrack = currentTrack;
|
||||
} else if (isPlaylistPlaying &&
|
||||
currentTrack.id != null &&
|
||||
currentTrack.id != playback.currentTrack?.id) {
|
||||
playback.setCurrentTrack = currentTrack;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Playback playback = context.watch<Playback>();
|
||||
|
||||
var isPlaylistPlaying = playback.currentPlaylist?.id == album.id;
|
||||
SpotifyApi spotify = context.watch<SpotifyDI>().spotifyApi;
|
||||
return Scaffold(
|
||||
body: FutureBuilder<Iterable<TrackSimple>>(
|
||||
future: spotify.albums.getTracks(album.id!).all(),
|
||||
builder: (context, snapshot) {
|
||||
List<Track> tracks = snapshot.data?.map((trackSmp) {
|
||||
return simpleTrackToTrack(trackSmp, album);
|
||||
}).toList() ??
|
||||
[];
|
||||
return Column(
|
||||
children: [
|
||||
PageWindowTitleBar(
|
||||
leading: Row(
|
||||
children: [
|
||||
// nav back
|
||||
const BackButton(),
|
||||
// heart playlist
|
||||
IconButton(
|
||||
icon: const Icon(Icons.favorite_outline_rounded),
|
||||
onPressed: () {},
|
||||
),
|
||||
// play playlist
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
isPlaylistPlaying
|
||||
? Icons.stop_rounded
|
||||
: Icons.play_arrow_rounded,
|
||||
),
|
||||
onPressed: snapshot.hasData
|
||||
? () => playPlaylist(playback, tracks)
|
||||
: null,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Text(album.name!,
|
||||
style: Theme.of(context).textTheme.headline4),
|
||||
),
|
||||
snapshot.hasError
|
||||
? const Center(child: Text("Error occurred"))
|
||||
: !snapshot.hasData
|
||||
? const Expanded(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator.adaptive()),
|
||||
)
|
||||
: TracksTableView(
|
||||
tracks,
|
||||
onTrackPlayButtonPressed: (currentTrack) =>
|
||||
playPlaylist(
|
||||
playback,
|
||||
tracks,
|
||||
currentTrack: currentTrack,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
|
||||
import 'package:spotube/helpers/zero-pad-num-str.dart';
|
||||
import 'package:spotube/components/Shared/TracksTableView.dart';
|
||||
import 'package:spotube/provider/Playback.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@ -17,7 +16,8 @@ class PlaylistView extends StatefulWidget {
|
||||
class _PlaylistViewState extends State<PlaylistView> {
|
||||
playPlaylist(Playback playback, List<Track> tracks, {Track? currentTrack}) {
|
||||
currentTrack ??= tracks.first;
|
||||
var isPlaylistPlaying = playback.currentPlaylist?.id == widget.playlist.id;
|
||||
var isPlaylistPlaying = playback.currentPlaylist?.id != null &&
|
||||
playback.currentPlaylist?.id == widget.playlist.id;
|
||||
if (!isPlaylistPlaying) {
|
||||
playback.setCurrentPlaylist = CurrentPlaylist(
|
||||
tracks: tracks,
|
||||
@ -33,111 +33,11 @@ class _PlaylistViewState extends State<PlaylistView> {
|
||||
}
|
||||
}
|
||||
|
||||
List<TableRow> trackToTableRow(Playback playback, List<Track> tracks) {
|
||||
return tracks.asMap().entries.map((track) {
|
||||
String? thumbnailUrl = (track.value.album?.images?.isNotEmpty ?? false)
|
||||
? track.value.album?.images?.last.url
|
||||
: null;
|
||||
String duration =
|
||||
"${track.value.duration?.inMinutes.remainder(60)}:${zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}";
|
||||
return (TableRow(
|
||||
children: [
|
||||
TableCell(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
(track.key + 1).toString(),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
)),
|
||||
TableCell(
|
||||
child: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
playback.currentTrack?.id != null &&
|
||||
playback.currentTrack?.id == track.value.id
|
||||
? Icons.pause_circle_rounded
|
||||
: Icons.play_circle_rounded,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
onPressed: () {
|
||||
playPlaylist(playback, tracks, currentTrack: track.value);
|
||||
},
|
||||
),
|
||||
if (thumbnailUrl != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
||||
child: CachedNetworkImage(
|
||||
placeholder: (context, url) {
|
||||
return Container(
|
||||
height: 40,
|
||||
width: 40,
|
||||
color: Colors.green[300],
|
||||
);
|
||||
},
|
||||
imageUrl: thumbnailUrl,
|
||||
maxHeightDiskCache: 40,
|
||||
maxWidthDiskCache: 40,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
track.value.name ?? "",
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 17,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Text(
|
||||
(track.value.artists ?? [])
|
||||
.map((e) => e.name)
|
||||
.join(", "),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
TableCell(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
track.value.album?.name ?? "",
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
TableCell(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
duration,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
));
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Playback playback = context.watch<Playback>();
|
||||
var isPlaylistPlaying =
|
||||
playback.currentPlaylist?.id == this.widget.playlist.id;
|
||||
var isPlaylistPlaying = playback.currentPlaylist?.id != null &&
|
||||
playback.currentPlaylist?.id == widget.playlist.id;
|
||||
return Consumer<SpotifyDI>(builder: (_, data, __) {
|
||||
return Scaffold(
|
||||
body: FutureBuilder<Iterable<Track>>(
|
||||
@ -150,8 +50,6 @@ class _PlaylistViewState extends State<PlaylistView> {
|
||||
.then((tracks) => tracks.map((e) => e.track!)),
|
||||
builder: (context, snapshot) {
|
||||
List<Track> tracks = snapshot.data?.toList() ?? [];
|
||||
TextStyle tableHeadStyle =
|
||||
const TextStyle(fontWeight: FontWeight.bold, fontSize: 16);
|
||||
return Column(
|
||||
children: [
|
||||
PageWindowTitleBar(
|
||||
@ -189,51 +87,13 @@ class _PlaylistViewState extends State<PlaylistView> {
|
||||
child: Center(
|
||||
child: CircularProgressIndicator.adaptive()),
|
||||
)
|
||||
: Expanded(
|
||||
child: Scrollbar(
|
||||
child: ListView(
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
child: Table(
|
||||
columnWidths: const {
|
||||
0: FixedColumnWidth(40),
|
||||
1: FlexColumnWidth(),
|
||||
2: FlexColumnWidth(),
|
||||
3: FixedColumnWidth(45),
|
||||
},
|
||||
children: [
|
||||
TableRow(
|
||||
children: [
|
||||
TableCell(
|
||||
child: Text(
|
||||
"#",
|
||||
textAlign: TextAlign.center,
|
||||
style: tableHeadStyle,
|
||||
)),
|
||||
TableCell(
|
||||
child: Text(
|
||||
"Title",
|
||||
style: tableHeadStyle,
|
||||
)),
|
||||
TableCell(
|
||||
child: Text(
|
||||
"Album",
|
||||
style: tableHeadStyle,
|
||||
)),
|
||||
TableCell(
|
||||
child: Text(
|
||||
"Time",
|
||||
textAlign: TextAlign.center,
|
||||
style: tableHeadStyle,
|
||||
)),
|
||||
],
|
||||
),
|
||||
...trackToTableRow(playback, tracks),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
: TracksTableView(
|
||||
tracks,
|
||||
onTrackPlayButtonPressed: (currentTrack) =>
|
||||
playPlaylist(
|
||||
playback,
|
||||
tracks,
|
||||
currentTrack: currentTrack,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
169
lib/components/Shared/TracksTableView.dart
Normal file
169
lib/components/Shared/TracksTableView.dart
Normal file
@ -0,0 +1,169 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/helpers/zero-pad-num-str.dart';
|
||||
import 'package:spotube/provider/Playback.dart';
|
||||
|
||||
class TracksTableView extends StatelessWidget {
|
||||
final void Function(Track currentTrack)? onTrackPlayButtonPressed;
|
||||
final List<Track> tracks;
|
||||
const TracksTableView(this.tracks, {Key? key, this.onTrackPlayButtonPressed})
|
||||
: super(key: key);
|
||||
|
||||
List<TableRow> trackToTableRow(
|
||||
BuildContext context, Playback playback, List<Track> tracks) {
|
||||
return tracks.asMap().entries.map((track) {
|
||||
String? thumbnailUrl = (track.value.album?.images?.isNotEmpty ?? false)
|
||||
? track.value.album?.images?.last.url
|
||||
: null;
|
||||
String duration =
|
||||
"${track.value.duration?.inMinutes.remainder(60)}:${zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}";
|
||||
return (TableRow(
|
||||
children: [
|
||||
TableCell(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
(track.key + 1).toString(),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
)),
|
||||
TableCell(
|
||||
child: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
playback.currentTrack?.id != null &&
|
||||
playback.currentTrack?.id == track.value.id
|
||||
? Icons.pause_circle_rounded
|
||||
: Icons.play_circle_rounded,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
onPressed: () => onTrackPlayButtonPressed?.call(
|
||||
track.value,
|
||||
),
|
||||
),
|
||||
if (thumbnailUrl != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(5)),
|
||||
child: CachedNetworkImage(
|
||||
placeholder: (context, url) {
|
||||
return Container(
|
||||
height: 40,
|
||||
width: 40,
|
||||
color: Colors.green[300],
|
||||
);
|
||||
},
|
||||
imageUrl: thumbnailUrl,
|
||||
maxHeightDiskCache: 40,
|
||||
maxWidthDiskCache: 40,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
track.value.name ?? "",
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 17,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Text(
|
||||
(track.value.artists ?? [])
|
||||
.map((e) => e.name)
|
||||
.join(", "),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
TableCell(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
track.value.album?.name ?? "",
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
TableCell(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
duration,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
));
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Playback playback = context.watch<Playback>();
|
||||
|
||||
TextStyle tableHeadStyle =
|
||||
const TextStyle(fontWeight: FontWeight.bold, fontSize: 16);
|
||||
return Expanded(
|
||||
child: Scrollbar(
|
||||
child: ListView(
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
child: Table(
|
||||
columnWidths: const {
|
||||
0: FixedColumnWidth(40),
|
||||
1: FlexColumnWidth(),
|
||||
2: FlexColumnWidth(),
|
||||
3: FixedColumnWidth(45),
|
||||
},
|
||||
children: [
|
||||
TableRow(
|
||||
children: [
|
||||
TableCell(
|
||||
child: Text(
|
||||
"#",
|
||||
textAlign: TextAlign.center,
|
||||
style: tableHeadStyle,
|
||||
)),
|
||||
TableCell(
|
||||
child: Text(
|
||||
"Title",
|
||||
style: tableHeadStyle,
|
||||
)),
|
||||
TableCell(
|
||||
child: Text(
|
||||
"Album",
|
||||
style: tableHeadStyle,
|
||||
)),
|
||||
TableCell(
|
||||
child: Text(
|
||||
"Time",
|
||||
textAlign: TextAlign.center,
|
||||
style: tableHeadStyle,
|
||||
)),
|
||||
],
|
||||
),
|
||||
...trackToTableRow(context, playback, tracks),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
23
lib/helpers/simple-track-to-track.dart
Normal file
23
lib/helpers/simple-track-to-track.dart
Normal file
@ -0,0 +1,23 @@
|
||||
import 'package:spotify/spotify.dart';
|
||||
|
||||
Track simpleTrackToTrack(TrackSimple trackSmp, Album album) {
|
||||
Track track = Track();
|
||||
track.name = trackSmp.name;
|
||||
track.album = album;
|
||||
track.artists = trackSmp.artists;
|
||||
track.availableMarkets = trackSmp.availableMarkets;
|
||||
track.discNumber = trackSmp.discNumber;
|
||||
track.durationMs = trackSmp.durationMs;
|
||||
track.explicit = trackSmp.explicit;
|
||||
track.externalUrls = trackSmp.externalUrls;
|
||||
track.href = trackSmp.href;
|
||||
track.id = trackSmp.id;
|
||||
track.isPlayable = trackSmp.isPlayable;
|
||||
track.linkedFrom = trackSmp.linkedFrom;
|
||||
track.name = trackSmp.name;
|
||||
track.previewUrl = trackSmp.previewUrl;
|
||||
track.trackNumber = trackSmp.trackNumber;
|
||||
track.type = trackSmp.type;
|
||||
track.uri = trackSmp.uri;
|
||||
return track;
|
||||
}
|
Loading…
Reference in New Issue
Block a user