Basic track search

This commit is contained in:
LittleSheep 2024-08-28 21:07:58 +08:00
parent 81f5a2f5cc
commit 3f41573f00
7 changed files with 141 additions and 1 deletions

View File

@ -5,6 +5,14 @@ Yet another spotify third-party client. Support multi-platform because built wit
This project is inspired by and taken supported by [spotube](https://spotube.krtirtho.dev).
Their original app is good enough. But I just want to redesign the user interface and make it ready add to more features and more backend support.
## Roadmap
- [x] Playing music
- [ ] Add netease music as source
- [x] Re-design user interface
- [x] Simplified UI and UX
- [ ] Support for large screen device
## License
This project is open-sourced under APGLv3 license. The original spotube project is open-sourced under license BSD-Clause4 and copyright by Kingkor Roy Tirtho.

View File

@ -5,6 +5,7 @@ import 'package:rhythm_box/screens/explore.dart';
import 'package:rhythm_box/screens/player/lyrics.dart';
import 'package:rhythm_box/screens/player/view.dart';
import 'package:rhythm_box/screens/playlist/view.dart';
import 'package:rhythm_box/screens/search/view.dart';
import 'package:rhythm_box/screens/settings.dart';
import 'package:rhythm_box/shells/nav_shell.dart';
@ -17,6 +18,11 @@ final router = GoRouter(routes: [
name: 'explore',
builder: (context, state) => const ExploreScreen(),
),
GoRoute(
path: '/search',
name: 'search',
builder: (context, state) => const SearchScreen(),
),
GoRoute(
path: '/playlist/:id',
name: 'playlistView',

View File

@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:rhythm_box/providers/spotify.dart';
import 'package:rhythm_box/providers/user_preferences.dart';
import 'package:rhythm_box/widgets/tracks/track_list.dart';
import 'package:spotify/spotify.dart';
class SearchScreen extends StatefulWidget {
const SearchScreen({super.key});
@override
State<SearchScreen> createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen> {
late final SpotifyProvider _spotify = Get.find();
bool _isLoading = false;
String? _searchTerm;
List<dynamic>? _searchResult;
Future<void> _search(String? term) async {
if (term != null) {
_searchTerm = term.trim();
}
if (_searchTerm == null) {
return;
}
setState(() => _isLoading = true);
final prefs = Get.find<UserPreferencesProvider>().state.value;
_searchResult = (await _spotify.api.search
.get(_searchTerm!, types: [SearchType.track], market: prefs.market)
.getPage(20))
.mapMany((x) => x.items)
.toList();
setState(() => _isLoading = false);
}
@override
Widget build(BuildContext context) {
return Material(
color: Theme.of(context).colorScheme.surface,
child: SafeArea(
child: Column(
children: [
SearchBar(
padding: const WidgetStatePropertyAll<EdgeInsets>(
EdgeInsets.symmetric(horizontal: 16.0),
),
onSubmitted: (value) {
if (_isLoading) return;
_search(value);
},
leading: const Icon(Icons.search),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
).paddingSymmetric(horizontal: 24, vertical: 8),
Expanded(
child: CustomScrollView(
slivers: [
if (_searchResult != null)
TrackSliverList(tracks: List<Track>.from(_searchResult!)),
],
),
),
],
),
),
);
}
}

View File

@ -24,7 +24,8 @@ class _NavShellState extends State<NavShell> {
final List<Destination> _allDestinations = <Destination>[
Destination('explore'.tr, 'explore', Icons.explore),
Destination('settings'.tr, 'settings', Icons.settings)
Destination('search'.tr, 'search', Icons.search),
Destination('settings'.tr, 'settings', Icons.settings),
];
@override

View File

@ -2,4 +2,5 @@ const i18nEnglish = {
'appName': 'RhythmBox',
'explore': 'Explore',
'settings': 'Settings',
'search': 'Search',
};

View File

@ -2,4 +2,5 @@ const i18nSimplifiedChinese = {
'appName': '韵律盒',
'explore': '探索',
'settings': '设置',
'search': '搜索',
};

View File

@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:rhythm_box/providers/audio_player.dart';
import 'package:rhythm_box/widgets/auto_cache_image.dart';
import 'package:spotify/spotify.dart';
import 'package:rhythm_box/services/artist.dart';
class TrackSliverList extends StatelessWidget {
final List<Track> tracks;
const TrackSliverList({
super.key,
required this.tracks,
});
@override
Widget build(BuildContext context) {
return SliverList.builder(
itemCount: tracks.length,
itemBuilder: (context, idx) {
final item = tracks[idx];
return ListTile(
leading: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: AutoCacheImage(
item.album!.images!.first.url!,
width: 64.0,
height: 64.0,
),
),
title: Text(item.name ?? 'Loading...'),
subtitle: Text(
item.artists?.asString() ?? 'Please stand by...',
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
onTap: () {
Get.find<AudioPlayerProvider>().load(
[item],
autoPlay: true,
);
},
);
},
);
}
}