New icon, banner, New font, Player UI fixes, Details UI fixed

This commit is contained in:
exttex 2020-07-18 23:45:48 +02:00
parent 4b3d264e2a
commit 691f7865d5
32 changed files with 516 additions and 329 deletions

View File

@ -1,6 +1,8 @@
# freezer
A music streaming app written from scratch, which uses Deezer as backend.
![Icon](https://notabug.org/exttex/freezer/raw/master/android/app/src/main/res/mipmap-hdpi/ic_launcher.png)
Free, unlimited, without DRM music streaming app, which uses Deezer as backend.
This app is still in BETA, so it is missing features and contains bugs.
If you want to report bug or request feature, please open an issue.
@ -17,25 +19,27 @@ Compile:
flutter pub get
flutter build apk
```
## just_audio
This app depends on modified just_audio plugin with Deezer support. Repo: https://notabug.org/exttex/just_audio
## Telegram
https://t.me/freezerandroid
## Credits
Tobs: Beta tester
Bas Curtiz: Icon, Logo, Banner, Design suggestions
Deemix: https://notabug.org/RemixDev/deemix
just_audio: https://github.com/ryanheise/just_audio
## Support me
BTC: `14hcr4PGbgqeXd3SoXY9QyJFNpyurgrL9y`
ETH: `0xb4D1893195404E1F4b45e5BDA77F202Ac4012288`
## just_audio
This app depends on modified just_audio plugin with Deezer support. Repo: https://notabug.org/exttex/just_audio
## Disclaimer
```
Freezer was not developed for piracy, but educational and private usage.
It may be illegal to use this in your country!
I am not responsible in any way for the usage of this app.
```
## Support me
BTC: `14hcr4PGbgqeXd3SoXY9QyJFNpyurgrL9y`
ETH: `0xb4D1893195404E1F4b45e5BDA77F202Ac4012288`
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 997 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
assets/cover_thumb.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

BIN
assets/fonts/MabryPro.otf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -43,6 +43,10 @@ class _FreezerAppState extends State<FreezerApp> {
void initState() {
//Make update theme global
updateTheme = _updateTheme;
//Precache placeholder
precacheImage(imagesDatabase.placeholderThumb, context);
super.initState();
}

View File

@ -67,11 +67,13 @@ class Settings {
Settings({this.downloadPath, this.arl});
static const deezerBg = Color(0xFF1F1A16);
static const font = 'MabryPro';
ThemeData get themeData {
switch (theme??Themes.Light) {
case Themes.Light:
return ThemeData(
fontFamily: 'Montserrat',
fontFamily: font,
primaryColor: primaryColor,
accentColor: primaryColor,
sliderTheme: _sliderTheme,
@ -79,16 +81,33 @@ class Settings {
);
case Themes.Dark:
return ThemeData(
fontFamily: 'Montserrat',
fontFamily: font,
brightness: Brightness.dark,
primaryColor: primaryColor,
accentColor: primaryColor,
sliderTheme: _sliderTheme,
toggleableActiveColor: primaryColor,
);
case Themes.Deezer:
return ThemeData(
fontFamily: font,
brightness: Brightness.dark,
primaryColor: primaryColor,
accentColor: primaryColor,
sliderTheme: _sliderTheme,
toggleableActiveColor: primaryColor,
backgroundColor: deezerBg,
scaffoldBackgroundColor: deezerBg,
bottomAppBarColor: deezerBg,
dialogBackgroundColor: deezerBg,
bottomSheetTheme: BottomSheetThemeData(
backgroundColor: deezerBg
),
cardColor: deezerBg
);
case Themes.Black:
return ThemeData(
fontFamily: 'Montserrat',
fontFamily: font,
brightness: Brightness.dark,
primaryColor: primaryColor,
accentColor: primaryColor,
@ -185,10 +204,12 @@ enum AudioQuality {
enum Themes {
Light,
Dark,
Deezer,
Black
}
enum DownloadNaming {
DEFAULT,
STANDALONE
STANDALONE,
}

View File

@ -99,5 +99,6 @@ const _$DownloadNamingEnumMap = {
const _$ThemesEnumMap = {
Themes.Light: 'Light',
Themes.Dark: 'Dark',
Themes.Deezer: 'Deezer',
Themes.Black: 'Black',
};

View File

@ -27,6 +27,8 @@ class ImagesDatabase {
Database db;
String imagesPath;
ImageProvider placeholderThumb = new AssetImage('assets/cover_thumb.jpg');
//Prepare database
Future init() async {
String dir = await getDatabasesPath();
@ -82,7 +84,7 @@ class ImagesDatabase {
Future<PaletteGenerator> getPaletteGenerator(String url) async {
String path = await getImage(url);
//Get image provider
ImageProvider provider = AssetImage('assets/cover.jpg');
ImageProvider provider = placeholderThumb;
if (path != null) {
provider = FileImage(File(path));
}
@ -120,8 +122,7 @@ class CachedImage extends StatefulWidget {
class _CachedImageState extends State<CachedImage> {
final ImageProvider _placeholder = AssetImage('assets/cover.jpg');
ImageProvider _image = AssetImage('assets/cover.jpg');
ImageProvider _image = imagesDatabase.placeholderThumb;
double _opacity = 0.0;
bool _disposed = false;
String _prevUrl;
@ -135,7 +136,7 @@ class _CachedImageState extends State<CachedImage> {
}
//Load image from db
String path = await imagesDatabase.getImage(widget.url);
if (path == null) return _placeholder;
if (path == null) return imagesDatabase.placeholderThumb;
return FileImage(File(path));
}
@ -177,10 +178,10 @@ class _CachedImageState extends State<CachedImage> {
widget.circular ?
CircleAvatar(
radius: (widget.width??widget.height),
backgroundImage: _placeholder,
backgroundImage: imagesDatabase.placeholderThumb,
):
Image(
image: _placeholder,
image: imagesDatabase.placeholderThumb,
height: widget.height,
width: widget.width,
),

View File

@ -48,7 +48,7 @@ class AlbumDetails extends StatelessWidget {
Container(height: 8.0,),
CachedImage(
url: album.art.full,
height: 256.0,
width: MediaQuery.of(context).size.width / 2
),
Container(height: 8,),
Text(
@ -259,11 +259,10 @@ class ArtistDetails extends StatelessWidget {
children: <Widget>[
CachedImage(
url: artist.picture.full,
height: 200,
width: MediaQuery.of(context).size.width / 2 - 8,
),
Container(
width: 200.0,
height: 220,
width: MediaQuery.of(context).size.width / 2 - 8,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
@ -500,11 +499,10 @@ class _PlaylistDetailsState extends State<PlaylistDetails> {
children: <Widget>[
CachedImage(
url: playlist.image.full,
height: 180.0,
height: MediaQuery.of(context).size.width / 2 - 8,
),
Container(
width: 180,
height: 200, //Card padding
width: MediaQuery.of(context).size.width / 2 - 8,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,

View File

@ -11,19 +11,18 @@ import '../settings.dart';
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
//TODO: SingleChildScrollView vs ListView speed/perf
return SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
SafeArea(
child: FreezerTitle(),
),
Flexible(child: HomePageScreen(),)
],
),
);
/*
return ListView(
children: <Widget>[
@ -34,22 +33,33 @@ class HomeScreen extends StatelessWidget {
HomePageScreen()
],
);
*/
*/
}
}
class FreezerTitle extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
'freezer',
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Jost',
fontSize: 75,
fontStyle: FontStyle.italic,
letterSpacing: 7
return Padding(
padding: EdgeInsets.fromLTRB(0, 24, 0, 8),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Image.asset('assets/icon.png', width: 64, height: 64),
Text(
'freezer',
style: TextStyle(
fontSize: 56,
fontWeight: FontWeight.w900
),
)
],
)
],
),
);
}
@ -158,7 +168,10 @@ class _HomePageScreenState extends State<HomePageScreen> {
textAlign: TextAlign.left,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 24.0),
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold
),
),
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0)
),

View File

@ -121,40 +121,45 @@ class PlayPauseButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
//Playing
if (AudioService.playbackState?.playing??false) {
return IconButton(
iconSize: this.size,
icon: Icon(Icons.pause),
onPressed: () => AudioService.pause()
);
}
return StreamBuilder(
stream: AudioService.playbackStateStream,
builder: (context, snapshot) {
//Playing
if (AudioService.playbackState?.playing??false) {
return IconButton(
iconSize: this.size,
icon: Icon(Icons.pause),
onPressed: () => AudioService.pause()
);
}
//Paused
if ((!AudioService.playbackState.playing &&
AudioService.playbackState.processingState == AudioProcessingState.ready) ||
//None state (stopped)
AudioService.playbackState.processingState == AudioProcessingState.none) {
return IconButton(
iconSize: this.size,
icon: Icon(Icons.play_arrow),
onPressed: () => AudioService.play()
);
}
//Paused
if ((!AudioService.playbackState.playing &&
AudioService.playbackState.processingState == AudioProcessingState.ready) ||
//None state (stopped)
AudioService.playbackState.processingState == AudioProcessingState.none) {
return IconButton(
iconSize: this.size,
icon: Icon(Icons.play_arrow),
onPressed: () => AudioService.play()
);
}
switch (AudioService.playbackState.processingState) {
//Stopped/Error
case AudioProcessingState.error:
case AudioProcessingState.none:
case AudioProcessingState.stopped:
return Container(width: this.size, height: this.size);
//Loading, connecting, rewinding...
default:
return Container(
width: this.size,
height: this.size,
child: CircularProgressIndicator(),
);
}
switch (AudioService.playbackState.processingState) {
//Stopped/Error
case AudioProcessingState.error:
case AudioProcessingState.none:
case AudioProcessingState.stopped:
return Container(width: this.size, height: this.size);
//Loading, connecting, rewinding...
default:
return Container(
width: this.size,
height: this.size,
child: CircularProgressIndicator(),
);
}
},
);
}
}

View File

@ -3,9 +3,11 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:audio_service/audio_service.dart';
import 'package:flutter_screenutil/screenutil.dart';
import 'package:freezer/api/deezer.dart';
import 'package:freezer/api/player.dart';
import 'package:freezer/ui/menu.dart';
import 'package:freezer/ui/settings_screen.dart';
import 'package:freezer/ui/tiles.dart';
import 'package:async/async.dart';
@ -13,9 +15,6 @@ import 'cached_image.dart';
import '../api/definitions.dart';
import 'player_bar.dart';
class PlayerScreen extends StatefulWidget {
@override
_PlayerScreenState createState() => _PlayerScreenState();
@ -23,242 +22,35 @@ class PlayerScreen extends StatefulWidget {
class _PlayerScreenState extends State<PlayerScreen> {
double iconSize = 48;
bool _lyrics = false;
@override
Widget build(BuildContext context) {
//Responsive
ScreenUtil.init(context, allowFontScaling: true);
return Scaffold(
body: SafeArea(
child: StreamBuilder(
stream: StreamZip([AudioService.playbackStateStream, AudioService.currentMediaItemStream]),
builder: (BuildContext context, AsyncSnapshot snapshot) {
//Disable lyrics when skipping songs, loading
if (snapshot.data is PlaybackState &&
snapshot.data.processingState != AudioProcessingState.ready &&
snapshot.data.processingState != AudioProcessingState.buffering) _lyrics = false;
//When disconnected
if (AudioService.currentMediaItem == null) {
playerHelper.startService();
return Center(child: CircularProgressIndicator(),);
}
return OrientationBuilder(
builder: (context, orientation) {
//Landscape
if (orientation == Orientation.landscape) {
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(16, 0, 16, 8),
child: Container(
width: 320,
child: Stack(
children: <Widget>[
CachedImage(
url: AudioService.currentMediaItem.artUri,
),
if (_lyrics) LyricsWidget(
artUri: AudioService.currentMediaItem.artUri,
trackId: AudioService.currentMediaItem.id,
lyrics: Track.fromMediaItem(AudioService.currentMediaItem).lyrics,
height: 320.0,
),
],
),
)
),
SizedBox(
width: MediaQuery.of(context).size.width / 2 - 32,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(8, 16, 8, 0),
child: Container(
width: 300,
child: PlayerScreenTopRow(),
)
),
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
AudioService.currentMediaItem.displayTitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold
),
),
Container(height: 4,),
Text(
AudioService.currentMediaItem.displaySubtitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 18.0,
color: Theme.of(context).primaryColor,
),
),
],
),
Container(
width: 320,
child: SeekBar(),
),
Container(
width: 320,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
PrevNextButton(iconSize, prev: true,),
PlayPauseButton(iconSize),
PrevNextButton(iconSize)
],
),
),
Padding(
padding: EdgeInsets.fromLTRB(8, 0, 8, 16),
child: Container(
width: 300,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(Icons.subtitles),
onPressed: () {
setState(() => _lyrics = !_lyrics);
},
),
Text(
AudioService.currentMediaItem.extras['qualityString']
),
IconButton(
icon: Icon(Icons.more_vert),
onPressed: () {
Track t = Track.fromMediaItem(AudioService.currentMediaItem);
MenuSheet m = MenuSheet(context);
m.defaultTrackMenu(t);
},
)
],
),
)
)
],
),
)
],
);
}
return OrientationBuilder(
builder: (context, orientation) {
//Landscape
if (orientation == Orientation.landscape) {
return PlayerScreenHorizontal();
}
//Portrait
return PlayerScreenVertical();
},
);
//Portrait
return Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(28, 12, 28, 4),
child: PlayerScreenTopRow()
),
Padding(
padding: EdgeInsets.fromLTRB(16, 0, 16, 0),
child: Container(
height: 360,
child: Stack(
children: <Widget>[
CachedImage(
url: AudioService.currentMediaItem.artUri,
),
if (_lyrics) LyricsWidget(
artUri: AudioService.currentMediaItem.artUri,
trackId: AudioService.currentMediaItem.id,
lyrics: Track.fromMediaItem(AudioService.currentMediaItem).lyrics,
height: 360.0,
),
],
),
)
),
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
AudioService.currentMediaItem.displayTitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 24.0,
fontWeight: FontWeight.bold
),
),
Container(height: 4,),
Text(
AudioService.currentMediaItem.displaySubtitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: 18.0,
color: Theme.of(context).primaryColor,
),
),
],
),
SeekBar(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
PrevNextButton(iconSize, prev: true,),
PlayPauseButton(iconSize),
PrevNextButton(iconSize)
],
),
//Container(height: 8.0,),
Padding(
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 16.0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(Icons.subtitles),
onPressed: () {
setState(() => _lyrics = !_lyrics);
},
),
Text(
AudioService.currentMediaItem.extras['qualityString']
),
IconButton(
icon: Icon(Icons.more_vert),
onPressed: () {
Track t = Track.fromMediaItem(AudioService.currentMediaItem);
MenuSheet m = MenuSheet(context);
m.defaultTrackMenu(t);
},
)
],
),
)
],
);
},
);
},
),
)
@ -266,6 +58,265 @@ class _PlayerScreenState extends State<PlayerScreen> {
}
}
//Landscape
class PlayerScreenHorizontal extends StatefulWidget {
@override
_PlayerScreenHorizontalState createState() => _PlayerScreenHorizontalState();
}
class _PlayerScreenHorizontalState extends State<PlayerScreenHorizontal> {
double iconSize = ScreenUtil().setWidth(64);
bool _lyrics = false;
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(16, 0, 16, 8),
child: Container(
width: ScreenUtil().setWidth(500),
child: Stack(
children: <Widget>[
CachedImage(
url: AudioService.currentMediaItem.artUri,
),
if (_lyrics) LyricsWidget(
artUri: AudioService.currentMediaItem.artUri,
trackId: AudioService.currentMediaItem.id,
lyrics: Track.fromMediaItem(AudioService.currentMediaItem).lyrics,
height: ScreenUtil().setWidth(500),
),
],
),
)
),
//Right side
SizedBox(
width: ScreenUtil().setWidth(500),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(8, 16, 8, 0),
child: Container(
child: PlayerScreenTopRow(
textSize: ScreenUtil().setSp(26),
iconSize: ScreenUtil().setSp(32),
textWidth: ScreenUtil().setWidth(256),
short: true
),
)
),
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
AudioService.currentMediaItem.displayTitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: ScreenUtil().setSp(40),
fontWeight: FontWeight.bold
),
),
Container(height: 4,),
Text(
AudioService.currentMediaItem.displaySubtitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: ScreenUtil().setSp(32),
color: Theme.of(context).primaryColor,
),
),
],
),
Container(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: SeekBar(),
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
PrevNextButton(iconSize, prev: true,),
PlayPauseButton(iconSize),
PrevNextButton(iconSize)
],
),
),
Padding(
padding: EdgeInsets.fromLTRB(8, 0, 8, 16),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 2.0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(Icons.subtitles, size: ScreenUtil().setWidth(32)),
onPressed: () {
setState(() => _lyrics = !_lyrics);
},
),
FlatButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => QualitySettings())
),
child: Text(
AudioService.currentMediaItem.extras['qualityString'],
style: TextStyle(fontSize: ScreenUtil().setSp(24)),
),
),
IconButton(
icon: Icon(Icons.more_vert, size: ScreenUtil().setWidth(32)),
onPressed: () {
Track t = Track.fromMediaItem(AudioService.currentMediaItem);
MenuSheet m = MenuSheet(context);
m.defaultTrackMenu(t);
},
)
],
),
)
)
],
),
)
],
);
}
}
//Portrait
class PlayerScreenVertical extends StatefulWidget {
@override
_PlayerScreenVerticalState createState() => _PlayerScreenVerticalState();
}
class _PlayerScreenVerticalState extends State<PlayerScreenVertical> {
double iconSize = ScreenUtil().setWidth(100);
bool _lyrics = false;
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(28, 10, 28, 0),
child: PlayerScreenTopRow()
),
Padding(
padding: EdgeInsets.fromLTRB(16, 0, 16, 0),
child: Container(
height: ScreenUtil().setHeight(1050),
child: Stack(
children: <Widget>[
CachedImage(
url: AudioService.currentMediaItem.artUri,
),
if (_lyrics) LyricsWidget(
artUri: AudioService.currentMediaItem.artUri,
trackId: AudioService.currentMediaItem.id,
lyrics: Track.fromMediaItem(AudioService.currentMediaItem).lyrics,
height: ScreenUtil().setHeight(1050),
),
],
),
)
),
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
AudioService.currentMediaItem.displayTitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: ScreenUtil().setSp(64),
fontWeight: FontWeight.bold
),
),
Container(height: 4,),
Text(
AudioService.currentMediaItem.displaySubtitle,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.clip,
style: TextStyle(
fontSize: ScreenUtil().setSp(52),
color: Theme.of(context).primaryColor,
),
),
],
),
SeekBar(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
PrevNextButton(iconSize, prev: true,),
PlayPauseButton(iconSize),
PrevNextButton(iconSize)
],
),
//Container(height: 8.0,),
Padding(
padding: EdgeInsets.symmetric(vertical: 0, horizontal: 16.0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
IconButton(
icon: Icon(Icons.subtitles, size: ScreenUtil().setWidth(46)),
onPressed: () {
setState(() => _lyrics = !_lyrics);
},
),
FlatButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => QualitySettings())
),
child: Text(
AudioService.currentMediaItem.extras['qualityString'],
style: TextStyle(
fontSize: ScreenUtil().setSp(32),
),
),
),
IconButton(
icon: Icon(Icons.more_vert, size: ScreenUtil().setWidth(46)),
onPressed: () {
Track t = Track.fromMediaItem(AudioService.currentMediaItem);
MenuSheet m = MenuSheet(context);
m.defaultTrackMenu(t);
},
)
],
),
)
],
);
}
}
class LyricsWidget extends StatefulWidget {
final Lyrics lyrics;
@ -287,8 +338,11 @@ class _LyricsWidgetState extends State<LyricsWidget> {
Timer _timer;
int _currentIndex;
double _boxHeight;
String _trackId;
Future _load() async {
_trackId = widget.trackId;
//Get text color by album art (black or white)
if (widget.artUri != null) {
bool bw = await imagesDatabase.isDark(widget.artUri);
@ -298,7 +352,7 @@ class _LyricsWidgetState extends State<LyricsWidget> {
if (widget.lyrics.lyrics == null || widget.lyrics.lyrics.length == 0) {
//Load from api
try {
_l = await deezerAPI.lyrics(widget.trackId);
_l = await deezerAPI.lyrics(_trackId);
setState(() => _loading = false);
} catch (e) {
//Error Lyrics
@ -315,6 +369,7 @@ class _LyricsWidgetState extends State<LyricsWidget> {
void initState() {
this._boxHeight = widget.height??400.0;
_load();
Timer.periodic(Duration(milliseconds: 500), (timer) {
_timer = timer;
if (_loading) return;
@ -340,6 +395,18 @@ class _LyricsWidgetState extends State<LyricsWidget> {
super.dispose();
}
@override
void didUpdateWidget(LyricsWidget oldWidget) {
if (this._trackId != widget.trackId) {
setState(() {
_loading = true;
this._trackId = widget.trackId;
});
_load();
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return Container(
@ -360,15 +427,31 @@ class _LyricsWidgetState extends State<LyricsWidget> {
return Container(
height: _boxHeight,
child: Center(
child: Text(
_l.lyrics[i].text,
textAlign: TextAlign.center,
style: TextStyle(
color: _textColor,
fontSize: 40.0,
fontWeight: (_currentIndex == i)?FontWeight.bold:FontWeight.normal
),
),
child: Stack(
children: <Widget>[
Text(
_l.lyrics[i].text,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 36.0,
fontWeight: (_currentIndex == i)?FontWeight.bold:FontWeight.normal,
foreground: Paint()
..strokeWidth = 6
..style = PaintingStyle.stroke
..color = (_textColor==Colors.black)?Colors.white:Colors.black,
),
),
Text(
_l.lyrics[i].text,
textAlign: TextAlign.center,
style: TextStyle(
color: _textColor,
fontSize: 36.0,
fontWeight: (_currentIndex == i)?FontWeight.bold:FontWeight.normal
),
),
],
)
)
);
}),
@ -382,26 +465,55 @@ class _LyricsWidgetState extends State<LyricsWidget> {
//Top row containing QueueSource, queue...
class PlayerScreenTopRow extends StatelessWidget {
double textSize;
double iconSize;
double textWidth;
bool short;
PlayerScreenTopRow({this.textSize, this.iconSize, this.textWidth, this.short});
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Playing from: ' + playerHelper.queueSource.text,
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.right,
style: TextStyle(fontSize: 16.0),
Row(
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(0, 0, 8, 0),
child: InkWell(
child: Container(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.keyboard_arrow_down, size: this.iconSize??ScreenUtil().setWidth(46)),
),
onTap: () {
Navigator.of(context).pop();
},
),
),
Container(
width: this.textWidth??ScreenUtil().setWidth(600),
child: Text(
(short??false)?playerHelper.queueSource.text:'Playing from: ' + playerHelper.queueSource.text,
maxLines: 1,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.left,
style: TextStyle(fontSize: this.textSize??ScreenUtil().setSp(34)),
),
)
],
),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RepeatButton(),
RepeatButton(size: this.iconSize),
Container(width: 16.0,),
InkWell(
child: Icon(Icons.menu),
child: Container(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.menu, size: this.iconSize??ScreenUtil().setWidth(46)),
),
onTap: (){
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => QueueScreen()
@ -418,25 +530,33 @@ class PlayerScreenTopRow extends StatelessWidget {
class RepeatButton extends StatefulWidget {
double size;
RepeatButton({this.size, Key key}): super(key: key);
@override
_RepeatButtonState createState() => _RepeatButtonState();
}
class _RepeatButtonState extends State<RepeatButton> {
double _size = ScreenUtil().setWidth(46);
Icon get icon {
switch (playerHelper.repeatType) {
case RepeatType.NONE:
return Icon(Icons.repeat);
return Icon(Icons.repeat, size: widget.size??_size);
case RepeatType.LIST:
return Icon(
Icons.repeat,
color: Theme.of(context).primaryColor,
size: widget.size??_size
);
case RepeatType.TRACK:
return Icon(
Icons.repeat_one,
color: Theme.of(context).primaryColor,
size: widget.size??_size
);
}
}
@ -453,7 +573,10 @@ class _RepeatButtonState extends State<RepeatButton> {
await playerHelper.changeRepeat();
setState(() {});
},
child: icon,
child: Container(
padding: EdgeInsets.all(8.0),
child: icon,
),
);
}
}
@ -505,13 +628,13 @@ class _SeekBarState extends State<SeekBar> {
Text(
_timeString(position),
style: TextStyle(
fontSize: 14.0
fontSize: ScreenUtil().setSp(35)
),
),
Text(
_timeString(duration),
style: TextStyle(
fontSize: 14.0
fontSize: ScreenUtil().setSp(35)
),
)
],

View File

@ -126,7 +126,8 @@ class SearchResultsScreen extends StatelessWidget {
'Tracks',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 26.0
fontSize: 26.0,
fontWeight: FontWeight.bold
),
),
...List.generate(3, (i) {
@ -170,7 +171,8 @@ class SearchResultsScreen extends StatelessWidget {
'Albums',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 26.0
fontSize: 26.0,
fontWeight: FontWeight.bold
),
),
...List.generate(3, (i) {
@ -208,7 +210,8 @@ class SearchResultsScreen extends StatelessWidget {
'Artists',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 26.0
fontSize: 26.0,
fontWeight: FontWeight.bold
),
),
Container(height: 4),
@ -243,7 +246,8 @@ class SearchResultsScreen extends StatelessWidget {
'Playlists',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 26.0
fontSize: 26.0,
fontWeight: FontWeight.bold
),
),
...List.generate(3, (i) {

View File

@ -136,7 +136,16 @@ class _AppearanceSettingsState extends State<AppearanceSettings> {
updateTheme();
Navigator.of(context).pop();
},
)
),
SimpleDialogOption(
child: Text('Deezer (Dark)'),
onPressed: () {
setState(() => settings.theme = Themes.Deezer);
settings.save();
updateTheme();
Navigator.of(context).pop();
},
),
],
);
}
@ -274,9 +283,13 @@ class _QualityPickerState extends State<QualityPicker> {
});
switch (widget.field) {
case 'mobile':
settings.mobileQuality = _quality; break;
settings.mobileQuality = _quality;
settings.updateAudioServiceQuality();
break;
case 'wifi':
settings.wifiQuality = _quality; break;
settings.wifiQuality = _quality;
settings.updateAudioServiceQuality();
break;
case 'download':
settings.downloadQuality = _quality; break;
case 'offline':

View File

@ -129,7 +129,7 @@ class ArtistTile extends StatelessWidget {
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 16.0
fontSize: 14.0
),
),
Container(height: 4,),
@ -228,7 +228,7 @@ class PlaylistCardTile extends StatelessWidget {
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 16.0),
style: TextStyle(fontSize: 14.0),
),
),
Container(height: 8.0,)
@ -271,7 +271,7 @@ class SmartTrackListTile extends StatelessWidget {
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 16.0
fontSize: 14.0
),
),
),
@ -315,7 +315,7 @@ class AlbumCard extends StatelessWidget {
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 16.0
fontSize: 14.0
),
),
),