Skip to content

Commit

Permalink
fixes & improvements ™
Browse files Browse the repository at this point in the history
- design tweaks
- tried to limit flickering when changing queue
- working with flutter 3.13.5, dart 3.1.2
  • Loading branch information
Chaphasilor committed Sep 24, 2023
1 parent cfe5432 commit 4a5693c
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 80 deletions.
3 changes: 2 additions & 1 deletion lib/components/PlayerScreen/player_buttons_more.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ class PlayerButtonsMore extends StatelessWidget {
Radius.circular(15),
),
),
icon: const Icon(
icon: Icon(
TablerIcons.menu_2,
color: IconTheme.of(context).color!,
),
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<PlayerButtonsMoreItems>>[
Expand Down
132 changes: 88 additions & 44 deletions lib/components/PlayerScreen/queue_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,10 @@ import 'queue_list_item.dart';
class _QueueListStreamState {
_QueueListStreamState(
this.mediaState,
this.playbackPosition,
this.queueInfo,
);

final MediaState mediaState;
final Duration playbackPosition;
final QueueInfo queueInfo;
}

Expand All @@ -59,7 +57,7 @@ class QueueList extends StatefulWidget {
void scrollDown() {
scrollController.animateTo(
scrollController.position.maxScrollExtent,
duration: Duration(seconds: 2),
duration: const Duration(seconds: 2),
curve: Curves.fastOutSlowIn,
);
}
Expand Down Expand Up @@ -398,6 +396,7 @@ class _PreviousTracksListState extends State<PreviousTracksList>
_previousTracks ??= snapshot.data!.previousTracks;

return SliverReorderableList(
autoScrollerVelocityScalar: 20.0,
onReorder: (oldIndex, newIndex) {
int draggingOffset = -(_previousTracks!.length - oldIndex);
int newPositionOffset = -(_previousTracks!.length - newIndex);
Expand All @@ -419,13 +418,21 @@ class _PreviousTracksListState extends State<PreviousTracksList>
// Feedback.forLongPress(context);
Vibrate.feedback(FeedbackType.selection);
},
findChildIndexCallback: (Key key) {
key = key as GlobalObjectKey;
final ValueKey<String> valueKey = key.value as ValueKey<String>;
// search from the back as this is probably more efficient for previous tracks
final index = _previousTracks!.lastIndexWhere((item) => item.id == valueKey.value);
if (index == -1) return null;
return index;
},
itemCount: _previousTracks?.length ?? 0,
itemBuilder: (context, index) {
final item = _previousTracks![index];
final actualIndex = index;
final indexOffset = -((_previousTracks?.length ?? 0) - index);
return QueueListItem(
key: ValueKey(_previousTracks![actualIndex].id),
key: ValueKey(item.id),
item: item,
listIndex: index,
actualIndex: actualIndex,
Expand Down Expand Up @@ -481,10 +488,10 @@ class _NextUpTracksListState extends State<NextUpTracksList> {
return SliverPadding(
padding: const EdgeInsets.only(top: 0.0, left: 8.0, right: 8.0),
sliver: SliverReorderableList(
autoScrollerVelocityScalar: 20.0,
onReorder: (oldIndex, newIndex) {
int draggingOffset = oldIndex + 1;
int newPositionOffset = newIndex + 1;
print("$draggingOffset -> $newPositionOffset");
if (mounted) {
Vibrate.feedback(FeedbackType.impact);
setState(() {
Expand All @@ -501,13 +508,20 @@ class _NextUpTracksListState extends State<NextUpTracksList> {
onReorderStart: (p0) {
Vibrate.feedback(FeedbackType.selection);
},
findChildIndexCallback: (Key key) {
key = key as GlobalObjectKey;
final ValueKey<String> valueKey = key.value as ValueKey<String>;
final index = _nextUp!.indexWhere((item) => item.id == valueKey.value);
if (index == -1) return null;
return index;
},
itemCount: _nextUp?.length ?? 0,
itemBuilder: (context, index) {
final item = _nextUp![index];
final actualIndex = index;
final indexOffset = index + 1;
return QueueListItem(
key: ValueKey(_nextUp![actualIndex].id),
key: ValueKey(item.id),
item: item,
listIndex: index,
actualIndex: actualIndex,
Expand Down Expand Up @@ -560,33 +574,42 @@ class _QueueTracksListState extends State<QueueTracksList> {
_nextUp ??= snapshot.data!.nextUp;

return SliverReorderableList(
autoScrollerVelocityScalar: 20.0,
onReorder: (oldIndex, newIndex) {
int draggingOffset = oldIndex + (_nextUp?.length ?? 0) + 1;
int newPositionOffset = newIndex + (_nextUp?.length ?? 0) + 1;
print("$draggingOffset -> $newPositionOffset");
if (mounted) {
// update external queue to commit changes, but don't await it
_queueService.reorderByOffset(
draggingOffset, newPositionOffset);
Vibrate.feedback(FeedbackType.impact);
setState(() {
// temporarily update internal queue
QueueItem tmp = _queue!.removeAt(oldIndex);
_queue!.insert(
newIndex < oldIndex ? newIndex : newIndex - 1, tmp);
// update external queue to commit changes, results in a rebuild
_queueService.reorderByOffset(
draggingOffset, newPositionOffset);
});
}
},
onReorderStart: (p0) {
Vibrate.feedback(FeedbackType.selection);
},
itemCount: _queue?.length ?? 0,
findChildIndexCallback: (Key key) {
key = key as GlobalObjectKey;
final ValueKey<String> valueKey = key.value as ValueKey<String>;
final index = _queue!.indexWhere((item) => item.id == valueKey.value);
if (index == -1) return null;
return index;
},
itemBuilder: (context, index) {
final item = _queue![index];
final actualIndex = index;
final indexOffset = index + _nextUp!.length + 1;

return QueueListItem(
key: ValueKey(_queue![actualIndex].id),
key: ValueKey(item.id),
item: item,
listIndex: index,
actualIndex: actualIndex,
Expand Down Expand Up @@ -642,18 +665,16 @@ class _CurrentTrackState extends State<CurrentTrack> {
Duration? playbackPosition;

return StreamBuilder<_QueueListStreamState>(
stream: Rx.combineLatest3<MediaState, Duration, QueueInfo,
stream: Rx.combineLatest2<MediaState, QueueInfo,
_QueueListStreamState>(
mediaStateStream,
AudioService.position
.startWith(_audioHandler.playbackState.value.position),
_queueService.getQueueStream(),
(a, b, c) => _QueueListStreamState(a, b, c)),
(a, b) => _QueueListStreamState(a, b)),
builder: (context, snapshot) {
if (snapshot.hasData) {
currentTrack = snapshot.data!.queueInfo.currentTrack;
mediaState = snapshot.data!.mediaState;
playbackPosition = snapshot.data!.playbackPosition;
// playbackPosition = snapshot.data!.playbackPosition;

jellyfin_models.BaseItemDto? baseItem = currentTrack!.item.extras?["itemJson"] == null
? null
Expand Down Expand Up @@ -725,7 +746,7 @@ class _CurrentTrackState extends State<CurrentTrack> {
TablerIcons.player_play,
size: 32,
),
color: Color.fromRGBO(255, 255, 255, 1.0),
color: Colors.white,
)),
],
),
Expand All @@ -736,25 +757,34 @@ class _CurrentTrackState extends State<CurrentTrack> {
left: 0,
top: 0,
// child: RepaintBoundary(
child: Container(
width: 298 *
(playbackPosition!.inMilliseconds /
(mediaState?.mediaItem?.duration ??
const Duration(seconds: 0))
.inMilliseconds),
height: 70.0,
decoration: ShapeDecoration(
// color: Color.fromRGBO(188, 136, 86, 0.75),
color: IconTheme.of(context).color!.withOpacity(0.75),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(8),
bottomRight: Radius.circular(8),
),
),
),
child: StreamBuilder<Duration>(
stream: AudioService.position
.startWith(_audioHandler.playbackState.value.position),
builder: (context, snapshot) {
if (snapshot.hasData) {
playbackPosition = snapshot.data;
return Container(
width: 298 *
(playbackPosition!.inMilliseconds /
(mediaState?.mediaItem?.duration ??
const Duration(seconds: 0))
.inMilliseconds),
height: 70.0,
decoration: ShapeDecoration(
color: IconTheme.of(context).color!.withOpacity(0.75),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(8),
bottomRight: Radius.circular(8),
),
),
),
);
} else {
return Container();
}
}
),
// ),
),
Row(
mainAxisSize: MainAxisSize.max,
Expand Down Expand Up @@ -795,18 +825,32 @@ class _CurrentTrackState extends State<CurrentTrack> {
overflow: TextOverflow.ellipsis),
),
Row(children: [
Text(
// '0:00',
playbackPosition!.inHours >= 1.0
? "${playbackPosition?.inHours.toString()}:${((playbackPosition?.inMinutes ?? 0) % 60).toString().padLeft(2, '0')}:${((playbackPosition?.inSeconds ?? 0) % 60).toString().padLeft(2, '0')}"
: "${playbackPosition?.inMinutes.toString()}:${((playbackPosition?.inSeconds ?? 0) % 60).toString().padLeft(2, '0')}",
style: TextStyle(
StreamBuilder<Duration>(
stream: AudioService.position
.startWith(_audioHandler.playbackState.value.position),
builder: (context, snapshot) {
final TextStyle style = TextStyle(
color: Colors.white.withOpacity(0.8),
fontSize: 14,
fontFamily: 'Lexend Deca',
fontWeight: FontWeight.w400,
),
),
);
if (snapshot.hasData) {
playbackPosition = snapshot.data;
return Text(
// '0:00',
playbackPosition!.inHours >= 1.0
? "${playbackPosition?.inHours.toString()}:${((playbackPosition?.inMinutes ?? 0) % 60).toString().padLeft(2, '0')}:${((playbackPosition?.inSeconds ?? 0) % 60).toString().padLeft(2, '0')}"
: "${playbackPosition?.inMinutes.toString()}:${((playbackPosition?.inSeconds ?? 0) % 60).toString().padLeft(2, '0')}",
style: style,
);
} else {
return Text(
"0:00",
style: style,
);
}
}),
const SizedBox(width: 2),
Text(
'/',
Expand Down Expand Up @@ -835,7 +879,7 @@ class _CurrentTrackState extends State<CurrentTrack> {
),
],)
],
)
),
],
),
// ),
Expand Down Expand Up @@ -904,7 +948,7 @@ class _CurrentTrackState extends State<CurrentTrack> {

void showSongMenu(QueueItem currentTrack) async {
final item = jellyfin_models.BaseItemDto.fromJson(
currentTrack?.item.extras?["itemJson"]);
currentTrack.item.extras?["itemJson"]);

final canGoToAlbum = _isAlbumDownloadedIfOffline(item.parentId);

Expand Down
42 changes: 20 additions & 22 deletions lib/components/PlayerScreen/queue_list_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import 'package:finamp/services/audio_service_helper.dart';
import 'package:finamp/services/downloads_helper.dart';
import 'package:finamp/services/finamp_settings_helper.dart';
import 'package:finamp/services/jellyfin_api_helper.dart';
import 'package:finamp/services/music_player_background_task.dart';
import 'package:finamp/services/process_artist.dart';
import 'package:flutter/material.dart' hide ReorderableList;
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
Expand All @@ -19,17 +18,17 @@ import 'package:flutter_vibrate/flutter_vibrate.dart';
import 'package:get_it/get_it.dart';

class QueueListItem extends StatefulWidget {
late QueueItem item;
late int listIndex;
late int actualIndex;
late int indexOffset;
late List<QueueItem> subqueue;
late bool isCurrentTrack;
late bool isPreviousTrack;
late bool allowReorder;
late void Function() onTap;

QueueListItem({
final QueueItem item;
final int listIndex;
final int actualIndex;
final int indexOffset;
final List<QueueItem> subqueue;
final bool isCurrentTrack;
final bool isPreviousTrack;
final bool allowReorder;
final void Function() onTap;

const QueueListItem({
Key? key,
required this.item,
required this.listIndex,
Expand All @@ -45,14 +44,18 @@ class QueueListItem extends StatefulWidget {
State<QueueListItem> createState() => _QueueListItemState();
}

class _QueueListItemState extends State<QueueListItem> {
final _audioHandler = GetIt.instance<MusicPlayerBackgroundTask>();
class _QueueListItemState extends State<QueueListItem> with AutomaticKeepAliveClientMixin {
final _audioServiceHelper = GetIt.instance<AudioServiceHelper>();
final _queueService = GetIt.instance<QueueService>();
final _jellyfinApiHelper = GetIt.instance<JellyfinApiHelper>();

@override
bool get wantKeepAlive => true;

@override
Widget build(BuildContext context) {
super.build(context);

return Dismissible(
key: Key(widget.item.id),
onDismissed: (direction) async {
Expand Down Expand Up @@ -89,11 +92,6 @@ class _QueueListItemState extends State<QueueListItem> {
widget.item.item.extras?["itemJson"]),
borderRadius: BorderRadius.zero,
),
// leading: Container(
// height: 60.0,
// width: 60.0,
// color: Colors.white,
// ),
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expand Down Expand Up @@ -149,7 +147,7 @@ class _QueueListItemState extends State<QueueListItem> {
margin: const EdgeInsets.only(right: 8.0),
padding: const EdgeInsets.only(right: 6.0),
// width: widget.allowReorder ? 145.0 : 115.0,
width: widget.allowReorder ? 70.0 : 35.0,
width: widget.allowReorder ? 70.0 : 40.0,
height: 50.0,
child: Row(
mainAxisSize: MainAxisSize.min,
Expand Down Expand Up @@ -189,9 +187,9 @@ class _QueueListItemState extends State<QueueListItem> {
if (widget.allowReorder)
ReorderableDragStartListener(
index: widget.listIndex,
child: Padding(
child: const Padding(
padding: EdgeInsets.only(bottom: 5.0, left: 6.0),
child: const Icon(
child: Icon(
TablerIcons.grip_horizontal,
color: Colors.white,
size: 28.0,
Expand Down
Loading

0 comments on commit 4a5693c

Please sign in to comment.