Docs: Producthunt badge

ArtistCard navigate to artist profile support
ArtistProfile details section added
This commit is contained in:
Kingkor Roy Tirtho 2022-01-23 13:24:17 +06:00
parent 3b88f91a5b
commit 46b652788f
5 changed files with 151 additions and 30 deletions

View File

@ -15,7 +15,9 @@ Following are the features that currently spotube offers:
- Small size & less data hungry
- No spotify or youtube ads since it uses all public & free APIs (But it's recommended to support the creators by watching/liking/subscribing to the artists youtube channel or add as favourite track in spotify. Mostly buying spotify premium is the best way to support their valuable creations)
- Lyrics
- Downloadable track (WIP)
- Downloadable track
<a href="https://www.producthunt.com/posts/spotube?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-spotube" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=327965&theme=dark" alt="Spotube - A lightweight+free Spotify desktop-client made with flutter | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
# Installation
@ -98,9 +100,9 @@ Also, you need a [genius](https://genius.com) account for **lyrics** & a API Cli
# TODO:
- [ ] Compile, Debug & Build for **MacOS**
- [x] Compile, Debug & Build for **MacOS**
- [x] Add support for show Lyric of currently playing track
- [ ] Track download
- [x] Track download
- [ ] Support for playing/streaming podcasts/shows
- [ ] Artist, User & Album pages

View File

@ -1,6 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/Artist/ArtistProfile.dart';
class ArtistCard extends StatelessWidget {
final Artist artist;
@ -9,7 +10,13 @@ class ArtistCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {},
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return ArtistProfile(artist.id!);
},
));
},
borderRadius: BorderRadius.circular(10),
child: Ink(
width: 200,
@ -35,7 +42,7 @@ class ArtistCard extends StatelessWidget {
.images?.isNotEmpty ??
false)
? artist.images!.first.url!
: "https://avatars.dicebear.com/api/open-peeps/${artist.id}.svg?b=%231ed760&r=50&flip=1&translateX=3&translateY=-6"),
: "https://avatars.dicebear.com/api/open-peeps/${artist.id}.png?b=%231ed760&r=50&flip=1&translateX=3&translateY=-6"),
),
Text(
artist.name!,

View File

@ -0,0 +1,124 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
import 'package:spotube/helpers/readable-number.dart';
import 'package:spotube/provider/SpotifyDI.dart';
class ArtistProfile extends StatefulWidget {
final String artistId;
const ArtistProfile(this.artistId, {Key? key}) : super(key: key);
@override
_ArtistProfileState createState() => _ArtistProfileState();
}
class _ArtistProfileState extends State<ArtistProfile> {
@override
Widget build(BuildContext context) {
SpotifyApi spotify = context.watch<SpotifyDI>().spotifyApi;
return Scaffold(
body: FutureBuilder<Artist>(
future: spotify.artists.get(widget.artistId),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator.adaptive());
}
return Column(
children: [
const PageWindowTitleBar(
leading: BackButton(),
),
Padding(
padding: const EdgeInsets.all(20),
child: Row(
children: [
const SizedBox(width: 50),
CircleAvatar(
maxRadius: 250,
minRadius: 100,
backgroundImage: CachedNetworkImageProvider(
snapshot.data!.images!.first.url!,
),
),
Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(50)),
child: Text(snapshot.data!.type!.toUpperCase(),
style: Theme.of(context)
.textTheme
.headline6
?.copyWith(color: Colors.white)),
),
Text(
snapshot.data!.name!,
style: Theme.of(context).textTheme.headline2,
),
Text(
"${toReadableNumber(snapshot.data!.followers!.total!.toDouble())} followers",
style: Theme.of(context).textTheme.headline5,
),
const SizedBox(height: 20),
Row(
children: [
// TODO: Implement check if user follows this artist
// LIMITATION: spotify-dart lib
FutureBuilder(
future: Future.value(true),
builder: (context, snapshot) {
return OutlinedButton(
onPressed: () async {
// TODO: make `follow/unfollow` artists button work
// LIMITATION: spotify-dart lib
},
child: Text(snapshot.data == true
? "Following"
: "Follow"),
);
}),
IconButton(
icon: const Icon(Icons.share_rounded),
onPressed: () {
Clipboard.setData(
ClipboardData(
text: snapshot
.data?.externalUrls?.spotify),
).then((val) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
width: 300,
behavior: SnackBarBehavior.floating,
content: Text(
"Artist URL copied to clipboard",
textAlign: TextAlign.center,
),
),
);
});
},
)
],
)
],
),
),
],
),
)
],
);
},
),
);
}
}

View File

@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:hotkey_manager/hotkey_manager.dart';
@ -48,30 +47,6 @@ class _PlayerState extends State<Player> with WidgetsBindingObserver {
HotKey(KeyCode.space, scope: HotKeyScope.inapp),
_playOrPause,
),
// causaes crash in Windows and macOS for aquiring global hotkey of
// keyboard media buttons
if (!Platform.isWindows && !Platform.isMacOS) ...[
GlobalKeyActions(
HotKey(KeyCode.mediaPlayPause),
_playOrPause,
),
GlobalKeyActions(HotKey(KeyCode.mediaTrackNext), (key) async {
_movePlaylistPositionBy(1);
}),
GlobalKeyActions(HotKey(KeyCode.mediaTrackPrevious), (key) async {
_movePlaylistPositionBy(-1);
}),
GlobalKeyActions(HotKey(KeyCode.mediaStop), (key) async {
Playback playback = context.read<Playback>();
setState(() {
_isPlaying = false;
_currentTrackId = null;
_duration = null;
_shuffled = false;
});
playback.reset();
})
]
];
WidgetsBinding.instance?.addObserver(this);
WidgetsBinding.instance?.addPostFrameCallback(_init);

View File

@ -0,0 +1,13 @@
String toReadableNumber(double num) {
if (num > 999 && num < 99999) {
return "${(num / 1000).toStringAsFixed(1)}K";
} else if (num > 99999 && num < 999999) {
return "${(num / 1000).toStringAsFixed(0)}K";
} else if (num > 999999 && num < 999999999) {
return "${(num / 1000000).toStringAsFixed(1)}M";
} else if (num > 999999999) {
return "${(num / 1000000000).toStringAsFixed(1)}B";
} else {
return num.toString();
}
}