Compare commits
No commits in common. "3f41573f00594e28c1c166b5a639555587ad9873" and "c0714ab3c8378e6c1033b483bfebb1b2ac0875d7" have entirely different histories.
3f41573f00
...
c0714ab3c8
24
README.md
24
README.md
@ -1,20 +1,16 @@
|
|||||||
# RhythmBox
|
# rhythm_box
|
||||||
|
|
||||||
Yet another spotify third-party client. Support multi-platform because built with flutter.
|
A new Flutter project.
|
||||||
|
|
||||||
This project is inspired by and taken supported by [spotube](https://spotube.krtirtho.dev).
|
## Getting Started
|
||||||
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
|
This project is a starting point for a Flutter application.
|
||||||
|
|
||||||
- [x] Playing music
|
A few resources to get you started if this is your first Flutter project:
|
||||||
- [ ] Add netease music as source
|
|
||||||
- [x] Re-design user interface
|
|
||||||
- [x] Simplified UI and UX
|
|
||||||
- [ ] Support for large screen device
|
|
||||||
|
|
||||||
## License
|
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||||
|
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||||
|
|
||||||
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.
|
For help getting started with Flutter development, view the
|
||||||
|
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||||
This project is all rights reversed by LittleSheep and Solsynth LLC.
|
samples, guidance on mobile development, and a full API reference.
|
||||||
|
@ -5,7 +5,6 @@ import 'package:rhythm_box/screens/explore.dart';
|
|||||||
import 'package:rhythm_box/screens/player/lyrics.dart';
|
import 'package:rhythm_box/screens/player/lyrics.dart';
|
||||||
import 'package:rhythm_box/screens/player/view.dart';
|
import 'package:rhythm_box/screens/player/view.dart';
|
||||||
import 'package:rhythm_box/screens/playlist/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/screens/settings.dart';
|
||||||
import 'package:rhythm_box/shells/nav_shell.dart';
|
import 'package:rhythm_box/shells/nav_shell.dart';
|
||||||
|
|
||||||
@ -18,11 +17,6 @@ final router = GoRouter(routes: [
|
|||||||
name: 'explore',
|
name: 'explore',
|
||||||
builder: (context, state) => const ExploreScreen(),
|
builder: (context, state) => const ExploreScreen(),
|
||||||
),
|
),
|
||||||
GoRoute(
|
|
||||||
path: '/search',
|
|
||||||
name: 'search',
|
|
||||||
builder: (context, state) => const SearchScreen(),
|
|
||||||
),
|
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/playlist/:id',
|
path: '/playlist/:id',
|
||||||
name: 'playlistView',
|
name: 'playlistView',
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
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,8 +24,7 @@ class _NavShellState extends State<NavShell> {
|
|||||||
|
|
||||||
final List<Destination> _allDestinations = <Destination>[
|
final List<Destination> _allDestinations = <Destination>[
|
||||||
Destination('explore'.tr, 'explore', Icons.explore),
|
Destination('explore'.tr, 'explore', Icons.explore),
|
||||||
Destination('search'.tr, 'search', Icons.search),
|
Destination('settings'.tr, 'settings', Icons.settings)
|
||||||
Destination('settings'.tr, 'settings', Icons.settings),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -2,5 +2,4 @@ const i18nEnglish = {
|
|||||||
'appName': 'RhythmBox',
|
'appName': 'RhythmBox',
|
||||||
'explore': 'Explore',
|
'explore': 'Explore',
|
||||||
'settings': 'Settings',
|
'settings': 'Settings',
|
||||||
'search': 'Search',
|
|
||||||
};
|
};
|
||||||
|
@ -2,5 +2,4 @@ const i18nSimplifiedChinese = {
|
|||||||
'appName': '韵律盒',
|
'appName': '韵律盒',
|
||||||
'explore': '探索',
|
'explore': '探索',
|
||||||
'settings': '设置',
|
'settings': '设置',
|
||||||
'search': '搜索',
|
|
||||||
};
|
};
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
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…
x
Reference in New Issue
Block a user