388 lines
11 KiB
Dart
388 lines
11 KiB
Dart
|
import 'package:flutter/material.dart';
|
||
|
import 'package:freezer/api/download.dart';
|
||
|
import 'package:freezer/api/player.dart';
|
||
|
import 'package:freezer/ui/details_screens.dart';
|
||
|
import 'package:freezer/ui/menu.dart';
|
||
|
|
||
|
import 'tiles.dart';
|
||
|
import '../api/deezer.dart';
|
||
|
import '../api/definitions.dart';
|
||
|
import '../settings.dart';
|
||
|
import 'error.dart';
|
||
|
|
||
|
class SearchScreen extends StatefulWidget {
|
||
|
@override
|
||
|
_SearchScreenState createState() => _SearchScreenState();
|
||
|
}
|
||
|
|
||
|
class _SearchScreenState extends State<SearchScreen> {
|
||
|
|
||
|
String _query;
|
||
|
bool _offline = settings.offlineMode;
|
||
|
|
||
|
void _submit(BuildContext context, {String query}) {
|
||
|
if (query != null) _query = query;
|
||
|
Navigator.of(context).push(
|
||
|
MaterialPageRoute(builder: (context) => SearchResultsScreen(_query, offline: _offline,))
|
||
|
);
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
appBar: AppBar(title: Text('Search'),),
|
||
|
body: ListView(
|
||
|
children: <Widget>[
|
||
|
Container(height: 16.0),
|
||
|
Padding(
|
||
|
padding: EdgeInsets.symmetric(horizontal: 16.0),
|
||
|
child: Row(
|
||
|
children: <Widget>[
|
||
|
Expanded(
|
||
|
child: TextField(
|
||
|
onChanged: (String s) => _query = s,
|
||
|
decoration: InputDecoration(
|
||
|
labelText: 'Search'
|
||
|
),
|
||
|
onSubmitted: (String s) => _submit(context, query: s),
|
||
|
),
|
||
|
),
|
||
|
IconButton(
|
||
|
icon: Icon(Icons.search),
|
||
|
onPressed: () => _submit(context),
|
||
|
)
|
||
|
],
|
||
|
),
|
||
|
),
|
||
|
ListTile(
|
||
|
title: Text('Offline search'),
|
||
|
leading: Switch(
|
||
|
value: _offline,
|
||
|
onChanged: (v) {
|
||
|
if (settings.offlineMode) {
|
||
|
setState(() => _offline = true);
|
||
|
} else {
|
||
|
setState(() => _offline = v);
|
||
|
}
|
||
|
},
|
||
|
),
|
||
|
)
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
class SearchResultsScreen extends StatelessWidget {
|
||
|
|
||
|
final String query;
|
||
|
final bool offline;
|
||
|
|
||
|
SearchResultsScreen(this.query, {this.offline});
|
||
|
|
||
|
Future _search() async {
|
||
|
if (offline??false) {
|
||
|
return await downloadManager.search(query);
|
||
|
}
|
||
|
return await deezerAPI.search(query);
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
appBar: AppBar(
|
||
|
title: Text('Search Results'),
|
||
|
),
|
||
|
body: FutureBuilder(
|
||
|
future: _search(),
|
||
|
builder: (BuildContext context, AsyncSnapshot snapshot) {
|
||
|
|
||
|
if (!snapshot.hasData) return Center(child: CircularProgressIndicator(),);
|
||
|
if (snapshot.hasError) return ErrorScreen();
|
||
|
|
||
|
SearchResults results = snapshot.data;
|
||
|
|
||
|
if (results.empty)
|
||
|
return Center(
|
||
|
child: Column(
|
||
|
mainAxisSize: MainAxisSize.min,
|
||
|
children: <Widget>[
|
||
|
Icon(
|
||
|
Icons.warning,
|
||
|
size: 64,
|
||
|
),
|
||
|
Text('No results!')
|
||
|
],
|
||
|
),
|
||
|
);
|
||
|
|
||
|
//Tracks
|
||
|
List<Widget> tracks = [];
|
||
|
if (results.tracks != null && results.tracks.length != 0) {
|
||
|
tracks = [
|
||
|
Text(
|
||
|
'Tracks',
|
||
|
textAlign: TextAlign.center,
|
||
|
style: TextStyle(
|
||
|
fontSize: 26.0
|
||
|
),
|
||
|
),
|
||
|
...List.generate(3, (i) {
|
||
|
if (results.tracks.length <= i) return Container(width: 0, height: 0,);
|
||
|
Track t = results.tracks[i];
|
||
|
return TrackTile(
|
||
|
t,
|
||
|
onTap: () {
|
||
|
playerHelper.playFromTrackList(results.tracks, t.id, QueueSource(
|
||
|
text: 'Search',
|
||
|
id: query,
|
||
|
source: 'search'
|
||
|
));
|
||
|
},
|
||
|
onHold: () {
|
||
|
MenuSheet m = MenuSheet(context);
|
||
|
m.defaultTrackMenu(t);
|
||
|
},
|
||
|
);
|
||
|
}),
|
||
|
ListTile(
|
||
|
title: Text('Show all tracks'),
|
||
|
onTap: () {
|
||
|
Navigator.of(context).push(
|
||
|
MaterialPageRoute(builder: (context) => TrackListScreen(results.tracks, QueueSource(
|
||
|
id: query,
|
||
|
source: 'search',
|
||
|
text: 'Search'
|
||
|
)))
|
||
|
);
|
||
|
},
|
||
|
)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
//Albums
|
||
|
List<Widget> albums = [];
|
||
|
if (results.albums != null && results.albums.length != 0) {
|
||
|
albums = [
|
||
|
Text(
|
||
|
'Albums',
|
||
|
textAlign: TextAlign.center,
|
||
|
style: TextStyle(
|
||
|
fontSize: 26.0
|
||
|
),
|
||
|
),
|
||
|
...List.generate(3, (i) {
|
||
|
if (results.albums.length <= i) return Container(height: 0, width: 0,);
|
||
|
Album a = results.albums[i];
|
||
|
return AlbumTile(
|
||
|
a,
|
||
|
onHold: () {
|
||
|
MenuSheet m = MenuSheet(context);
|
||
|
m.defaultAlbumMenu(a);
|
||
|
},
|
||
|
onTap: () {
|
||
|
Navigator.of(context).push(
|
||
|
MaterialPageRoute(builder: (context) => AlbumDetails(a))
|
||
|
);
|
||
|
},
|
||
|
);
|
||
|
}),
|
||
|
ListTile(
|
||
|
title: Text('Show all albums'),
|
||
|
onTap: () {
|
||
|
Navigator.of(context).push(
|
||
|
MaterialPageRoute(builder: (context) => AlbumListScreen(results.albums))
|
||
|
);
|
||
|
},
|
||
|
)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
//Artists
|
||
|
List<Widget> artists = [];
|
||
|
if (results.artists != null && results.artists.length != 0) {
|
||
|
artists = [
|
||
|
Text(
|
||
|
'Artists',
|
||
|
textAlign: TextAlign.center,
|
||
|
style: TextStyle(
|
||
|
fontSize: 26.0
|
||
|
),
|
||
|
),
|
||
|
Container(height: 4),
|
||
|
SingleChildScrollView(
|
||
|
scrollDirection: Axis.horizontal,
|
||
|
child: Row(
|
||
|
children: List.generate(results.artists.length, (int i) {
|
||
|
Artist a = results.artists[i];
|
||
|
return ArtistTile(
|
||
|
a,
|
||
|
onTap: () {
|
||
|
Navigator.of(context).push(
|
||
|
MaterialPageRoute(builder: (context) => ArtistDetails(a))
|
||
|
);
|
||
|
},
|
||
|
onHold: () {
|
||
|
MenuSheet m = MenuSheet(context);
|
||
|
m.defaultArtistMenu(a);
|
||
|
},
|
||
|
);
|
||
|
}),
|
||
|
)
|
||
|
)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
//Playlists
|
||
|
List<Widget> playlists = [];
|
||
|
if (results.playlists != null && results.playlists.length != 0) {
|
||
|
playlists = [
|
||
|
Text(
|
||
|
'Playlists',
|
||
|
textAlign: TextAlign.center,
|
||
|
style: TextStyle(
|
||
|
fontSize: 26.0
|
||
|
),
|
||
|
),
|
||
|
...List.generate(3, (i) {
|
||
|
if (results.playlists.length <= i) return Container(height: 0, width: 0,);
|
||
|
Playlist p = results.playlists[i];
|
||
|
return PlaylistTile(
|
||
|
p,
|
||
|
onTap: () {
|
||
|
Navigator.of(context).push(
|
||
|
MaterialPageRoute(builder: (context) => PlaylistDetails(p))
|
||
|
);
|
||
|
},
|
||
|
onHold: () {
|
||
|
MenuSheet m = MenuSheet(context);
|
||
|
m.defaultPlaylistMenu(p);
|
||
|
},
|
||
|
);
|
||
|
}),
|
||
|
ListTile(
|
||
|
title: Text('Show all playlists'),
|
||
|
onTap: () {
|
||
|
Navigator.of(context).push(
|
||
|
MaterialPageRoute(builder: (context) => SearchResultPlaylists(results.playlists))
|
||
|
);
|
||
|
},
|
||
|
)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
return ListView(
|
||
|
children: <Widget>[
|
||
|
Container(height: 8.0,),
|
||
|
...tracks,
|
||
|
Container(height: 8.0,),
|
||
|
...albums,
|
||
|
Container(height: 8.0,),
|
||
|
...artists,
|
||
|
Container(height: 8.0,),
|
||
|
...playlists
|
||
|
],
|
||
|
);
|
||
|
},
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//List all tracks
|
||
|
class TrackListScreen extends StatelessWidget {
|
||
|
|
||
|
final QueueSource queueSource;
|
||
|
final List<Track> tracks;
|
||
|
|
||
|
TrackListScreen(this.tracks, this.queueSource);
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
appBar: AppBar(title: Text('Tracks'),),
|
||
|
body: ListView.builder(
|
||
|
itemCount: tracks.length,
|
||
|
itemBuilder: (BuildContext context, int i) {
|
||
|
Track t = tracks[i];
|
||
|
return TrackTile(
|
||
|
t,
|
||
|
onTap: () {
|
||
|
playerHelper.playFromTrackList(tracks, t.id, queueSource);
|
||
|
},
|
||
|
onHold: () {
|
||
|
MenuSheet m = MenuSheet(context);
|
||
|
m.defaultTrackMenu(t);
|
||
|
},
|
||
|
);
|
||
|
},
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//List all albums
|
||
|
class AlbumListScreen extends StatelessWidget {
|
||
|
|
||
|
final List<Album> albums;
|
||
|
AlbumListScreen(this.albums);
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
appBar: AppBar(title: Text('Albums'),),
|
||
|
body: ListView.builder(
|
||
|
itemCount: albums.length,
|
||
|
itemBuilder: (context, i) {
|
||
|
Album a = albums[i];
|
||
|
return AlbumTile(
|
||
|
a,
|
||
|
onTap: () {
|
||
|
Navigator.of(context).push(
|
||
|
MaterialPageRoute(builder: (context) => AlbumDetails(a))
|
||
|
);
|
||
|
},
|
||
|
onHold: () {
|
||
|
MenuSheet m = MenuSheet(context);
|
||
|
m.defaultAlbumMenu(a);
|
||
|
},
|
||
|
);
|
||
|
},
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class SearchResultPlaylists extends StatelessWidget {
|
||
|
|
||
|
final List<Playlist> playlists;
|
||
|
SearchResultPlaylists(this.playlists);
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return Scaffold(
|
||
|
appBar: AppBar(title: Text('Playlists'),),
|
||
|
body: ListView.builder(
|
||
|
itemCount: playlists.length,
|
||
|
itemBuilder: (context, i) {
|
||
|
Playlist p = playlists[i];
|
||
|
return PlaylistTile(
|
||
|
p,
|
||
|
onTap: () {
|
||
|
Navigator.of(context).push(
|
||
|
MaterialPageRoute(builder: (context) => PlaylistDetails(p))
|
||
|
);
|
||
|
},
|
||
|
onHold: () {
|
||
|
MenuSheet m = MenuSheet(context);
|
||
|
m.defaultPlaylistMenu(p);
|
||
|
},
|
||
|
);
|
||
|
},
|
||
|
),
|
||
|
);
|
||
|
}
|
||
|
}
|