spotube/lib/pages/search/tabs/artists.dart
2025-07-13 15:11:56 +06:00

95 lines
3.5 KiB
Dart

import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_undraw/flutter_undraw.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotube/collections/fake.dart';
import 'package:spotube/components/waypoint.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/modules/artist/artist_card.dart';
import 'package:spotube/modules/search/loading.dart';
import 'package:spotube/pages/search/search.dart';
import 'package:spotube/provider/metadata_plugin/search/artists.dart';
class SearchPageArtistsTab extends HookConsumerWidget {
const SearchPageArtistsTab({super.key});
@override
Widget build(BuildContext context, ref) {
final controller = useScrollController();
final searchTerm = ref.watch(searchTermStateProvider);
final searchArtistsSnapshot =
ref.watch(metadataPluginSearchArtistsProvider(searchTerm));
final searchArtistsNotifier =
ref.read(metadataPluginSearchArtistsProvider(searchTerm).notifier);
final searchArtists = searchArtistsSnapshot.asData?.value.items ?? [];
return SearchPlaceholder(
snapshot: searchArtistsSnapshot,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: LayoutBuilder(builder: (context, constrains) {
if (searchArtistsSnapshot.hasValue && searchArtists.isEmpty) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
spacing: 10,
children: [
Undraw(
height: 200 * context.theme.scaling,
illustration: UndrawIllustration.taken,
color: Theme.of(context).colorScheme.primary,
),
Text(
context.l10n.nothing_found,
textAlign: TextAlign.center,
).muted().small()
],
),
);
}
return GridView.builder(
padding: const EdgeInsets.all(16),
itemCount: searchArtists.length + 1,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
mainAxisExtent: constrains.smAndDown ? 225 : 250,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemBuilder: (context, index) {
if (searchArtists.isNotEmpty && index == searchArtists.length) {
if (searchArtistsSnapshot.asData?.value.hasMore != true) {
return const SizedBox.shrink();
}
return Waypoint(
controller: controller,
isGrid: true,
onTouchEdge: searchArtistsNotifier.fetchMore,
child: Skeletonizer(
enabled: true,
child: ArtistCard(FakeData.artist),
),
);
}
return Skeletonizer(
enabled: searchArtistsSnapshot.isLoading,
child: ArtistCard(
searchArtists.elementAtOrNull(index) ?? FakeData.artist,
),
);
},
);
}),
),
);
}
}