spotube/lib/pages/home/genres/genre_playlists.dart
2023-12-12 21:27:14 +06:00

172 lines
6.1 KiB
Dart

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotify/spotify.dart' hide Offset;
import 'package:spotube/collections/fake.dart';
import 'package:spotube/components/playlist/playlist_card.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/components/shared/page_window_title_bar.dart';
import 'package:spotube/components/shared/waypoint.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/services/queries/queries.dart';
import 'package:collection/collection.dart';
class GenrePlaylistsPage extends HookConsumerWidget {
final Category category;
const GenrePlaylistsPage({Key? key, required this.category})
: super(key: key);
@override
Widget build(BuildContext context, ref) {
final playlistsQuery = useQueries.category.playlistsOf(
ref,
category.id!,
);
final playlists = useMemoized(
() => playlistsQuery.pages.expand(
(page) {
return page.items?.whereNotNull() ??
const Iterable<PlaylistSimple>.empty();
},
).toList(),
[playlistsQuery.pages],
);
final mediaQuery = MediaQuery.of(context);
final scrollController = useScrollController();
return Scaffold(
appBar: const PageWindowTitleBar(
leading: BackButton(color: Colors.white),
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,
),
extendBodyBehindAppBar: true,
body: CustomScrollView(
controller: scrollController,
slivers: [
SliverAppBar(
automaticallyImplyLeading: false,
expandedHeight: mediaQuery.mdAndDown ? 200 : 150,
pinned: true,
floating: false,
title: const Text(""),
backgroundColor: Colors.brown.withOpacity(0.7),
flexibleSpace: FlexibleSpaceBar(
stretchModes: const [
StretchMode.zoomBackground,
StretchMode.blurBackground,
],
background: DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: UniversalImage.imageProvider(
category.icons!.first.url!,
),
fit: BoxFit.cover,
),
),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: const ColoredBox(color: Colors.transparent),
),
),
centerTitle: true,
title: Text(
category.name!,
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
color: Colors.white,
letterSpacing: 3,
shadows: [
const Shadow(
offset: Offset(-1.5, -1.5),
color: Colors.black54,
),
const Shadow(
offset: Offset(1.5, -1.5),
color: Colors.black54,
),
const Shadow(
offset: Offset(1.5, 1.5),
color: Colors.black54,
),
const Shadow(
offset: Offset(-1.5, 1.5),
color: Colors.black54,
),
],
),
),
collapseMode: CollapseMode.parallax,
),
),
const SliverGap(20),
SliverSafeArea(
top: false,
sliver: SliverPadding(
padding: EdgeInsets.symmetric(
horizontal: mediaQuery.mdAndDown ? 12 : 24,
),
sliver: playlists.isEmpty
? Skeletonizer.sliver(
child: SliverToBoxAdapter(
child: Wrap(
spacing: 12,
runSpacing: 12,
children: List.generate(
6,
(index) => PlaylistCard(FakeData.playlist),
),
),
),
)
: SliverGrid.builder(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 190,
mainAxisExtent: mediaQuery.mdAndDown ? 225 : 250,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
itemCount: playlists.length + 1,
itemBuilder: (context, index) {
final playlist = playlists.elementAtOrNull(index);
if (playlist == null) {
if (!playlistsQuery.hasNextPage) {
return const SizedBox.shrink();
}
return Skeletonizer(
enabled: true,
child: Waypoint(
controller: scrollController,
isGrid: true,
onTouchEdge: () async {
if (playlistsQuery.hasNextPage) {
await playlistsQuery.fetchNext();
}
},
child: PlaylistCard(FakeData.playlist),
),
);
}
return Skeleton.keep(
child: PlaylistCard(playlist),
);
},
),
),
),
const SliverGap(20),
],
),
);
}
}