mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
Play playlist from PlaylistCard support
Login page tuned Replace loading texts with Circular progress bar Sidebar icons are rounded now New Icon & banner
This commit is contained in:
parent
0ef44709fa
commit
db42e81501
68
assets/spotube-logo.svg
Normal file
68
assets/spotube-logo.svg
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:bx="https://boxy-svg.com">
|
||||||
|
<defs>
|
||||||
|
<filter id="inner-shadow-filter-1" x="-500%" y="-500%" width="1000%" height="1000%" bx:preset="inner-shadow 1 0 0 9 0.5 rgba(0,0,0,0.7)">
|
||||||
|
<feOffset dx="0" dy="0"/>
|
||||||
|
<feGaussianBlur stdDeviation="9"/>
|
||||||
|
<feComposite operator="out" in="SourceGraphic"/>
|
||||||
|
<feComponentTransfer result="choke">
|
||||||
|
<feFuncA type="linear" slope="1"/>
|
||||||
|
</feComponentTransfer>
|
||||||
|
<feFlood flood-color="rgba(0,0,0,0.7)" result="color"/>
|
||||||
|
<feComposite operator="in" in="color" in2="choke" result="shadow"/>
|
||||||
|
<feComposite operator="over" in="shadow" in2="SourceGraphic"/>
|
||||||
|
</filter>
|
||||||
|
<linearGradient id="gradient-0-0" gradientUnits="userSpaceOnUse" x1="47.146" y1="18.044" x2="47.146" y2="75.354" xlink:href="#gradient-0"/>
|
||||||
|
<linearGradient id="gradient-0">
|
||||||
|
<stop offset="0.031" style="stop-color: rgb(255, 115, 0);"/>
|
||||||
|
<stop offset="1" style="stop-color: rgb(1, 107, 255);"/>
|
||||||
|
</linearGradient>
|
||||||
|
<filter id="inner-shadow-filter-0" x="-500%" y="-500%" width="1000%" height="1000%" bx:preset="inner-shadow 1 0 0 3 0.5 rgba(0,0,0,0.7)">
|
||||||
|
<feOffset dx="0" dy="0"/>
|
||||||
|
<feGaussianBlur stdDeviation="3"/>
|
||||||
|
<feComposite operator="out" in="SourceGraphic"/>
|
||||||
|
<feComponentTransfer result="choke">
|
||||||
|
<feFuncA type="linear" slope="1"/>
|
||||||
|
</feComponentTransfer>
|
||||||
|
<feFlood flood-color="rgba(0,0,0,0.7)" result="color"/>
|
||||||
|
<feComposite operator="in" in="color" in2="choke" result="shadow"/>
|
||||||
|
<feComposite operator="over" in="shadow" in2="SourceGraphic"/>
|
||||||
|
</filter>
|
||||||
|
<linearGradient id="gradient-4-1" gradientUnits="userSpaceOnUse" x1="82.026" y1="144.832" x2="82.026" y2="264.462" xlink:href="#gradient-4"/>
|
||||||
|
<linearGradient id="gradient-4">
|
||||||
|
<stop offset="0.04" style="stop-color: rgb(255, 107, 1);"/>
|
||||||
|
<stop offset="0.598" style="stop-color: rgb(0, 234, 255);"/>
|
||||||
|
<stop offset="0.909" style="stop-color: rgb(8, 85, 140);"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="gradient-4-2" gradientUnits="userSpaceOnUse" x1="143.693" y1="22.804" x2="143.693" y2="264.582" xlink:href="#gradient-4"/>
|
||||||
|
<linearGradient id="gradient-4-0" gradientUnits="userSpaceOnUse" x1="205.862" y1="146.28" x2="205.862" y2="265.91" xlink:href="#gradient-4"/>
|
||||||
|
</defs>
|
||||||
|
<ellipse style="paint-order: fill; fill: rgb(255, 255, 255); filter: url(#inner-shadow-filter-1);" cx="249.704" cy="250.295" rx="241.45" ry="241.45"/>
|
||||||
|
<g transform="matrix(0.372585, 0, 0, 0.376313, 245.872849, 308.773438)" style="">
|
||||||
|
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: none; fill-rule: nonzero; opacity: 1;" transform="translate(-175.05 -175.05000000000004) scale(3.89 3.89)">
|
||||||
|
<path d="M 91.835 18.32 C 91.637 18.132 91.374 18.036 91.096 18.046 C 84.617 18.367 77.578 19.444 68.948 21.435 C 68.677 21.498 68.444 21.67 68.305 21.911 C 68.166 22.152 68.135 22.44 68.217 22.705 L 69.055 25.409 C 62.692 22.996 53.742 21.607 45.995 21.912 C 43.155 21.912 39.913 23.321 36.95 25.412 L 25.235 25.412 L 26.074 22.704 C 26.157 22.438 26.124 22.151 25.986 21.91 C 25.848 21.669 25.615 21.496 25.344 21.434 C 16.714 19.443 9.676 18.366 3.196 18.045 C 2.927 18.033 2.656 18.13 2.457 18.319 C 2.258 18.509 2.146 18.771 2.146 19.045 L 2.146 53.387 C 2.146 53.94 2.594 54.387 3.146 54.387 L 15.524 54.387 C 15.962 54.387 16.35 54.102 16.479 53.683 L 16.951 52.16 C 27.138 64.032 37.497 74.935 45.585 74.935 C 47.142 74.935 48.614 74.524 49.986 73.645 L 50.197 73.859 C 51.141 74.815 52.406 75.346 53.758 75.354 C 53.769 75.354 53.779 75.354 53.79 75.354 C 55.129 75.354 56.387 74.839 57.336 73.903 C 58.17 73.078 58.635 72.03 58.772 70.947 C 59.702 71.718 60.833 72.127 61.978 72.127 C 63.259 72.126 64.542 71.643 65.525 70.673 C 66.634 69.577 67.105 68.092 66.981 66.648 C 67.427 66.758 67.878 66.833 68.331 66.833 C 69.614 66.833 70.869 66.39 71.779 65.491 C 72.735 64.547 73.266 63.282 73.274 61.93 C 73.282 60.578 72.767 59.308 71.887 58.423 C 71.519 57.97 71.139 57.535 70.766 57.089 L 77.582 52.94 L 77.812 53.682 C 77.942 54.101 78.329 54.386 78.767 54.386 L 91.146 54.386 C 91.699 54.386 92.146 53.939 92.146 53.386 L 92.146 19.045 C 92.146 18.771 92.034 18.509 91.835 18.32 Z M 14.787 52.387 L 4.146 52.387 L 4.146 20.102 C 9.952 20.461 16.268 21.437 23.845 23.145 L 14.787 52.387 Z M 70.373 64.067 C 69.234 65.193 67.063 65.072 65.817 63.809 C 65.8 63.792 65.778 63.786 65.76 63.771 C 65.693 63.694 65.642 63.608 65.569 63.534 L 54.619 52.448 C 54.229 52.056 53.598 52.052 53.204 52.439 C 52.811 52.828 52.808 53.46 53.195 53.854 L 64.145 64.94 C 64.714 65.515 65.025 66.283 65.02 67.1 C 65.015 67.916 64.695 68.68 64.119 69.248 C 62.924 70.431 60.991 70.416 59.809 69.222 L 57.384 66.767 C 57.382 66.765 57.381 66.763 57.38 66.762 L 46.43 55.677 C 46.041 55.286 45.408 55.281 45.016 55.668 C 44.623 56.057 44.619 56.689 45.007 57.083 L 55.956 68.169 C 57.138 69.364 57.126 71.298 55.93 72.479 C 54.734 73.661 52.8 73.647 51.62 72.453 L 38.24 58.908 C 37.851 58.516 37.218 58.51 36.826 58.9 C 36.433 59.288 36.429 59.921 36.817 60.314 L 48.528 72.169 C 41.093 76.143 28.778 62.93 17.651 49.901 L 24.616 27.414 L 34.431 27.414 C 31.69 29.846 29.43 32.75 28.339 35.397 C 26.943 38.783 27.852 40.687 28.86 41.688 C 28.886 41.714 28.914 41.739 28.943 41.762 C 32.786 44.809 36.571 45.577 42.466 39.479 C 44.467 39.601 46.171 39.254 47.65 38.415 C 55.956 44.222 63.587 51.376 70.399 59.758 C 71.581 60.953 71.569 62.887 70.373 64.067 Z M 69.464 55.541 C 63.058 48.131 55.937 41.698 48.248 36.395 C 47.907 36.159 47.455 36.159 47.114 36.394 C 45.792 37.301 44.175 37.645 42.17 37.449 C 41.859 37.415 41.556 37.533 41.343 37.759 C 35.758 43.702 32.999 42.415 30.234 40.232 C 29.238 39.193 29.657 37.448 30.188 36.159 C 32.412 30.761 40.301 23.913 46.034 23.912 C 54.206 23.599 63.683 25.188 69.82 27.879 L 76.972 50.97 L 69.464 55.541 Z M 90.146 52.387 L 79.504 52.387 L 70.446 23.145 C 78.023 21.437 84.34 20.461 90.145 20.102 L 90.145 52.387 Z" style="stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill-rule: nonzero; opacity: 1; stroke: url(#gradient-0-0); fill: rgb(28, 28, 29); stroke-width: 3.33887px; paint-order: stroke;" stroke-linecap="round"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(1.289989, 0, 0, 1.28817, 62.9105, 31.643152)" style="filter: url(#inner-shadow-filter-0);">
|
||||||
|
<g>
|
||||||
|
<path d="M 71.421 155.437 L 71.421 253.857 C 71.421 259.724 76.162 264.462 82.026 264.462 C 87.88 264.462 92.631 259.724 92.631 253.857 L 92.631 155.437 C 92.631 149.581 87.88 144.832 82.026 144.832 C 76.162 144.832 71.421 149.576 71.421 155.437 Z" style="stroke-width: 9.80924px; stroke: url(#gradient-4-1); fill: rgb(29, 29, 29); stroke-linecap: round; stroke-linejoin: round;"/>
|
||||||
|
<path d="M29.456,264.582h23.351v-116.85c0.064-0.56,0.166-1.119,0.166-1.693c0-50.412,40.69-91.42,90.698-91.42 c50.002,0,90.692,41.008,90.692,91.42c0,0.771,0.113,1.518,0.228,2.263v116.28h23.354c16.254,0,29.442-13.64,29.442-30.469 v-60.936c0-13.878-8.989-25.57-21.261-29.249c-1.129-66.971-55.608-121.124-122.45-121.124 c-66.86,0-121.347,54.158-122.465,121.15C8.956,147.638,0,159.32,0,173.187v60.926C0,250.932,13.187,264.582,29.456,264.582z" style="stroke-width: 11.3184px; stroke: url(#gradient-4-2); fill: rgb(29, 29, 29); stroke-linecap: round; stroke-linejoin: round;"/>
|
||||||
|
<path d="M 195.258 156.885 L 195.258 255.305 C 195.258 261.172 200.006 265.91 205.862 265.91 C 211.718 265.91 216.466 261.172 216.466 255.305 L 216.466 156.885 C 216.466 151.029 211.718 146.28 205.862 146.28 C 199.995 146.28 195.258 151.024 195.258 156.885 Z" style="stroke-width: 9.80924px; stroke: url(#gradient-4-0); fill: rgb(29, 29, 29); stroke-linecap: round; stroke-linejoin: round;"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
<g transform="matrix(0.972684, 0, 0, 0.972684, 62.9105, 10.735223)" style=""/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 9.0 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 736 KiB After Width: | Height: | Size: 9.3 KiB |
@ -33,8 +33,13 @@ class _HomeState extends State<Home> {
|
|||||||
|
|
||||||
if (clientId != null && clientSecret != null) {
|
if (clientId != null && clientSecret != null) {
|
||||||
SpotifyApi spotifyApi = SpotifyApi(
|
SpotifyApi spotifyApi = SpotifyApi(
|
||||||
SpotifyApiCredentials(clientId, clientSecret,
|
SpotifyApiCredentials(clientId, clientSecret, scopes: [
|
||||||
scopes: ["user-library-read", "user-library-modify"]),
|
"user-library-read",
|
||||||
|
"user-library-modify",
|
||||||
|
"user-read-private",
|
||||||
|
"user-read-email",
|
||||||
|
"playlist-read-collaborative"
|
||||||
|
]),
|
||||||
);
|
);
|
||||||
SpotifyApiCredentials credentials = await spotifyApi.getCredentials();
|
SpotifyApiCredentials credentials = await spotifyApi.getCredentials();
|
||||||
if (credentials.accessToken?.isNotEmpty ?? false) {
|
if (credentials.accessToken?.isNotEmpty ?? false) {
|
||||||
@ -89,7 +94,7 @@ class _HomeState extends State<Home> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
color: Colors.grey.shade100,
|
color: Colors.blueGrey[50],
|
||||||
constraints: const BoxConstraints(maxWidth: 230),
|
constraints: const BoxConstraints(maxWidth: 230),
|
||||||
child: Material(
|
child: Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
@ -121,28 +126,39 @@ class _HomeState extends State<Home> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
// user name & settings
|
// user name & settings
|
||||||
Padding(
|
Consumer<SpotifyDI>(builder: (context, data, widget) {
|
||||||
|
return FutureBuilder<User>(
|
||||||
|
future: data.spotifyApi.me.get(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
return Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
Text(
|
||||||
|
snapshot.data?.displayName ??
|
||||||
"User's name",
|
"User's name",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.settings_outlined),
|
icon:
|
||||||
|
const Icon(Icons.settings_outlined),
|
||||||
onPressed: () {}),
|
onPressed: () {}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// contents of the spotify
|
// contents of the spotify
|
||||||
Consumer<SpotifyDI>(builder: (_, data, __) {
|
Expanded(
|
||||||
return Expanded(
|
|
||||||
child: Scrollbar(
|
child: Scrollbar(
|
||||||
child: PagedListView(
|
child: PagedListView(
|
||||||
pagingController: _pagingController,
|
pagingController: _pagingController,
|
||||||
@ -150,10 +166,10 @@ class _HomeState extends State<Home> {
|
|||||||
itemBuilder: (context, item, index) {
|
itemBuilder: (context, item, index) {
|
||||||
return CategoryCard(item);
|
return CategoryCard(item);
|
||||||
},
|
},
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
}),
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
@ -51,46 +52,64 @@ class _LoginState extends State<Login> {
|
|||||||
return Consumer<Auth>(
|
return Consumer<Auth>(
|
||||||
builder: (context, authState, child) {
|
builder: (context, authState, child) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Container(
|
body: Center(
|
||||||
padding: EdgeInsets.all(8.0),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text("Add your spotify credentials to get started",
|
Text("Add your spotify credentials to get started",
|
||||||
style: Theme.of(context).textTheme.headline3),
|
style: Theme.of(context).textTheme.headline4),
|
||||||
Text(
|
const Text(
|
||||||
"Don't worry, any of your credentials won't be collected or shared with anyone"),
|
"Don't worry, any of your credentials won't be collected or shared with anyone"),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 400,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
TextField(
|
TextField(
|
||||||
decoration: InputDecoration(
|
decoration: const InputDecoration(
|
||||||
hintText: "Spotify Client ID", labelText: "ClientId"),
|
hintText: "Spotify Client ID",
|
||||||
|
label: Text("ClientID"),
|
||||||
|
),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
client_id = value;
|
client_id = value;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
TextField(
|
TextField(
|
||||||
decoration: InputDecoration(
|
decoration: const InputDecoration(
|
||||||
hintText: "Spotify Client Secret",
|
hintText: "Spotify Client Secret",
|
||||||
labelText: "ClientSecret"),
|
label: Text("Client Secret"),
|
||||||
|
),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
client_secret = value;
|
client_secret = value;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
MaterialButton(
|
ElevatedButton(
|
||||||
color: Theme.of(context).buttonColor,
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
handleLogin(authState);
|
handleLogin(authState);
|
||||||
},
|
},
|
||||||
child: Text("Submit"),
|
child: const Text("Submit"),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -115,6 +115,7 @@ class _PlayerState extends State<Player> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future playPlaylist(CurrentPlaylist playlist) async {
|
Future playPlaylist(CurrentPlaylist playlist) async {
|
||||||
|
try {
|
||||||
if (player.isRunning() && playlist.id != _currentPlaylistId) {
|
if (player.isRunning() && playlist.id != _currentPlaylistId) {
|
||||||
var playlistPath = "/tmp/playlist-${playlist.id}.txt";
|
var playlistPath = "/tmp/playlist-${playlist.id}.txt";
|
||||||
File file = File(playlistPath);
|
File file = File(playlistPath);
|
||||||
@ -132,6 +133,10 @@ class _PlayerState extends State<Player> {
|
|||||||
_shuffled = false;
|
_shuffled = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} catch (e, stackTrace) {
|
||||||
|
print("[Player]: $e");
|
||||||
|
print(stackTrace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String artistsToString(List<Artist> artists) {
|
String artistsToString(List<Artist> artists) {
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/PlaylistView.dart';
|
import 'package:spotube/components/PlaylistView.dart';
|
||||||
|
import 'package:spotube/provider/Playback.dart';
|
||||||
|
import 'package:spotube/provider/SpotifyDI.dart';
|
||||||
|
|
||||||
class PlaylistCard extends StatefulWidget {
|
class PlaylistCard extends StatefulWidget {
|
||||||
PlaylistSimple playlist;
|
final PlaylistSimple playlist;
|
||||||
PlaylistCard(this.playlist);
|
const PlaylistCard(this.playlist, {Key? key}) : super(key: key);
|
||||||
@override
|
@override
|
||||||
_PlaylistCardState createState() => _PlaylistCardState();
|
_PlaylistCardState createState() => _PlaylistCardState();
|
||||||
}
|
}
|
||||||
@ -22,7 +25,7 @@ class _PlaylistCardState extends State<PlaylistCard> {
|
|||||||
));
|
));
|
||||||
},
|
},
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints: BoxConstraints(maxWidth: 200),
|
constraints: const BoxConstraints(maxWidth: 200),
|
||||||
child: Ink(
|
child: Ink(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
@ -30,7 +33,7 @@ class _PlaylistCardState extends State<PlaylistCard> {
|
|||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
blurRadius: 10,
|
blurRadius: 10,
|
||||||
offset: Offset(0, 3),
|
offset: const Offset(0, 3),
|
||||||
spreadRadius: 5,
|
spreadRadius: 5,
|
||||||
color: Colors.grey.shade300,
|
color: Colors.grey.shade300,
|
||||||
)
|
)
|
||||||
@ -40,21 +43,66 @@ class _PlaylistCardState extends State<PlaylistCard> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
// thumbnail of the playlist
|
// thumbnail of the playlist
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: CachedNetworkImage(
|
child: CachedNetworkImage(
|
||||||
imageUrl: widget.playlist.images![0].url!),
|
imageUrl: widget.playlist.images![0].url!),
|
||||||
),
|
),
|
||||||
SizedBox(height: 5),
|
Positioned.directional(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
bottom: 10,
|
||||||
|
end: 5,
|
||||||
|
child: Builder(builder: (context) {
|
||||||
|
Playback playback = context.watch<Playback>();
|
||||||
|
SpotifyDI data = context.watch<SpotifyDI>();
|
||||||
|
bool isPlaylistPlaying = playback.currentPlaylist !=
|
||||||
|
null &&
|
||||||
|
playback.currentPlaylist!.id == widget.playlist.id;
|
||||||
|
return ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (isPlaylistPlaying) return;
|
||||||
|
|
||||||
|
List<Track> tracks = (await data.spotifyApi.playlists
|
||||||
|
.getTracksByPlaylistId(widget.playlist.id!)
|
||||||
|
.all())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
playback.setCurrentPlaylist = CurrentPlaylist(
|
||||||
|
tracks: tracks,
|
||||||
|
id: widget.playlist.id!,
|
||||||
|
name: widget.playlist.name!,
|
||||||
|
thumbnail: widget.playlist.images!.first.url!,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Icon(
|
||||||
|
isPlaylistPlaying
|
||||||
|
? Icons.pause_rounded
|
||||||
|
: Icons.play_arrow_rounded,
|
||||||
|
),
|
||||||
|
style: ButtonStyle(
|
||||||
|
shape: MaterialStateProperty.all(
|
||||||
|
const CircleBorder(),
|
||||||
|
),
|
||||||
|
padding: MaterialStateProperty.all(
|
||||||
|
const EdgeInsets.all(16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
Padding(
|
Padding(
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 10),
|
const EdgeInsets.symmetric(horizontal: 8, vertical: 10),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
widget.playlist.name!,
|
widget.playlist.name!,
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -45,7 +45,7 @@ class _PlaylistGenreViewState extends State<PlaylistGenreView> {
|
|||||||
return const Center(child: Text("Error occurred"));
|
return const Center(child: Text("Error occurred"));
|
||||||
}
|
}
|
||||||
if (!snapshot.hasData) {
|
if (!snapshot.hasData) {
|
||||||
return const Center(child: Text("Loading.."));
|
return const CircularProgressIndicator.adaptive();
|
||||||
}
|
}
|
||||||
return Wrap(
|
return Wrap(
|
||||||
children: snapshot.data!
|
children: snapshot.data!
|
||||||
|
@ -15,7 +15,6 @@ class PlaylistView extends StatefulWidget {
|
|||||||
class _PlaylistViewState extends State<PlaylistView> {
|
class _PlaylistViewState extends State<PlaylistView> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Playback playback = context.read<Playback>();
|
|
||||||
return Consumer<SpotifyDI>(builder: (_, data, __) {
|
return Consumer<SpotifyDI>(builder: (_, data, __) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: FutureBuilder<Iterable<Track>>(
|
body: FutureBuilder<Iterable<Track>>(
|
||||||
@ -70,7 +69,7 @@ class _PlaylistViewState extends State<PlaylistView> {
|
|||||||
snapshot.hasError
|
snapshot.hasError
|
||||||
? const Center(child: Text("Error occurred"))
|
? const Center(child: Text("Error occurred"))
|
||||||
: !snapshot.hasData
|
: !snapshot.hasData
|
||||||
? const Center(child: Text("Loading.."))
|
? const CircularProgressIndicator.adaptive()
|
||||||
: Expanded(
|
: Expanded(
|
||||||
child: Scrollbar(
|
child: Scrollbar(
|
||||||
isAlwaysShown: true,
|
isAlwaysShown: true,
|
||||||
|
@ -32,8 +32,33 @@ class MyApp extends StatelessWidget {
|
|||||||
buttonTheme: const ButtonThemeData(
|
buttonTheme: const ButtonThemeData(
|
||||||
buttonColor: Colors.green,
|
buttonColor: Colors.green,
|
||||||
),
|
),
|
||||||
|
textTheme: TextTheme(
|
||||||
|
bodyText1: TextStyle(color: Colors.grey[850]),
|
||||||
|
headline1: TextStyle(color: Colors.grey[850]),
|
||||||
|
headline2: TextStyle(color: Colors.grey[850]),
|
||||||
|
headline3: TextStyle(color: Colors.grey[850]),
|
||||||
|
headline4: TextStyle(color: Colors.grey[850]),
|
||||||
|
headline5: TextStyle(color: Colors.grey[850]),
|
||||||
|
headline6: TextStyle(color: Colors.grey[850]),
|
||||||
),
|
),
|
||||||
home: Home(),
|
listTileTheme: ListTileThemeData(
|
||||||
|
iconColor: Colors.grey[850],
|
||||||
|
horizontalTitleGap: 0,
|
||||||
|
),
|
||||||
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.green[400]!,
|
||||||
|
width: 2.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Colors.grey[800]!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
home: const Home(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ class SideBarTiles {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<SideBarTiles> sidebarTileList = [
|
List<SideBarTiles> sidebarTileList = [
|
||||||
SideBarTiles(icon: Icons.home_filled, title: "Browse"),
|
SideBarTiles(icon: Icons.home_rounded, title: "Browse"),
|
||||||
SideBarTiles(icon: Icons.search, title: "Search"),
|
SideBarTiles(icon: Icons.search_rounded, title: "Search"),
|
||||||
SideBarTiles(icon: Icons.library_books_rounded, title: "Library"),
|
SideBarTiles(icon: Icons.library_books_rounded, title: "Library"),
|
||||||
];
|
];
|
||||||
|
Loading…
Reference in New Issue
Block a user