✨ Basic track search
This commit is contained in:
parent
81f5a2f5cc
commit
3f41573f00
@ -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.
|
||||
|
@ -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',
|
||||
|
76
lib/screens/search/view.dart
Normal file
76
lib/screens/search/view.dart
Normal 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!)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -2,4 +2,5 @@ const i18nEnglish = {
|
||||
'appName': 'RhythmBox',
|
||||
'explore': 'Explore',
|
||||
'settings': 'Settings',
|
||||
'search': 'Search',
|
||||
};
|
||||
|
@ -2,4 +2,5 @@ const i18nSimplifiedChinese = {
|
||||
'appName': '韵律盒',
|
||||
'explore': '探索',
|
||||
'settings': '设置',
|
||||
'search': '搜索',
|
||||
};
|
||||
|
47
lib/widgets/tracks/track_list.dart
Normal file
47
lib/widgets/tracks/track_list.dart
Normal 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,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user