207 lines
7.3 KiB
Dart
207 lines
7.3 KiB
Dart
import 'package:finamp/components/AlbumScreen/sync_album_or_playlist_button.dart';
|
|
import 'package:finamp/services/downloads_helper.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:finamp/l10n/app_localizations.dart';
|
|
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
|
|
import 'package:get_it/get_it.dart';
|
|
|
|
import '../../models/jellyfin_models.dart';
|
|
import '../../services/finamp_settings_helper.dart';
|
|
import '../../components/favourite_button.dart';
|
|
import 'album_screen_content_flexible_space_bar.dart';
|
|
import 'delete_button.dart';
|
|
import 'song_list_tile.dart';
|
|
import 'playlist_name_edit_button.dart';
|
|
|
|
typedef BaseItemDtoCallback = void Function(BaseItemDto item);
|
|
|
|
class AlbumScreenContent extends StatefulWidget {
|
|
const AlbumScreenContent({
|
|
Key? key,
|
|
required this.parent,
|
|
required this.children,
|
|
}) : super(key: key);
|
|
|
|
final BaseItemDto parent;
|
|
final List<BaseItemDto> children;
|
|
|
|
@override
|
|
State<AlbumScreenContent> createState() => _AlbumScreenContentState();
|
|
}
|
|
|
|
class _AlbumScreenContentState extends State<AlbumScreenContent> {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
void onDelete(BaseItemDto item) {
|
|
// This is pretty inefficient (has to search through whole list) but
|
|
// SongsSliverList gets passed some weird split version of children to
|
|
// handle multi-disc albums and it's 00:35 so I can't be bothered to get
|
|
// it to return an index
|
|
setState(() {
|
|
widget.children.remove(item);
|
|
});
|
|
}
|
|
|
|
List<List<BaseItemDto>> childrenPerDisc = [];
|
|
// if not in playlist, try splitting up tracks by disc numbers
|
|
// if first track has a disc number, let's assume the rest has it too
|
|
if (widget.parent.type != "Playlist" &&
|
|
widget.children[0].parentIndexNumber != null) {
|
|
int? lastDiscNumber;
|
|
for (var child in widget.children) {
|
|
if (child.parentIndexNumber != null &&
|
|
child.parentIndexNumber != lastDiscNumber) {
|
|
lastDiscNumber = child.parentIndexNumber;
|
|
childrenPerDisc.add([]);
|
|
}
|
|
childrenPerDisc.last.add(child);
|
|
}
|
|
}
|
|
|
|
return Scrollbar(
|
|
child: CustomScrollView(
|
|
slivers: [
|
|
SliverAppBar(
|
|
title: Text(widget.parent.name ??
|
|
AppLocalizations.of(context)!.unknownName),
|
|
// 125 + 64 is the total height of the widget we use as a
|
|
// FlexibleSpaceBar. We add the toolbar height since the widget
|
|
// should appear below the appbar.
|
|
// TODO: This height is affected by platform density.
|
|
expandedHeight: kToolbarHeight + 125 + 64,
|
|
pinned: true,
|
|
flexibleSpace: AlbumScreenContentFlexibleSpaceBar(
|
|
album: widget.parent,
|
|
items: widget.children,
|
|
),
|
|
actions: [
|
|
if (widget.parent.type == "Playlist" &&
|
|
!FinampSettingsHelper.finampSettings.isOffline)
|
|
PlaylistNameEditButton(playlist: widget.parent),
|
|
FavoriteButton(item: widget.parent),
|
|
if (GetIt.instance<DownloadsHelper>().isAlbumDownloaded(widget.parent.id))
|
|
DeleteButton(parent: widget.parent, items: widget.children),
|
|
if (!FinampSettingsHelper.finampSettings.isOffline)
|
|
SyncAlbumOrPlaylistButton(parent: widget.parent, items: widget.children)
|
|
],
|
|
),
|
|
if (widget.children.length > 1 &&
|
|
childrenPerDisc.length >
|
|
1) // show headers only for multi disc albums
|
|
for (var childrenOfThisDisc in childrenPerDisc)
|
|
SliverStickyHeader(
|
|
header: Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 16.0,
|
|
vertical: 16.0,
|
|
),
|
|
color: Theme.of(context).colorScheme.surfaceVariant,
|
|
child: Text(
|
|
AppLocalizations.of(context)!
|
|
.discNumber(childrenOfThisDisc[0].parentIndexNumber!),
|
|
style: const TextStyle(fontSize: 20.0),
|
|
),
|
|
),
|
|
sliver: SongsSliverList(
|
|
childrenForList: childrenOfThisDisc,
|
|
childrenForQueue: widget.children,
|
|
parent: widget.parent,
|
|
onDelete: onDelete,
|
|
),
|
|
)
|
|
else if (widget.children.isNotEmpty)
|
|
SongsSliverList(
|
|
childrenForList: widget.children,
|
|
childrenForQueue: widget.children,
|
|
parent: widget.parent,
|
|
onDelete: onDelete,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class SongsSliverList extends StatefulWidget {
|
|
const SongsSliverList({
|
|
Key? key,
|
|
required this.childrenForList,
|
|
required this.childrenForQueue,
|
|
required this.parent,
|
|
this.onDelete,
|
|
}) : super(key: key);
|
|
|
|
final List<BaseItemDto> childrenForList;
|
|
final List<BaseItemDto> childrenForQueue;
|
|
final BaseItemDto parent;
|
|
final BaseItemDtoCallback? onDelete;
|
|
|
|
@override
|
|
State<SongsSliverList> createState() => _SongsSliverListState();
|
|
}
|
|
|
|
class _SongsSliverListState extends State<SongsSliverList> {
|
|
final GlobalKey<SliverAnimatedListState> sliverListKey =
|
|
GlobalKey<SliverAnimatedListState>();
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// When user selects song from disc other than first, index number is
|
|
// incorrect and song with the same index on first disc is played instead.
|
|
// Adding this offset ensures playback starts for nth song on correct disc.
|
|
final int indexOffset =
|
|
widget.childrenForQueue.indexOf(widget.childrenForList[0]);
|
|
|
|
return SliverList(
|
|
delegate: SliverChildBuilderDelegate(
|
|
(BuildContext context, int index) {
|
|
final BaseItemDto item = widget.childrenForList[index];
|
|
|
|
BaseItemDto removeItem() {
|
|
late BaseItemDto item;
|
|
|
|
setState(() {
|
|
item = widget.childrenForList.removeAt(index);
|
|
});
|
|
|
|
return item;
|
|
}
|
|
|
|
return SongListTile(
|
|
item: item,
|
|
children: widget.childrenForQueue,
|
|
index: index + indexOffset,
|
|
parentId: widget.parent.id,
|
|
onDelete: () {
|
|
final item = removeItem();
|
|
if (widget.onDelete != null) {
|
|
widget.onDelete!(item);
|
|
}
|
|
},
|
|
isInPlaylist: widget.parent.type == "Playlist",
|
|
// show artists except for this one scenario
|
|
showArtists: !(
|
|
// we're on album screen
|
|
widget.parent.type == "MusicAlbum"
|
|
// "hide song artists if they're the same as album artists" == true
|
|
&&
|
|
FinampSettingsHelper
|
|
.finampSettings.hideSongArtistsIfSameAsAlbumArtists
|
|
// song artists == album artists
|
|
&&
|
|
setEquals(
|
|
widget.parent.albumArtists?.map((e) => e.name).toSet(),
|
|
item.artists?.toSet())),
|
|
);
|
|
},
|
|
childCount: widget.childrenForList.length,
|
|
),
|
|
);
|
|
}
|
|
} |