lyrics: allow user to scroll freely
This commit is contained in:
parent
e30e2bebf1
commit
122c7c2ae7
|
|
@ -24,14 +24,15 @@ class _LyricsScreenState extends State<LyricsScreen> {
|
||||||
Lyrics lyrics;
|
Lyrics lyrics;
|
||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
bool _error = false;
|
bool _error = false;
|
||||||
int _currentIndex = 0;
|
int _currentIndex = -1;
|
||||||
int _prevIndex = 0;
|
int _prevIndex = -1;
|
||||||
Timer _timer;
|
Timer _timer;
|
||||||
ScrollController _controller = ScrollController();
|
ScrollController _controller = ScrollController();
|
||||||
StreamSubscription _mediaItemSub;
|
StreamSubscription _mediaItemSub;
|
||||||
final double height = 90;
|
final double height = 90;
|
||||||
|
|
||||||
bool _freeScroll = false;
|
bool _freeScroll = false;
|
||||||
|
bool _animatedScroll = false;
|
||||||
|
|
||||||
Future _load() async {
|
Future _load() async {
|
||||||
//Already available
|
//Already available
|
||||||
|
|
@ -59,15 +60,17 @@ class _LyricsScreenState extends State<LyricsScreen> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _scrollToLyric() {
|
Future<void> _scrollToLyric() async {
|
||||||
//Lyric height, screen height, appbar height
|
//Lyric height, screen height, appbar height
|
||||||
double _scrollTo = (height * _currentIndex) -
|
double _scrollTo = (height * _currentIndex) -
|
||||||
(MediaQuery.of(context).size.height / 2) +
|
(MediaQuery.of(context).size.height / 2) +
|
||||||
(height / 2) +
|
(height / 2) +
|
||||||
56;
|
56;
|
||||||
if (0 > _scrollTo) return;
|
if (0 > _scrollTo) return;
|
||||||
_controller.animateTo(_scrollTo,
|
_animatedScroll = true;
|
||||||
|
await _controller.animateTo(_scrollTo,
|
||||||
duration: Duration(milliseconds: 250), curve: Curves.ease);
|
duration: Duration(milliseconds: 250), curve: Curves.ease);
|
||||||
|
_animatedScroll = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -82,7 +85,7 @@ class _LyricsScreenState extends State<LyricsScreen> {
|
||||||
(l) => l.offset <= AudioService.playbackState.currentPosition);
|
(l) => l.offset <= AudioService.playbackState.currentPosition);
|
||||||
if (_loading) return;
|
if (_loading) return;
|
||||||
//Scroll to current lyric
|
//Scroll to current lyric
|
||||||
if (_currentIndex <= 0) return;
|
if (_currentIndex < 0) return;
|
||||||
if (_prevIndex == _currentIndex) return;
|
if (_prevIndex == _currentIndex) return;
|
||||||
//Update current lyric index
|
//Update current lyric index
|
||||||
setState(() => null);
|
setState(() => null);
|
||||||
|
|
@ -149,8 +152,10 @@ class _LyricsScreenState extends State<LyricsScreen> {
|
||||||
children: [
|
children: [
|
||||||
//Visualizer
|
//Visualizer
|
||||||
if (settings.lyricsVisualizer)
|
if (settings.lyricsVisualizer)
|
||||||
Align(
|
Positioned(
|
||||||
alignment: Alignment.bottomCenter,
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
child: StreamBuilder(
|
child: StreamBuilder(
|
||||||
stream: playerHelper.visualizerStream,
|
stream: playerHelper.visualizerStream,
|
||||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
|
|
@ -193,17 +198,10 @@ class _LyricsScreenState extends State<LyricsScreen> {
|
||||||
)
|
)
|
||||||
: NotificationListener(
|
: NotificationListener(
|
||||||
onNotification: (Notification notification) {
|
onNotification: (Notification notification) {
|
||||||
if (notification is! ScrollEndNotification)
|
if (_freeScroll ||
|
||||||
|
notification is! ScrollStartNotification)
|
||||||
return false;
|
return false;
|
||||||
if (_freeScroll) return false;
|
if (!_animatedScroll)
|
||||||
double _currentScroll = _controller.position.pixels;
|
|
||||||
double _expectedScroll = (height * _currentIndex) -
|
|
||||||
(MediaQuery.of(context).size.height / 2) +
|
|
||||||
(height / 2) +
|
|
||||||
56;
|
|
||||||
print(
|
|
||||||
'current: $_currentScroll, expected: $_expectedScroll');
|
|
||||||
if (_currentScroll != _expectedScroll)
|
|
||||||
setState(() => _freeScroll = true);
|
setState(() => _freeScroll = true);
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@ class PlayerScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PlayerScreenState extends State<PlayerScreen> {
|
class _PlayerScreenState extends State<PlayerScreen> {
|
||||||
|
|
||||||
LinearGradient _bgGradient;
|
LinearGradient _bgGradient;
|
||||||
StreamSubscription _mediaItemSub;
|
StreamSubscription _mediaItemSub;
|
||||||
ImageProvider _blurImage;
|
ImageProvider _blurImage;
|
||||||
|
|
@ -53,35 +52,51 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
||||||
//BG Image
|
//BG Image
|
||||||
if (settings.blurPlayerBackground)
|
if (settings.blurPlayerBackground)
|
||||||
setState(() {
|
setState(() {
|
||||||
_blurImage = NetworkImage(AudioService.currentMediaItem.extras['thumb'] ?? AudioService.currentMediaItem.artUri);
|
_blurImage = NetworkImage(
|
||||||
|
AudioService.currentMediaItem.extras['thumb'] ??
|
||||||
|
AudioService.currentMediaItem.artUri);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Run in isolate
|
//Run in isolate
|
||||||
PaletteGenerator palette = await PaletteGenerator.fromImageProvider(CachedNetworkImageProvider(AudioService.currentMediaItem.extras['thumb'] ?? AudioService.currentMediaItem.artUri));
|
PaletteGenerator palette = await PaletteGenerator.fromImageProvider(
|
||||||
|
CachedNetworkImageProvider(
|
||||||
|
AudioService.currentMediaItem.extras['thumb'] ??
|
||||||
|
AudioService.currentMediaItem.artUri));
|
||||||
|
|
||||||
//Update notification
|
//Update notification
|
||||||
if (settings.blurPlayerBackground)
|
if (settings.blurPlayerBackground)
|
||||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||||
statusBarColor: palette.dominantColor.color.withOpacity(0.25),
|
statusBarColor: palette.dominantColor.color.withOpacity(0.25),
|
||||||
systemNavigationBarColor: Color.alphaBlend(palette.dominantColor.color.withOpacity(0.25), Theme.of(context).scaffoldBackgroundColor),
|
systemNavigationBarColor: Color.alphaBlend(
|
||||||
systemNavigationBarIconBrightness: ThemeData.estimateBrightnessForColor(palette.dominantColor.color) == Brightness.light ? Brightness.dark : Brightness.light
|
palette.dominantColor.color.withOpacity(0.25),
|
||||||
));
|
Theme.of(context).scaffoldBackgroundColor),
|
||||||
|
systemNavigationBarIconBrightness:
|
||||||
|
ThemeData.estimateBrightnessForColor(
|
||||||
|
palette.dominantColor.color) ==
|
||||||
|
Brightness.light
|
||||||
|
? Brightness.dark
|
||||||
|
: Brightness.light));
|
||||||
|
|
||||||
//Color gradient
|
//Color gradient
|
||||||
if (!settings.blurPlayerBackground) {
|
if (!settings.blurPlayerBackground) {
|
||||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||||
statusBarColor: palette.dominantColor.color.withOpacity(0.7),
|
statusBarColor: palette.dominantColor.color.withOpacity(0.7),
|
||||||
statusBarIconBrightness: ThemeData.estimateBrightnessForColor(palette.dominantColor.color.withOpacity(0.7)) == Brightness.light ? Brightness.dark : Brightness.light
|
statusBarIconBrightness: ThemeData.estimateBrightnessForColor(
|
||||||
));
|
palette.dominantColor.color.withOpacity(0.7)) ==
|
||||||
|
Brightness.light
|
||||||
|
? Brightness.dark
|
||||||
|
: Brightness.light));
|
||||||
setState(() => _bgGradient = LinearGradient(
|
setState(() => _bgGradient = LinearGradient(
|
||||||
begin: Alignment.topCenter,
|
begin: Alignment.topCenter,
|
||||||
end: Alignment.bottomCenter,
|
end: Alignment.bottomCenter,
|
||||||
colors: [palette.dominantColor.color.withOpacity(0.7), Color.fromARGB(0, 0, 0, 0)],
|
colors: [
|
||||||
stops: [
|
palette.dominantColor.color.withOpacity(0.7),
|
||||||
0.0,
|
Color.fromARGB(0, 0, 0, 0)
|
||||||
0.6
|
],
|
||||||
]
|
stops: [
|
||||||
));
|
0.0,
|
||||||
|
0.6
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,13 +113,11 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
if (_mediaItemSub != null)
|
if (_mediaItemSub != null) _mediaItemSub.cancel();
|
||||||
_mediaItemSub.cancel();
|
|
||||||
//Fix bottom buttons
|
//Fix bottom buttons
|
||||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||||
systemNavigationBarColor: settings.themeData.bottomAppBarColor,
|
systemNavigationBarColor: settings.themeData.bottomAppBarColor,
|
||||||
statusBarColor: Colors.transparent
|
statusBarColor: Colors.transparent));
|
||||||
));
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,57 +127,57 @@ class _PlayerScreenState extends State<PlayerScreen> {
|
||||||
ScreenUtil.init(context, allowFontScaling: true);
|
ScreenUtil.init(context, allowFontScaling: true);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: settings.blurPlayerBackground ? null : _bgGradient
|
gradient:
|
||||||
),
|
settings.blurPlayerBackground ? null : _bgGradient),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
if (settings.blurPlayerBackground && _blurImage != null)
|
if (settings.blurPlayerBackground && _blurImage != null)
|
||||||
ClipRect(
|
ClipRect(
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
image: _blurImage,
|
image: _blurImage,
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
colorFilter: ColorFilter.mode(Colors.black.withOpacity(0.25), BlendMode.dstATop)
|
colorFilter: ColorFilter.mode(
|
||||||
)
|
Colors.black.withOpacity(0.25),
|
||||||
|
BlendMode.dstATop))),
|
||||||
|
child: BackdropFilter(
|
||||||
|
filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
|
||||||
|
child: Container(color: Colors.transparent),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
StreamBuilder(
|
||||||
|
stream: StreamZip([
|
||||||
|
AudioService.playbackStateStream,
|
||||||
|
AudioService.currentMediaItemStream
|
||||||
|
]),
|
||||||
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||||||
|
//When disconnected
|
||||||
|
if (AudioService.currentMediaItem == null) {
|
||||||
|
playerHelper.startService();
|
||||||
|
return Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OrientationBuilder(
|
||||||
|
builder: (context, orientation) {
|
||||||
|
//Landscape
|
||||||
|
if (orientation == Orientation.landscape) {
|
||||||
|
return PlayerScreenHorizontal();
|
||||||
|
}
|
||||||
|
//Portrait
|
||||||
|
return PlayerScreenVertical();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
child: BackdropFilter(
|
],
|
||||||
filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
|
))));
|
||||||
child: Container(color: Colors.transparent),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
StreamBuilder(
|
|
||||||
stream: StreamZip([AudioService.playbackStateStream, AudioService.currentMediaItemStream]),
|
|
||||||
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
|
||||||
|
|
||||||
//When disconnected
|
|
||||||
if (AudioService.currentMediaItem == null) {
|
|
||||||
playerHelper.startService();
|
|
||||||
return Center(child: CircularProgressIndicator(),);
|
|
||||||
}
|
|
||||||
|
|
||||||
return OrientationBuilder(
|
|
||||||
builder: (context, orientation) {
|
|
||||||
//Landscape
|
|
||||||
if (orientation == Orientation.landscape) {
|
|
||||||
return PlayerScreenHorizontal();
|
|
||||||
}
|
|
||||||
//Portrait
|
|
||||||
return PlayerScreenVertical();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,13 +197,13 @@ class _PlayerScreenHorizontalState extends State<PlayerScreenHorizontal> {
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.fromLTRB(16, 0, 16, 8),
|
padding: EdgeInsets.fromLTRB(16, 0, 16, 8),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: ScreenUtil().setWidth(500),
|
width: ScreenUtil().setWidth(500),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
BigAlbumArt(),
|
BigAlbumArt(),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
//Right side
|
//Right side
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
|
@ -203,41 +216,40 @@ class _PlayerScreenHorizontalState extends State<PlayerScreenHorizontal> {
|
||||||
padding: EdgeInsets.fromLTRB(8, 16, 8, 0),
|
padding: EdgeInsets.fromLTRB(8, 16, 8, 0),
|
||||||
child: Container(
|
child: Container(
|
||||||
child: PlayerScreenTopRow(
|
child: PlayerScreenTopRow(
|
||||||
textSize: ScreenUtil().setSp(24),
|
textSize: ScreenUtil().setSp(24),
|
||||||
iconSize: ScreenUtil().setSp(36),
|
iconSize: ScreenUtil().setSp(36),
|
||||||
textWidth: ScreenUtil().setWidth(350),
|
textWidth: ScreenUtil().setWidth(350),
|
||||||
short: true
|
short: true),
|
||||||
),
|
)),
|
||||||
)
|
|
||||||
),
|
|
||||||
Column(
|
Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
height: ScreenUtil().setSp(50),
|
height: ScreenUtil().setSp(50),
|
||||||
child: AudioService.currentMediaItem.displayTitle.length >= 22 ?
|
child: AudioService
|
||||||
Marquee(
|
.currentMediaItem.displayTitle.length >=
|
||||||
text: AudioService.currentMediaItem.displayTitle,
|
22
|
||||||
style: TextStyle(
|
? Marquee(
|
||||||
fontSize: ScreenUtil().setSp(40),
|
text: AudioService.currentMediaItem.displayTitle,
|
||||||
fontWeight: FontWeight.bold
|
style: TextStyle(
|
||||||
),
|
fontSize: ScreenUtil().setSp(40),
|
||||||
blankSpace: 32.0,
|
fontWeight: FontWeight.bold),
|
||||||
startPadding: 10.0,
|
blankSpace: 32.0,
|
||||||
accelerationDuration: Duration(seconds: 1),
|
startPadding: 10.0,
|
||||||
pauseAfterRound: Duration(seconds: 2),
|
accelerationDuration: Duration(seconds: 1),
|
||||||
):
|
pauseAfterRound: Duration(seconds: 2),
|
||||||
Text(
|
)
|
||||||
AudioService.currentMediaItem.displayTitle,
|
: Text(
|
||||||
maxLines: 1,
|
AudioService.currentMediaItem.displayTitle,
|
||||||
overflow: TextOverflow.ellipsis,
|
maxLines: 1,
|
||||||
style: TextStyle(
|
overflow: TextOverflow.ellipsis,
|
||||||
fontSize: ScreenUtil().setSp(40),
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.bold
|
fontSize: ScreenUtil().setSp(40),
|
||||||
),
|
fontWeight: FontWeight.bold),
|
||||||
)
|
)),
|
||||||
|
Container(
|
||||||
|
height: 4,
|
||||||
),
|
),
|
||||||
Container(height: 4,),
|
|
||||||
Text(
|
Text(
|
||||||
AudioService.currentMediaItem.displaySubtitle ?? '',
|
AudioService.currentMediaItem.displaySubtitle ?? '',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
|
@ -264,11 +276,13 @@ class _PlayerScreenHorizontalState extends State<PlayerScreenHorizontal> {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.subtitles, size: ScreenUtil().setWidth(32)),
|
icon: Icon(Icons.subtitles,
|
||||||
|
size: ScreenUtil().setWidth(32)),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).push(MaterialPageRoute(
|
Navigator.of(context).push(MaterialPageRoute(
|
||||||
builder: (context) => LyricsScreen(trackId: AudioService.currentMediaItem.id)
|
builder: (context) => LyricsScreen(
|
||||||
));
|
trackId:
|
||||||
|
AudioService.currentMediaItem.id)));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
QualityInfoWidget(),
|
QualityInfoWidget(),
|
||||||
|
|
@ -276,8 +290,7 @@ class _PlayerScreenHorizontalState extends State<PlayerScreenHorizontal> {
|
||||||
PlayerMenuButton()
|
PlayerMenuButton()
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
))
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -286,8 +299,6 @@ class _PlayerScreenHorizontalState extends State<PlayerScreenHorizontal> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Portrait
|
//Portrait
|
||||||
class PlayerScreenVertical extends StatefulWidget {
|
class PlayerScreenVertical extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
|
|
@ -302,9 +313,8 @@ class _PlayerScreenVerticalState extends State<PlayerScreenVertical> {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.fromLTRB(30, 4, 16, 0),
|
padding: EdgeInsets.fromLTRB(30, 4, 16, 0),
|
||||||
child: PlayerScreenTopRow()
|
child: PlayerScreenTopRow()),
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.fromLTRB(16, 0, 16, 0),
|
padding: EdgeInsets.fromLTRB(16, 0, 16, 0),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|
@ -321,30 +331,29 @@ class _PlayerScreenVerticalState extends State<PlayerScreenVertical> {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
height: ScreenUtil().setSp(80),
|
height: ScreenUtil().setSp(80),
|
||||||
child: AudioService.currentMediaItem.displayTitle.length >= 26 ?
|
child: AudioService.currentMediaItem.displayTitle.length >= 26
|
||||||
Marquee(
|
? Marquee(
|
||||||
text: AudioService.currentMediaItem.displayTitle,
|
text: AudioService.currentMediaItem.displayTitle,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: ScreenUtil().setSp(64),
|
fontSize: ScreenUtil().setSp(64),
|
||||||
fontWeight: FontWeight.bold
|
fontWeight: FontWeight.bold),
|
||||||
),
|
blankSpace: 32.0,
|
||||||
blankSpace: 32.0,
|
startPadding: 10.0,
|
||||||
startPadding: 10.0,
|
accelerationDuration: Duration(seconds: 1),
|
||||||
accelerationDuration: Duration(seconds: 1),
|
pauseAfterRound: Duration(seconds: 2),
|
||||||
pauseAfterRound: Duration(seconds: 2),
|
)
|
||||||
):
|
: Text(
|
||||||
Text(
|
AudioService.currentMediaItem.displayTitle,
|
||||||
AudioService.currentMediaItem.displayTitle,
|
maxLines: 1,
|
||||||
maxLines: 1,
|
overflow: TextOverflow.ellipsis,
|
||||||
overflow: TextOverflow.ellipsis,
|
style: TextStyle(
|
||||||
style: TextStyle(
|
fontSize: ScreenUtil().setSp(64),
|
||||||
fontSize: ScreenUtil().setSp(64),
|
fontWeight: FontWeight.bold),
|
||||||
fontWeight: FontWeight.bold
|
)),
|
||||||
),
|
Container(
|
||||||
)
|
height: 4,
|
||||||
),
|
),
|
||||||
Container(height: 4,),
|
|
||||||
Text(
|
Text(
|
||||||
AudioService.currentMediaItem.displaySubtitle ?? '',
|
AudioService.currentMediaItem.displaySubtitle ?? '',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
|
|
@ -370,13 +379,13 @@ class _PlayerScreenVerticalState extends State<PlayerScreenVertical> {
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
//Fix bottom buttons
|
//Fix bottom buttons
|
||||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||||
systemNavigationBarColor: settings.themeData.bottomAppBarColor,
|
systemNavigationBarColor:
|
||||||
statusBarColor: Colors.transparent
|
settings.themeData.bottomAppBarColor,
|
||||||
));
|
statusBarColor: Colors.transparent));
|
||||||
|
|
||||||
await Navigator.of(context).push(MaterialPageRoute(
|
await Navigator.of(context).push(MaterialPageRoute(
|
||||||
builder: (context) => LyricsScreen(trackId: AudioService.currentMediaItem.id)
|
builder: (context) => LyricsScreen(
|
||||||
));
|
trackId: AudioService.currentMediaItem.id)));
|
||||||
|
|
||||||
updateColor();
|
updateColor();
|
||||||
},
|
},
|
||||||
|
|
@ -398,14 +407,14 @@ class QualityInfoWidget extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _QualityInfoWidgetState extends State<QualityInfoWidget> {
|
class _QualityInfoWidgetState extends State<QualityInfoWidget> {
|
||||||
|
|
||||||
String value = '';
|
String value = '';
|
||||||
StreamSubscription streamSubscription;
|
StreamSubscription streamSubscription;
|
||||||
|
|
||||||
//Load data from native
|
//Load data from native
|
||||||
void _load() async {
|
void _load() async {
|
||||||
if (AudioService.currentMediaItem == null) return;
|
if (AudioService.currentMediaItem == null) return;
|
||||||
Map data = await DownloadManager.platform.invokeMethod("getStreamInfo", {"id": AudioService.currentMediaItem.id});
|
Map data = await DownloadManager.platform.invokeMethod(
|
||||||
|
"getStreamInfo", {"id": AudioService.currentMediaItem.id});
|
||||||
//N/A
|
//N/A
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
setState(() => value = '');
|
setState(() => value = '');
|
||||||
|
|
@ -418,7 +427,8 @@ class _QualityInfoWidgetState extends State<QualityInfoWidget> {
|
||||||
//Update
|
//Update
|
||||||
StreamQualityInfo info = StreamQualityInfo.fromJson(data);
|
StreamQualityInfo info = StreamQualityInfo.fromJson(data);
|
||||||
setState(() {
|
setState(() {
|
||||||
value = '${info.format} ${info.bitrate(AudioService.currentMediaItem.duration)}kbps';
|
value =
|
||||||
|
'${info.format} ${info.bitrate(AudioService.currentMediaItem.duration)}kbps';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -426,7 +436,8 @@ class _QualityInfoWidgetState extends State<QualityInfoWidget> {
|
||||||
void initState() {
|
void initState() {
|
||||||
_load();
|
_load();
|
||||||
if (streamSubscription == null)
|
if (streamSubscription == null)
|
||||||
streamSubscription = AudioService.currentMediaItemStream.listen((event) async {
|
streamSubscription =
|
||||||
|
AudioService.currentMediaItemStream.listen((event) async {
|
||||||
_load();
|
_load();
|
||||||
});
|
});
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
@ -434,8 +445,7 @@ class _QualityInfoWidgetState extends State<QualityInfoWidget> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
if (streamSubscription != null)
|
if (streamSubscription != null) streamSubscription.cancel();
|
||||||
streamSubscription.cancel();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -444,13 +454,13 @@ class _QualityInfoWidgetState extends State<QualityInfoWidget> {
|
||||||
return TextButton(
|
return TextButton(
|
||||||
child: Text(value),
|
child: Text(value),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).push(MaterialPageRoute(builder: (context) => QualitySettings()));
|
Navigator.of(context)
|
||||||
|
.push(MaterialPageRoute(builder: (context) => QualitySettings()));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PlayerMenuButton extends StatelessWidget {
|
class PlayerMenuButton extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -465,50 +475,37 @@ class PlayerMenuButton extends StatelessWidget {
|
||||||
m.defaultTrackMenu(t, options: [m.sleepTimer(), m.wakelock()]);
|
m.defaultTrackMenu(t, options: [m.sleepTimer(), m.wakelock()]);
|
||||||
else
|
else
|
||||||
m.defaultShowEpisodeMenu(
|
m.defaultShowEpisodeMenu(
|
||||||
Show.fromJson(jsonDecode(AudioService.currentMediaItem.extras['show'])),
|
Show.fromJson(
|
||||||
ShowEpisode.fromMediaItem(AudioService.currentMediaItem),
|
jsonDecode(AudioService.currentMediaItem.extras['show'])),
|
||||||
options: [m.sleepTimer(), m.wakelock()]
|
ShowEpisode.fromMediaItem(AudioService.currentMediaItem),
|
||||||
);
|
options: [m.sleepTimer(), m.wakelock()]);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RepeatButton extends StatefulWidget {
|
class RepeatButton extends StatefulWidget {
|
||||||
|
|
||||||
final double iconSize;
|
final double iconSize;
|
||||||
RepeatButton(this.iconSize, {Key key}): super(key: key);
|
RepeatButton(this.iconSize, {Key key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_RepeatButtonState createState() => _RepeatButtonState();
|
_RepeatButtonState createState() => _RepeatButtonState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RepeatButtonState extends State<RepeatButton> {
|
class _RepeatButtonState extends State<RepeatButton> {
|
||||||
|
|
||||||
Icon get repeatIcon {
|
Icon get repeatIcon {
|
||||||
switch (playerHelper.repeatType) {
|
switch (playerHelper.repeatType) {
|
||||||
case LoopMode.off:
|
case LoopMode.off:
|
||||||
return Icon(
|
return Icon(Icons.repeat, size: widget.iconSize);
|
||||||
Icons.repeat,
|
|
||||||
size: widget.iconSize
|
|
||||||
);
|
|
||||||
case LoopMode.all:
|
case LoopMode.all:
|
||||||
return Icon(
|
return Icon(Icons.repeat,
|
||||||
Icons.repeat,
|
color: Theme.of(context).primaryColor, size: widget.iconSize);
|
||||||
color: Theme.of(context).primaryColor,
|
|
||||||
size: widget.iconSize
|
|
||||||
);
|
|
||||||
case LoopMode.one:
|
case LoopMode.one:
|
||||||
return Icon(
|
return Icon(Icons.repeat_one,
|
||||||
Icons.repeat_one,
|
color: Theme.of(context).primaryColor, size: widget.iconSize);
|
||||||
color: Theme.of(context).primaryColor,
|
|
||||||
size: widget.iconSize
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
|
|
@ -521,20 +518,18 @@ class _RepeatButtonState extends State<RepeatButton> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PlaybackControls extends StatefulWidget {
|
class PlaybackControls extends StatefulWidget {
|
||||||
|
|
||||||
final double iconSize;
|
final double iconSize;
|
||||||
PlaybackControls(this.iconSize, {Key key}): super(key: key);
|
PlaybackControls(this.iconSize, {Key key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_PlaybackControlsState createState() => _PlaybackControlsState();
|
_PlaybackControlsState createState() => _PlaybackControlsState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PlaybackControlsState extends State<PlaybackControls> {
|
class _PlaybackControlsState extends State<PlaybackControls> {
|
||||||
|
|
||||||
Icon get libraryIcon {
|
Icon get libraryIcon {
|
||||||
if (cache.checkTrackFavorite(Track.fromMediaItem(AudioService.currentMediaItem))) {
|
if (cache.checkTrackFavorite(
|
||||||
|
Track.fromMediaItem(AudioService.currentMediaItem))) {
|
||||||
return Icon(Icons.favorite, size: widget.iconSize * 0.64);
|
return Icon(Icons.favorite, size: widget.iconSize * 0.64);
|
||||||
}
|
}
|
||||||
return Icon(Icons.favorite_border, size: widget.iconSize * 0.64);
|
return Icon(Icons.favorite_border, size: widget.iconSize * 0.64);
|
||||||
|
|
@ -549,32 +544,37 @@ class _PlaybackControlsState extends State<PlaybackControls> {
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.sentiment_very_dissatisfied, size: ScreenUtil().setWidth(46)),
|
icon: Icon(Icons.sentiment_very_dissatisfied,
|
||||||
|
size: ScreenUtil().setWidth(46)),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await deezerAPI.dislikeTrack(AudioService.currentMediaItem.id);
|
await deezerAPI.dislikeTrack(AudioService.currentMediaItem.id);
|
||||||
if (playerHelper.queueIndex < (AudioService.queue??[]).length - 1) {
|
if (playerHelper.queueIndex <
|
||||||
|
(AudioService.queue ?? []).length - 1) {
|
||||||
AudioService.skipToNext();
|
AudioService.skipToNext();
|
||||||
}
|
}
|
||||||
}
|
}),
|
||||||
),
|
|
||||||
PrevNextButton(widget.iconSize, prev: true),
|
PrevNextButton(widget.iconSize, prev: true),
|
||||||
PlayPauseButton(widget.iconSize * 1.25),
|
PlayPauseButton(widget.iconSize * 1.25),
|
||||||
PrevNextButton(widget.iconSize),
|
PrevNextButton(widget.iconSize),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: libraryIcon,
|
icon: libraryIcon,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (cache.libraryTracks == null)
|
if (cache.libraryTracks == null) cache.libraryTracks = [];
|
||||||
cache.libraryTracks = [];
|
|
||||||
|
|
||||||
if (cache.checkTrackFavorite(Track.fromMediaItem(AudioService.currentMediaItem))) {
|
if (cache.checkTrackFavorite(
|
||||||
|
Track.fromMediaItem(AudioService.currentMediaItem))) {
|
||||||
//Remove from library
|
//Remove from library
|
||||||
setState(() => cache.libraryTracks.remove(AudioService.currentMediaItem.id));
|
setState(() => cache.libraryTracks
|
||||||
await deezerAPI.removeFavorite(AudioService.currentMediaItem.id);
|
.remove(AudioService.currentMediaItem.id));
|
||||||
|
await deezerAPI
|
||||||
|
.removeFavorite(AudioService.currentMediaItem.id);
|
||||||
await cache.save();
|
await cache.save();
|
||||||
} else {
|
} else {
|
||||||
//Add
|
//Add
|
||||||
setState(() => cache.libraryTracks.add(AudioService.currentMediaItem.id));
|
setState(() =>
|
||||||
await deezerAPI.addFavoriteTrack(AudioService.currentMediaItem.id);
|
cache.libraryTracks.add(AudioService.currentMediaItem.id));
|
||||||
|
await deezerAPI
|
||||||
|
.addFavoriteTrack(AudioService.currentMediaItem.id);
|
||||||
await cache.save();
|
await cache.save();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -585,14 +585,12 @@ class _PlaybackControlsState extends State<PlaybackControls> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class BigAlbumArt extends StatefulWidget {
|
class BigAlbumArt extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
_BigAlbumArtState createState() => _BigAlbumArtState();
|
_BigAlbumArtState createState() => _BigAlbumArtState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BigAlbumArtState extends State<BigAlbumArt> {
|
class _BigAlbumArtState extends State<BigAlbumArt> {
|
||||||
|
|
||||||
PageController _pageController = PageController(
|
PageController _pageController = PageController(
|
||||||
initialPage: playerHelper.queueIndex,
|
initialPage: playerHelper.queueIndex,
|
||||||
);
|
);
|
||||||
|
|
@ -603,7 +601,8 @@ class _BigAlbumArtState extends State<BigAlbumArt> {
|
||||||
void initState() {
|
void initState() {
|
||||||
_currentItemSub = AudioService.currentMediaItemStream.listen((event) async {
|
_currentItemSub = AudioService.currentMediaItemStream.listen((event) async {
|
||||||
_animationLock = true;
|
_animationLock = true;
|
||||||
await _pageController.animateToPage(playerHelper.queueIndex, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
|
await _pageController.animateToPage(playerHelper.queueIndex,
|
||||||
|
duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
|
||||||
_animationLock = false;
|
_animationLock = false;
|
||||||
});
|
});
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
@ -611,8 +610,7 @@ class _BigAlbumArtState extends State<BigAlbumArt> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
if (_currentItemSub != null)
|
if (_currentItemSub != null) _currentItemSub.cancel();
|
||||||
_currentItemSub.cancel();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -634,7 +632,8 @@ class _BigAlbumArtState extends State<BigAlbumArt> {
|
||||||
if (_animationLock) return;
|
if (_animationLock) return;
|
||||||
AudioService.skipToQueueItem(AudioService.queue[index].id);
|
AudioService.skipToQueueItem(AudioService.queue[index].id);
|
||||||
},
|
},
|
||||||
children: List.generate(AudioService.queue.length, (i) => ZoomableImage(url: AudioService.queue[i].artUri)),
|
children: List.generate(AudioService.queue.length,
|
||||||
|
(i) => ZoomableImage(url: AudioService.queue[i].artUri)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -642,12 +641,12 @@ class _BigAlbumArtState extends State<BigAlbumArt> {
|
||||||
|
|
||||||
//Top row containing QueueSource, queue...
|
//Top row containing QueueSource, queue...
|
||||||
class PlayerScreenTopRow extends StatelessWidget {
|
class PlayerScreenTopRow extends StatelessWidget {
|
||||||
|
|
||||||
final double textSize;
|
final double textSize;
|
||||||
final double iconSize;
|
final double iconSize;
|
||||||
final double textWidth;
|
final double textWidth;
|
||||||
final bool short;
|
final bool short;
|
||||||
PlayerScreenTopRow({this.textSize, this.iconSize, this.textWidth, this.short});
|
PlayerScreenTopRow(
|
||||||
|
{this.textSize, this.iconSize, this.textWidth, this.short});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -657,29 +656,31 @@ class PlayerScreenTopRow extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
width: this.textWidth??ScreenUtil().setWidth(800),
|
width: this.textWidth ?? ScreenUtil().setWidth(800),
|
||||||
child: Text(
|
child: Text(
|
||||||
(short??false)?(playerHelper.queueSource.text??''):'Playing from:'.i18n + ' ' + (playerHelper.queueSource?.text??''),
|
(short ?? false)
|
||||||
|
? (playerHelper.queueSource.text ?? '')
|
||||||
|
: 'Playing from:'.i18n +
|
||||||
|
' ' +
|
||||||
|
(playerHelper.queueSource?.text ?? ''),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
style: TextStyle(fontSize: this.textSize??ScreenUtil().setSp(38)),
|
style: TextStyle(fontSize: this.textSize ?? ScreenUtil().setSp(38)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.menu),
|
icon: Icon(Icons.menu),
|
||||||
iconSize: this.iconSize??ScreenUtil().setSp(52),
|
iconSize: this.iconSize ?? ScreenUtil().setSp(52),
|
||||||
splashRadius: this.iconSize??ScreenUtil().setWidth(52),
|
splashRadius: this.iconSize ?? ScreenUtil().setWidth(52),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
//Fix bottom buttons
|
//Fix bottom buttons
|
||||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||||
systemNavigationBarColor: settings.themeData.bottomAppBarColor,
|
systemNavigationBarColor: settings.themeData.bottomAppBarColor,
|
||||||
statusBarColor: Colors.transparent
|
statusBarColor: Colors.transparent));
|
||||||
));
|
|
||||||
//Navigate
|
//Navigate
|
||||||
await Navigator.of(context).push(MaterialPageRoute(
|
await Navigator.of(context)
|
||||||
builder: (context) => QueueScreen()
|
.push(MaterialPageRoute(builder: (context) => QueueScreen()));
|
||||||
));
|
|
||||||
//Fix colors
|
//Fix colors
|
||||||
updateColor();
|
updateColor();
|
||||||
},
|
},
|
||||||
|
|
@ -689,22 +690,21 @@ class PlayerScreenTopRow extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SeekBar extends StatefulWidget {
|
class SeekBar extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
_SeekBarState createState() => _SeekBarState();
|
_SeekBarState createState() => _SeekBarState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SeekBarState extends State<SeekBar> {
|
class _SeekBarState extends State<SeekBar> {
|
||||||
|
|
||||||
bool _seeking = false;
|
bool _seeking = false;
|
||||||
double _pos;
|
double _pos;
|
||||||
|
|
||||||
double get position {
|
double get position {
|
||||||
if (_seeking) return _pos;
|
if (_seeking) return _pos;
|
||||||
if (AudioService.playbackState == null) return 0.0;
|
if (AudioService.playbackState == null) return 0.0;
|
||||||
double p = AudioService.playbackState.currentPosition.inMilliseconds.toDouble()??0.0;
|
double p =
|
||||||
|
AudioService.playbackState.currentPosition.inMilliseconds.toDouble() ??
|
||||||
|
0.0;
|
||||||
if (p > duration) return duration;
|
if (p > duration) return duration;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
@ -735,15 +735,11 @@ class _SeekBarState extends State<SeekBar> {
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
_timeString(position),
|
_timeString(position),
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: ScreenUtil().setSp(35)),
|
||||||
fontSize: ScreenUtil().setSp(35)
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
_timeString(duration),
|
_timeString(duration),
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: ScreenUtil().setSp(35)),
|
||||||
fontSize: ScreenUtil().setSp(35)
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -751,7 +747,10 @@ class _SeekBarState extends State<SeekBar> {
|
||||||
Container(
|
Container(
|
||||||
height: 32.0,
|
height: 32.0,
|
||||||
child: Slider(
|
child: Slider(
|
||||||
focusNode: FocusNode(canRequestFocus: false, skipTraversal: true), // Don't focus on Slider - it doesn't work (and not needed)
|
focusNode: FocusNode(
|
||||||
|
canRequestFocus: false,
|
||||||
|
skipTraversal:
|
||||||
|
true), // Don't focus on Slider - it doesn't work (and not needed)
|
||||||
value: position,
|
value: position,
|
||||||
max: duration,
|
max: duration,
|
||||||
onChangeStart: (double d) {
|
onChangeStart: (double d) {
|
||||||
|
|
@ -787,19 +786,19 @@ class QueueScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _QueueScreenState extends State<QueueScreen> {
|
class _QueueScreenState extends State<QueueScreen> {
|
||||||
|
|
||||||
StreamSubscription _queueSub;
|
StreamSubscription _queueSub;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
_queueSub = AudioService.queueStream.listen((event) {setState((){});});
|
_queueSub = AudioService.queueStream.listen((event) {
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
if (_queueSub != null)
|
if (_queueSub != null) _queueSub.cancel();
|
||||||
_queueSub.cancel();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -820,32 +819,33 @@ class _QueueScreenState extends State<QueueScreen> {
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ReorderableListView(
|
body: ReorderableListView.builder(
|
||||||
onReorder: (int oldIndex, int newIndex) async {
|
onReorder: (int oldIndex, int newIndex) {
|
||||||
if (oldIndex == playerHelper.queueIndex) return;
|
if (oldIndex == playerHelper.queueIndex) return;
|
||||||
await playerHelper.reorder(oldIndex, newIndex);
|
playerHelper
|
||||||
setState(() {});
|
.reorder(oldIndex, newIndex)
|
||||||
|
.then((value) => setState(() => null));
|
||||||
},
|
},
|
||||||
children: List.generate(AudioService.queue.length, (int i) {
|
itemCount: AudioService.queue.length,
|
||||||
|
itemBuilder: (BuildContext context, int i) {
|
||||||
Track t = Track.fromMediaItem(AudioService.queue[i]);
|
Track t = Track.fromMediaItem(AudioService.queue[i]);
|
||||||
return TrackTile(
|
return TrackTile(
|
||||||
t,
|
t,
|
||||||
onTap: () async {
|
onTap: () {
|
||||||
pageViewLock = true;
|
pageViewLock = true;
|
||||||
await AudioService.skipToQueueItem(t.id);
|
AudioService.skipToQueueItem(t.id)
|
||||||
Navigator.of(context).pop();
|
.then((value) => Navigator.of(context).pop());
|
||||||
},
|
},
|
||||||
key: Key(i.toString()),
|
key: Key(i.toString()),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
icon: Icon(Icons.close),
|
icon: Icon(Icons.close),
|
||||||
onPressed: () async {
|
onPressed: () {
|
||||||
await AudioService.removeQueueItem(t.toMediaItem());
|
AudioService.removeQueueItem(t.toMediaItem())
|
||||||
setState(() {});
|
.then((value) => setState(() => null));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
},
|
||||||
)
|
));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue