diff --git a/lib/api/cache.dart b/lib/api/cache.dart index f09af8d..3a2cc49 100644 --- a/lib/api/cache.dart +++ b/lib/api/cache.dart @@ -1,9 +1,6 @@ import 'dart:async'; -import 'package:freezer/api/deezer.dart'; import 'package:freezer/api/definitions.dart'; -import 'package:freezer/ui/details_screens.dart'; -import 'package:freezer/ui/library.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart' as p; diff --git a/lib/api/deezer.dart b/lib/api/deezer.dart index fbef18b..215c44e 100644 --- a/lib/api/deezer.dart +++ b/lib/api/deezer.dart @@ -4,7 +4,6 @@ import 'package:freezer/api/spotify.dart'; import 'package:freezer/settings.dart'; import 'package:http/http.dart' as http; -import 'dart:io'; import 'dart:convert'; import 'dart:async'; @@ -32,7 +31,7 @@ class DeezerAPI { "Accept-Charset": "utf-8,ISO-8859-1;q=0.7,*;q=0.3", "Accept-Language": "${settings.deezerLanguage??"en"}-${settings.deezerCountry??'US'},${settings.deezerLanguage??"en"};q=0.9,en-US;q=0.8,en;q=0.7", "Connection": "keep-alive", - "Cookie": "arl=${arl}" + ((sid == null) ? '' : '; sid=${sid}') + "Cookie": "arl=$arl" + ((sid == null) ? '' : '; sid=$sid') }; //Call private API diff --git a/lib/api/definitions.dart b/lib/api/definitions.dart index f51fc48..e23ac2a 100644 --- a/lib/api/definitions.dart +++ b/lib/api/definitions.dart @@ -279,7 +279,7 @@ class Artist { Map json, { Map albumsJson = const {}, Map topJson = const {}, - Map highlight = null, + Map highlight, bool library = false }) { //Get wether radio is available diff --git a/lib/main.dart b/lib/main.dart index b51c671..ca7ae3b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -373,14 +373,14 @@ class _MainScreenState extends State with SingleTickerProviderStateM items: [ BottomNavigationBarItem( icon: Icon(Icons.home), - title: Text('Home'.i18n)), + label: 'Home'.i18n), BottomNavigationBarItem( icon: Icon(Icons.search), - title: Text('Search'.i18n), + label: 'Search'.i18n, ), BottomNavigationBarItem( icon: Icon(Icons.library_music), - title: Text('Library'.i18n)) + label: 'Library'.i18n) ], ) ], diff --git a/lib/settings.dart b/lib/settings.dart index 46f0af6..dd105ac 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -213,8 +213,8 @@ class Settings { case AudioQuality.MP3_128: return 1; case AudioQuality.MP3_320: return 3; case AudioQuality.FLAC: return 9; + default: return 8; } - return 8; //default } //Check if is dark, can't use theme directly, because of system themes, and Theme.of(context).brightness broke @@ -242,6 +242,7 @@ class Settings { sliderTheme: _sliderTheme, toggleableActiveColor: primaryColor, bottomAppBarColor: Color(0xfff5f5f5), + appBarTheme: AppBarTheme(brightness: Brightness.light), ), Themes.Dark: ThemeData( textTheme: _textTheme, @@ -251,6 +252,7 @@ class Settings { accentColor: primaryColor, sliderTheme: _sliderTheme, toggleableActiveColor: primaryColor, + appBarTheme: AppBarTheme(brightness: Brightness.dark), ), Themes.Deezer: ThemeData( textTheme: _textTheme, @@ -267,6 +269,7 @@ class Settings { bottomSheetTheme: BottomSheetThemeData( backgroundColor: deezerBottom ), + appBarTheme: AppBarTheme(brightness: Brightness.dark), cardColor: deezerBg ), Themes.Black: ThemeData( @@ -283,7 +286,8 @@ class Settings { toggleableActiveColor: primaryColor, bottomSheetTheme: BottomSheetThemeData( backgroundColor: Colors.black, - ) + ), + appBarTheme: AppBarTheme(brightness: Brightness.dark), ) }; diff --git a/lib/ui/cached_image.dart b/lib/ui/cached_image.dart index b48ba49..1383b16 100644 --- a/lib/ui/cached_image.dart +++ b/lib/ui/cached_image.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:palette_generator/palette_generator.dart'; import 'package:cached_network_image/cached_network_image.dart'; @@ -114,7 +113,7 @@ class _ZoomableImageState extends State { @override Widget build(BuildContext context) { ctx = context; - return FlatButton( + return TextButton( child: CachedImage( url: widget.url, rounded: widget.rounded, diff --git a/lib/ui/details_screens.dart b/lib/ui/details_screens.dart index cfbed94..ea55dc8 100644 --- a/lib/ui/details_screens.dart +++ b/lib/ui/details_screens.dart @@ -1,7 +1,4 @@ -import 'dart:convert'; - import 'package:draggable_scrollbar/draggable_scrollbar.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:fluttericon/font_awesome5_icons.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -21,7 +18,7 @@ import 'menu.dart'; class AlbumDetails extends StatefulWidget { - Album album; + final Album album; AlbumDetails(this.album, {Key key}): super(key: key); @override @@ -165,7 +162,7 @@ class _AlbumDetailsState extends State { mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - FlatButton( + TextButton( child: Row( children: [ Icon((album.library??false)? Icons.favorite : Icons.favorite_border, size: 32), @@ -196,7 +193,7 @@ class _AlbumDetailsState extends State { }, ), MakeAlbumOffline(album: album), - FlatButton( + TextButton( child: Row( children: [ Icon(Icons.file_download, size: 32.0,), @@ -248,7 +245,7 @@ class _AlbumDetailsState extends State { class MakeAlbumOffline extends StatefulWidget { - Album album; + final Album album; MakeAlbumOffline({Key key, this.album}): super(key: key); @override @@ -266,6 +263,7 @@ class _MakeAlbumOfflineState extends State { _offline = v; }); }); + super.initState(); } @override @@ -399,7 +397,7 @@ class ArtistDetails extends StatelessWidget { mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - FlatButton( + TextButton( child: Row( children: [ Icon(Icons.favorite, size: 32), @@ -417,7 +415,7 @@ class ArtistDetails extends StatelessWidget { }, ), if ((artist.radio??false)) - FlatButton( + TextButton( child: Row( children: [ Icon(Icons.radio, size: 32), @@ -714,7 +712,7 @@ class _DiscographyScreenState extends State { class PlaylistDetails extends StatefulWidget { - Playlist playlist; + final Playlist playlist; PlaylistDetails(this.playlist, {Key key}): super(key: key); @override @@ -1060,7 +1058,7 @@ class _PlaylistDetailsState extends State { } class MakePlaylistOffline extends StatefulWidget { - Playlist playlist; + final Playlist playlist; MakePlaylistOffline(this.playlist, {Key key}): super(key: key); @override @@ -1077,6 +1075,7 @@ class _MakePlaylistOfflineState extends State { _offline = v; }); }); + super.initState(); } @override @@ -1115,7 +1114,7 @@ class _MakePlaylistOfflineState extends State { class ShowScreen extends StatefulWidget { - Show show; + final Show show; ShowScreen(this.show, {Key key}): super(key: key); @override diff --git a/lib/ui/downloads_screen.dart b/lib/ui/downloads_screen.dart index eec2b6e..599ea48 100644 --- a/lib/ui/downloads_screen.dart +++ b/lib/ui/downloads_screen.dart @@ -239,11 +239,11 @@ class DownloadTile extends StatelessWidget { title: Text('Delete'.i18n), content: Text('Are you sure you want to delete this download?'.i18n), actions: [ - FlatButton( + TextButton( child: Text('Cancel'.i18n), onPressed: () => Navigator.of(context).pop(), ), - FlatButton( + TextButton( child: Text('Delete'.i18n), onPressed: () async { await downloadManager.removeDownload(download.id); diff --git a/lib/ui/home_screen.dart b/lib/ui/home_screen.dart index d95faf0..a7efb84 100644 --- a/lib/ui/home_screen.dart +++ b/lib/ui/home_screen.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:freezer/api/deezer.dart'; import 'package:freezer/api/definitions.dart'; import 'package:freezer/api/player.dart'; -import 'package:freezer/main.dart'; import 'package:freezer/ui/elements.dart'; import 'package:freezer/ui/error.dart'; import 'package:freezer/ui/menu.dart'; @@ -180,7 +179,7 @@ class HomepageSectionWidget extends StatelessWidget { //Has more items if (j == section.items.length) { if (section.hasMore ?? false) { - return FlatButton( + return TextButton( child: Text( 'Show more'.i18n, textAlign: TextAlign.center, @@ -217,7 +216,7 @@ class HomepageSectionWidget extends StatelessWidget { class HomePageItemWidget extends StatelessWidget { - HomePageItem item; + final HomePageItem item; HomePageItemWidget(this.item); @override diff --git a/lib/ui/importer_screen.dart b/lib/ui/importer_screen.dart index a4a9526..5606463 100644 --- a/lib/ui/importer_screen.dart +++ b/lib/ui/importer_screen.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:freezer/api/deezer.dart'; import 'package:freezer/api/definitions.dart'; @@ -141,9 +140,8 @@ class _ImporterWidgetState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - RaisedButton( + ElevatedButton( child: Text('Convert'.i18n), - color: Theme.of(context).primaryColor, onPressed: () { spotify.convertPlaylist(widget.playlist); Navigator.of(context).pushReplacement(MaterialPageRoute( @@ -151,9 +149,8 @@ class _ImporterWidgetState extends State { )); }, ), - RaisedButton( + ElevatedButton( child: Text('Download only'.i18n), - color: Theme.of(context).primaryColor, onPressed: () async { //Ask for quality AudioQuality quality; @@ -263,7 +260,7 @@ class CurrentlyImportingScreen extends StatelessWidget { ], ), if (snapshot.data != null) - FlatButton( + TextButton( child: Text('Playlist menu'.i18n), onPressed: () async { Playlist p = await deezerAPI.playlist(snapshot.data); diff --git a/lib/ui/library.dart b/lib/ui/library.dart index a053074..97432ab 100644 --- a/lib/ui/library.dart +++ b/lib/ui/library.dart @@ -22,7 +22,6 @@ import '../api/spotify.dart'; import '../api/download.dart'; class LibraryAppBar extends StatelessWidget implements PreferredSizeWidget { - @override Size get preferredSize => AppBar().preferredSize; @@ -35,22 +34,19 @@ class LibraryAppBar extends StatelessWidget implements PreferredSizeWidget { icon: Icon(Icons.file_download), onPressed: () { Navigator.of(context).push( - MaterialPageRoute(builder: (context) => DownloadsScreen()) - ); + MaterialPageRoute(builder: (context) => DownloadsScreen())); }, ), IconButton( icon: Icon(Icons.settings), onPressed: () { Navigator.of(context).push( - MaterialPageRoute(builder: (context) => SettingsScreen()) - ); + MaterialPageRoute(builder: (context) => SettingsScreen())); }, ), ], ); } - } class LibraryScreen extends StatelessWidget { @@ -60,17 +56,20 @@ class LibraryScreen extends StatelessWidget { appBar: LibraryAppBar(), body: ListView( children: [ - Container(height: 4.0,), + Container( + height: 4.0, + ), if (!downloadManager.running && downloadManager.queueSize > 0) ListTile( title: Text('Downloads'.i18n), leading: LeadingIcon(Icons.file_download, color: Colors.grey), - subtitle: Text('Downloading is currently stopped, click here to resume.'.i18n), + subtitle: Text( + 'Downloading is currently stopped, click here to resume.' + .i18n), onTap: () { downloadManager.start(); - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => DownloadsScreen() - )); + Navigator.of(context).push( + MaterialPageRoute(builder: (context) => DownloadsScreen())); }, ), ListTile( @@ -78,11 +77,13 @@ class LibraryScreen extends StatelessWidget { leading: LeadingIcon(Icons.shuffle, color: Color(0xffeca704)), onTap: () async { List tracks = await deezerAPI.libraryShuffle(); - playerHelper.playFromTrackList(tracks, tracks[0].id, QueueSource( - id: 'libraryshuffle', - source: 'libraryshuffle', - text: 'Library shuffle'.i18n - )); + playerHelper.playFromTrackList( + tracks, + tracks[0].id, + QueueSource( + id: 'libraryshuffle', + source: 'libraryshuffle', + text: 'Library shuffle'.i18n)); }, ), FreezerDivider(), @@ -91,8 +92,7 @@ class LibraryScreen extends StatelessWidget { leading: LeadingIcon(Icons.audiotrack, color: Color(0xffbe3266)), onTap: () { Navigator.of(context).push( - MaterialPageRoute(builder: (context) => LibraryTracks()) - ); + MaterialPageRoute(builder: (context) => LibraryTracks())); }, ), ListTile( @@ -100,8 +100,7 @@ class LibraryScreen extends StatelessWidget { leading: LeadingIcon(Icons.album, color: Color(0xff4b2e7e)), onTap: () { Navigator.of(context).push( - MaterialPageRoute(builder: (context) => LibraryAlbums()) - ); + MaterialPageRoute(builder: (context) => LibraryAlbums())); }, ), ListTile( @@ -109,8 +108,7 @@ class LibraryScreen extends StatelessWidget { leading: LeadingIcon(Icons.recent_actors, color: Color(0xff384697)), onTap: () { Navigator.of(context).push( - MaterialPageRoute(builder: (context) => LibraryArtists()) - ); + MaterialPageRoute(builder: (context) => LibraryArtists())); }, ), ListTile( @@ -118,8 +116,7 @@ class LibraryScreen extends StatelessWidget { leading: LeadingIcon(Icons.playlist_play, color: Color(0xff0880b5)), onTap: () { Navigator.of(context).push( - MaterialPageRoute(builder: (context) => LibraryPlaylists()) - ); + MaterialPageRoute(builder: (context) => LibraryPlaylists())); }, ), FreezerDivider(), @@ -128,8 +125,7 @@ class LibraryScreen extends StatelessWidget { leading: LeadingIcon(Icons.history, color: Color(0xff009a85)), onTap: () { Navigator.of(context).push( - MaterialPageRoute(builder: (context) => HistoryScreen()) - ); + MaterialPageRoute(builder: (context) => HistoryScreen())); }, ), FreezerDivider(), @@ -139,16 +135,14 @@ class LibraryScreen extends StatelessWidget { subtitle: Text('Import playlists from Spotify'.i18n), onTap: () { if (spotify.doneImporting != null) { - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => CurrentlyImportingScreen()) - ); + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => CurrentlyImportingScreen())); if (spotify.doneImporting) spotify.doneImporting = null; return; } Navigator.of(context).push( - MaterialPageRoute(builder: (context) => ImporterScreen()) - ); + MaterialPageRoute(builder: (context) => ImporterScreen())); }, ), ExpansionTile( @@ -159,15 +153,14 @@ class LibraryScreen extends StatelessWidget { future: downloadManager.getStats(), builder: (context, snapshot) { if (snapshot.hasError) return ErrorScreen(); - if (!snapshot.hasData) return Padding( - padding: EdgeInsets.symmetric(vertical: 4.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CircularProgressIndicator() - ], - ), - ); + if (!snapshot.hasData) + return Padding( + padding: EdgeInsets.symmetric(vertical: 4.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [CircularProgressIndicator()], + ), + ); List data = snapshot.data; return Column( children: [ @@ -214,7 +207,6 @@ class LibraryTracks extends StatefulWidget { } class _LibraryTracksState extends State { - bool _loading = false; bool _loadingTracks = false; ScrollController _scrollController = ScrollController(); @@ -233,15 +225,16 @@ class _LibraryTracksState extends State { tcopy.sort((a, b) => a.title.compareTo(b.title)); break; case SortType.ARTIST: - tcopy.sort((a, b) => a.artists[0].name.toLowerCase().compareTo(b.artists[0].name.toLowerCase())); + tcopy.sort((a, b) => a.artists[0].name + .toLowerCase() + .compareTo(b.artists[0].name.toLowerCase())); break; case SortType.DEFAULT: default: break; } //Reverse - if (_sort.reverse) - return tcopy.reversed.toList(); + if (_sort.reverse) return tcopy.reversed.toList(); return tcopy; } @@ -257,15 +250,15 @@ class _LibraryTracksState extends State { await cache.save(); //Preload for sorting - if (tracks.length < (trackCount??0)) - _loadFull(); + if (tracks.length < (trackCount ?? 0)) _loadFull(); } Future _load() async { //Already loaded if (trackCount != null && tracks.length >= trackCount) { //Update tracks cache if fully loaded - if (cache.libraryTracks == null || cache.libraryTracks.length != trackCount) { + if (cache.libraryTracks == null || + cache.libraryTracks.length != trackCount) { setState(() { cache.libraryTracks = tracks.map((t) => t.id).toList(); }); @@ -293,8 +286,7 @@ class _LibraryTracksState extends State { //Update setState(() { trackCount = favPlaylist.trackCount; - if (tracks.length == 0) - tracks = favPlaylist.tracks; + if (tracks.length == 0) tracks = favPlaylist.tracks; _makeFavorite(); _loading = false; }); @@ -307,7 +299,8 @@ class _LibraryTracksState extends State { List _t; try { - _t = await deezerAPI.playlistTracksPage(deezerAPI.favoritesPlaylistId, pos); + _t = await deezerAPI.playlistTracksPage( + deezerAPI.favoritesPlaylistId, pos); } catch (e) {} //On error load offline if (_t == null) { @@ -320,13 +313,12 @@ class _LibraryTracksState extends State { _loading = false; _loadingTracks = false; }); - } } //Load all tracks Future _loadFull() async { - if (tracks.length == 0 || tracks.length < (trackCount??0)) { + if (tracks.length == 0 || tracks.length < (trackCount ?? 0)) { Playlist p; try { p = await deezerAPI.fullPlaylist(deezerAPI.favoritesPlaylistId); @@ -342,10 +334,12 @@ class _LibraryTracksState extends State { } Future _loadOffline() async { - Playlist p = await downloadManager.getPlaylist(deezerAPI.favoritesPlaylistId); - if (p != null) setState(() { - tracks = p.tracks; - }); + Playlist p = + await downloadManager.getPlaylist(deezerAPI.favoritesPlaylistId); + if (p != null) + setState(() { + tracks = p.tracks; + }); } Future _loadAllOffline() async { @@ -357,8 +351,7 @@ class _LibraryTracksState extends State { //Update tracks with favorite true void _makeFavorite() { - for (int i=0; i { //Load sorting int index = Sorting.index(SortSourceTypes.TRACKS); - if (index != null) - setState(() => _sort = cache.sorts[index]); + if (index != null) setState(() => _sort = cache.sorts[index]); - if (_sort.type != SortType.DEFAULT || _sort.reverse) - _loadFull(); + if (_sort.type != SortType.DEFAULT || _sort.reverse) _loadFull(); super.initState(); } @@ -387,156 +378,163 @@ class _LibraryTracksState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: FreezerAppBar( - 'Tracks'.i18n, - actions: [ - IconButton( - icon: Icon(_sort.reverse ? FontAwesome5.sort_alpha_up : FontAwesome5.sort_alpha_down), - onPressed: () async { - await _reverse(); - } - ), - PopupMenuButton( - child: Icon(Icons.sort, size: 32.0), - color: Theme.of(context).scaffoldBackgroundColor, - onSelected: (SortType s) async { - //Preload for sorting - if (tracks.length < (trackCount??0)) - await _loadFull(); + appBar: FreezerAppBar( + 'Tracks'.i18n, + actions: [ + IconButton( + icon: Icon(_sort.reverse + ? FontAwesome5.sort_alpha_up + : FontAwesome5.sort_alpha_down), + onPressed: () async { + await _reverse(); + }), + PopupMenuButton( + child: Icon(Icons.sort, size: 32.0), + color: Theme.of(context).scaffoldBackgroundColor, + onSelected: (SortType s) async { + //Preload for sorting + if (tracks.length < (trackCount ?? 0)) await _loadFull(); - setState(() => _sort.type = s); - //Save sorting in cache - int index = Sorting.index(SortSourceTypes.TRACKS); - if (index != null) { - cache.sorts[index] = _sort; - } else { - cache.sorts.add(_sort); - } - await cache.save(); - }, - itemBuilder: (context) => >[ - PopupMenuItem( - value: SortType.DEFAULT, - child: Text('Default'.i18n, style: popupMenuTextStyle()), - ), - PopupMenuItem( - value: SortType.ALPHABETIC, - child: Text('Alphabetic'.i18n, style: popupMenuTextStyle()), - ), - PopupMenuItem( - value: SortType.ARTIST, - child: Text('Artist'.i18n, style: popupMenuTextStyle()), - ), - ], - ), - Container(width: 8.0), - ], - ), - body: DraggableScrollbar.rrect( - controller: _scrollController, - backgroundColor: Theme.of(context).primaryColor, - child: ListView( - controller: _scrollController, - children: [ - Container( - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - MakePlaylistOffline(_playlist), - FlatButton( - child: Row( - children: [ - Icon(Icons.file_download, size: 32.0,), - Container(width: 4,), - Text('Download'.i18n) - ], - ), - onPressed: () async { - if (await downloadManager.addOfflinePlaylist(_playlist, private: false, context: context) != false) - MenuSheet(context).showDownloadStartedToast(); - }, - ) - ], - ) + setState(() => _sort.type = s); + //Save sorting in cache + int index = Sorting.index(SortSourceTypes.TRACKS); + if (index != null) { + cache.sorts[index] = _sort; + } else { + cache.sorts.add(_sort); + } + await cache.save(); + }, + itemBuilder: (context) => >[ + PopupMenuItem( + value: SortType.DEFAULT, + child: Text('Default'.i18n, style: popupMenuTextStyle()), + ), + PopupMenuItem( + value: SortType.ALPHABETIC, + child: Text('Alphabetic'.i18n, style: popupMenuTextStyle()), + ), + PopupMenuItem( + value: SortType.ARTIST, + child: Text('Artist'.i18n, style: popupMenuTextStyle()), + ), + ], ), - FreezerDivider(), - //Loved tracks - ...List.generate(tracks.length, (i) { - Track t = (tracks.length == (trackCount??0))?_sorted[i]:tracks[i]; - return TrackTile( - t, - onTap: () { - playerHelper.playFromTrackList((tracks.length == (trackCount??0))?_sorted:tracks, t.id, QueueSource( - id: deezerAPI.favoritesPlaylistId, - text: 'Favorites'.i18n, - source: 'playlist' - )); - }, - onHold: () { - MenuSheet m = MenuSheet(context); - m.defaultTrackMenu( - t, - onRemove: () { - setState(() { - tracks.removeWhere((track) => t.id == track.id); - }); - } - ); - }, - ); - }), - if (_loading) - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: EdgeInsets.symmetric(vertical: 8.0), - child: CircularProgressIndicator(), - ) - ], - ), - FreezerDivider(), - Text( - 'All offline tracks'.i18n, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold - ), - ), - Container(height: 8,), - ...List.generate(allTracks.length, (i) { - Track t = allTracks[i]; - return TrackTile( - t, - onTap: () { - playerHelper.playFromTrackList(allTracks, t.id, QueueSource( - id: 'allTracks', - text: 'All offline tracks'.i18n, - source: 'offline' - )); - }, - onHold: () { - MenuSheet m = MenuSheet(context); - m.defaultTrackMenu(t); - }, - ); - }) + Container(width: 8.0), ], - ) - )); + ), + body: DraggableScrollbar.rrect( + controller: _scrollController, + backgroundColor: Theme.of(context).primaryColor, + child: ListView( + controller: _scrollController, + children: [ + Container( + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + MakePlaylistOffline(_playlist), + TextButton( + child: Row( + children: [ + Icon( + Icons.file_download, + size: 32.0, + ), + Container( + width: 4, + ), + Text('Download'.i18n) + ], + ), + onPressed: () async { + if (await downloadManager.addOfflinePlaylist(_playlist, + private: false, context: context) != + false) + MenuSheet(context).showDownloadStartedToast(); + }, + ) + ], + )), + FreezerDivider(), + //Loved tracks + ...List.generate(tracks.length, (i) { + Track t = (tracks.length == (trackCount ?? 0)) + ? _sorted[i] + : tracks[i]; + return TrackTile( + t, + onTap: () { + playerHelper.playFromTrackList( + (tracks.length == (trackCount ?? 0)) + ? _sorted + : tracks, + t.id, + QueueSource( + id: deezerAPI.favoritesPlaylistId, + text: 'Favorites'.i18n, + source: 'playlist')); + }, + onHold: () { + MenuSheet m = MenuSheet(context); + m.defaultTrackMenu(t, onRemove: () { + setState(() { + tracks.removeWhere((track) => t.id == track.id); + }); + }); + }, + ); + }), + if (_loading) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: CircularProgressIndicator(), + ) + ], + ), + FreezerDivider(), + Text( + 'All offline tracks'.i18n, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + ), + Container( + height: 8, + ), + ...List.generate(allTracks.length, (i) { + Track t = allTracks[i]; + return TrackTile( + t, + onTap: () { + playerHelper.playFromTrackList( + allTracks, + t.id, + QueueSource( + id: 'allTracks', + text: 'All offline tracks'.i18n, + source: 'offline')); + }, + onHold: () { + MenuSheet m = MenuSheet(context); + m.defaultTrackMenu(t); + }, + ); + }) + ], + ))); } } - class LibraryAlbums extends StatefulWidget { @override _LibraryAlbumsState createState() => _LibraryAlbumsState(); } class _LibraryAlbumsState extends State { - List _albums; Sorting _sort = Sorting(sourceType: SortSourceTypes.ALBUMS); ScrollController _scrollController = ScrollController(); @@ -548,22 +546,26 @@ class _LibraryAlbumsState extends State { case SortType.DEFAULT: break; case SortType.ALPHABETIC: - albums.sort((a, b) => a.title.toLowerCase().compareTo(b.title.toLowerCase())); + albums.sort( + (a, b) => a.title.toLowerCase().compareTo(b.title.toLowerCase())); break; case SortType.ARTIST: - albums.sort((a, b) => a.artists[0].name.toLowerCase().compareTo(b.artists[0].name.toLowerCase())); + albums.sort((a, b) => a.artists[0].name + .toLowerCase() + .compareTo(b.artists[0].name.toLowerCase())); break; case SortType.RELEASE_DATE: - albums.sort((a, b) => DateTime.parse(a.releaseDate).compareTo(DateTime.parse(b.releaseDate))); + albums.sort((a, b) => DateTime.parse(a.releaseDate) + .compareTo(DateTime.parse(b.releaseDate))); + break; + default: break; } //Reverse - if (_sort.reverse) - return albums.reversed.toList(); + if (_sort.reverse) return albums.reversed.toList(); return albums; } - Future _load() async { if (settings.offlineMode) return; try { @@ -577,8 +579,7 @@ class _LibraryAlbumsState extends State { _load(); //Load sorting int index = Sorting.index(SortSourceTypes.ALBUMS); - if (index != null) - _sort = cache.sorts[index]; + if (index != null) _sort = cache.sorts[index]; super.initState(); } @@ -598,138 +599,138 @@ class _LibraryAlbumsState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: FreezerAppBar( - 'Albums'.i18n, - actions: [ - IconButton( - icon: Icon(_sort.reverse ? FontAwesome5.sort_alpha_up : FontAwesome5.sort_alpha_down), - onPressed: () => _reverse(), - ), - PopupMenuButton( - color: Theme.of(context).scaffoldBackgroundColor, - child: Icon(Icons.sort, size: 32.0), - onSelected: (SortType s) async { - setState(() => _sort.type = s); - //Save to cache - int index = Sorting.index(SortSourceTypes.ALBUMS); - if (index == null) { - cache.sorts.add(_sort); - } else { - cache.sorts[index] = _sort; - } - await cache.save(); - }, - itemBuilder: (context) => >[ - PopupMenuItem( - value: SortType.DEFAULT, - child: Text('Default'.i18n, style: popupMenuTextStyle()), - ), - PopupMenuItem( - value: SortType.ALPHABETIC, - child: Text('Alphabetic'.i18n, style: popupMenuTextStyle()), - ), - PopupMenuItem( - value: SortType.ARTIST, - child: Text('Artist'.i18n, style: popupMenuTextStyle()), - ), - PopupMenuItem( - value: SortType.RELEASE_DATE, - child: Text('Release date'.i18n, style: popupMenuTextStyle()), - ), - ], - ), - Container(width: 8.0), - ], - ), - body: DraggableScrollbar.rrect( - controller: _scrollController, - backgroundColor: Theme.of(context).primaryColor, - child: ListView( - controller: _scrollController, - children: [ - Container(height: 8.0,), - if (!settings.offlineMode && _albums == null) - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CircularProgressIndicator() - ], - ), - - if (_albums != null) - ...List.generate(_albums.length, (int i) { - Album a = _sorted[i]; - return AlbumTile( - a, - onTap: () { - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => AlbumDetails(a)) - ); - }, - onHold: () async { - MenuSheet m = MenuSheet(context); - m.defaultAlbumMenu(a, onRemove: () { - setState(() => _albums.remove(a)); - }); - }, - ); - }), - - FutureBuilder( - future: downloadManager.getOfflineAlbums(), - builder: (context, snapshot) { - if (snapshot.hasError || !snapshot.hasData || snapshot.data.length == 0) return Container(height: 0, width: 0,); - - List albums = snapshot.data; - return Column( - children: [ - FreezerDivider(), - Text( - 'Offline albums'.i18n, - textAlign: TextAlign.center, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 24.0 - ), - ), - ...List.generate(albums.length, (i) { - Album a = albums[i]; - return AlbumTile( - a, - onTap: () { - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => AlbumDetails(a)) - ); - }, - onHold: () async { - MenuSheet m = MenuSheet(context); - m.defaultAlbumMenu(a, onRemove: () { - setState(() { - albums.remove(a); - _albums.remove(a); - }); - }); - }, - ); - }) - ], - ); + appBar: FreezerAppBar( + 'Albums'.i18n, + actions: [ + IconButton( + icon: Icon(_sort.reverse + ? FontAwesome5.sort_alpha_up + : FontAwesome5.sort_alpha_down), + onPressed: () => _reverse(), + ), + PopupMenuButton( + color: Theme.of(context).scaffoldBackgroundColor, + child: Icon(Icons.sort, size: 32.0), + onSelected: (SortType s) async { + setState(() => _sort.type = s); + //Save to cache + int index = Sorting.index(SortSourceTypes.ALBUMS); + if (index == null) { + cache.sorts.add(_sort); + } else { + cache.sorts[index] = _sort; + } + await cache.save(); }, - ) + itemBuilder: (context) => >[ + PopupMenuItem( + value: SortType.DEFAULT, + child: Text('Default'.i18n, style: popupMenuTextStyle()), + ), + PopupMenuItem( + value: SortType.ALPHABETIC, + child: Text('Alphabetic'.i18n, style: popupMenuTextStyle()), + ), + PopupMenuItem( + value: SortType.ARTIST, + child: Text('Artist'.i18n, style: popupMenuTextStyle()), + ), + PopupMenuItem( + value: SortType.RELEASE_DATE, + child: Text('Release date'.i18n, style: popupMenuTextStyle()), + ), + ], + ), + Container(width: 8.0), ], ), - )); + body: DraggableScrollbar.rrect( + controller: _scrollController, + backgroundColor: Theme.of(context).primaryColor, + child: ListView( + controller: _scrollController, + children: [ + Container( + height: 8.0, + ), + if (!settings.offlineMode && _albums == null) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [CircularProgressIndicator()], + ), + if (_albums != null) + ...List.generate(_albums.length, (int i) { + Album a = _sorted[i]; + return AlbumTile( + a, + onTap: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => AlbumDetails(a))); + }, + onHold: () async { + MenuSheet m = MenuSheet(context); + m.defaultAlbumMenu(a, onRemove: () { + setState(() => _albums.remove(a)); + }); + }, + ); + }), + FutureBuilder( + future: downloadManager.getOfflineAlbums(), + builder: (context, snapshot) { + if (snapshot.hasError || + !snapshot.hasData || + snapshot.data.length == 0) + return Container( + height: 0, + width: 0, + ); + + List albums = snapshot.data; + return Column( + children: [ + FreezerDivider(), + Text( + 'Offline albums'.i18n, + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.bold, fontSize: 24.0), + ), + ...List.generate(albums.length, (i) { + Album a = albums[i]; + return AlbumTile( + a, + onTap: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => AlbumDetails(a))); + }, + onHold: () async { + MenuSheet m = MenuSheet(context); + m.defaultAlbumMenu(a, onRemove: () { + setState(() { + albums.remove(a); + _albums.remove(a); + }); + }); + }, + ); + }) + ], + ); + }, + ) + ], + ), + )); } } - class LibraryArtists extends StatefulWidget { @override _LibraryArtistsState createState() => _LibraryArtistsState(); } class _LibraryArtistsState extends State { - List _artists; Sorting _sort = Sorting(sourceType: SortSourceTypes.ARTISTS); bool _loading = true; @@ -746,12 +747,14 @@ class _LibraryArtistsState extends State { artists.sort((a, b) => b.fans - a.fans); break; case SortType.ALPHABETIC: - artists.sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase())); + artists.sort( + (a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase())); + break; + default: break; } //Reverse - if (_sort.reverse) - return artists.reversed.toList(); + if (_sort.reverse) return artists.reversed.toList(); return artists; } @@ -786,13 +789,11 @@ class _LibraryArtistsState extends State { await cache.save(); } - @override void initState() { //Restore sort int index = Sorting.index(SortSourceTypes.ARTISTS); - if (index != null) - _sort = cache.sorts[index]; + if (index != null) _sort = cache.sorts[index]; _load(); super.initState(); @@ -801,86 +802,84 @@ class _LibraryArtistsState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: FreezerAppBar( - 'Artists'.i18n, - actions: [ - IconButton( - icon: Icon(_sort.reverse ? FontAwesome5.sort_alpha_up : FontAwesome5.sort_alpha_down), - onPressed: () => _reverse(), - ), - PopupMenuButton( - child: Icon(Icons.sort, size: 32.0), - color: Theme.of(context).scaffoldBackgroundColor, - onSelected: (SortType s) async { - setState(() => _sort.type = s); - //Save - int index = Sorting.index(SortSourceTypes.ARTISTS); - if (index == null) { - cache.sorts.add(_sort); - } else { - cache.sorts[index] = _sort; - } - await cache.save(); - }, - itemBuilder: (context) => >[ - PopupMenuItem( - value: SortType.DEFAULT, - child: Text('Default'.i18n, style: popupMenuTextStyle()), - ), - PopupMenuItem( - value: SortType.ALPHABETIC, - child: Text('Alphabetic'.i18n, style: popupMenuTextStyle()), - ), - PopupMenuItem( - value: SortType.POPULARITY, - child: Text('Popularity'.i18n, style: popupMenuTextStyle()), - ), + appBar: FreezerAppBar( + 'Artists'.i18n, + actions: [ + IconButton( + icon: Icon(_sort.reverse + ? FontAwesome5.sort_alpha_up + : FontAwesome5.sort_alpha_down), + onPressed: () => _reverse(), + ), + PopupMenuButton( + child: Icon(Icons.sort, size: 32.0), + color: Theme.of(context).scaffoldBackgroundColor, + onSelected: (SortType s) async { + setState(() => _sort.type = s); + //Save + int index = Sorting.index(SortSourceTypes.ARTISTS); + if (index == null) { + cache.sorts.add(_sort); + } else { + cache.sorts[index] = _sort; + } + await cache.save(); + }, + itemBuilder: (context) => >[ + PopupMenuItem( + value: SortType.DEFAULT, + child: Text('Default'.i18n, style: popupMenuTextStyle()), + ), + PopupMenuItem( + value: SortType.ALPHABETIC, + child: Text('Alphabetic'.i18n, style: popupMenuTextStyle()), + ), + PopupMenuItem( + value: SortType.POPULARITY, + child: Text('Popularity'.i18n, style: popupMenuTextStyle()), + ), + ], + ), + Container(width: 8.0), + ], + ), + body: DraggableScrollbar.rrect( + controller: _scrollController, + backgroundColor: Theme.of(context).primaryColor, + child: ListView( + controller: _scrollController, + children: [ + if (_loading) + Padding( + padding: EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [CircularProgressIndicator()], + ), + ), + if (_error) Center(child: ErrorScreen()), + if (!_loading && !_error) + ...List.generate(_artists.length, (i) { + Artist a = _sorted[i]; + return ArtistHorizontalTile( + a, + onTap: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => ArtistDetails(a))); + }, + onHold: () { + MenuSheet m = MenuSheet(context); + m.defaultArtistMenu(a, onRemove: () { + setState(() { + _artists.remove(a); + }); + }); + }, + ); + }), ], ), - Container(width: 8.0), - ], - ), - body: DraggableScrollbar.rrect( - controller: _scrollController, - backgroundColor: Theme.of(context).primaryColor, - child: ListView( - controller: _scrollController, - children: [ - if (_loading) - Padding( - padding: EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [CircularProgressIndicator()], - ), - ), - - if (_error) - Center(child: ErrorScreen()), - - if (!_loading && !_error) - ...List.generate(_artists.length, (i) { - Artist a = _sorted[i]; - return ArtistHorizontalTile( - a, - onTap: () { - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => ArtistDetails(a)) - ); - }, - onHold: () { - MenuSheet m = MenuSheet(context); - m.defaultArtistMenu(a, onRemove: () { - setState(() { - _artists.remove(a); - }); - }); - }, - ); - }), - ], - ), - )); + )); } } @@ -890,29 +889,33 @@ class LibraryPlaylists extends StatefulWidget { } class _LibraryPlaylistsState extends State { - List _playlists; Sorting _sort = Sorting(sourceType: SortSourceTypes.PLAYLISTS); ScrollController _scrollController = ScrollController(); String _filter = ''; List get _sorted { - List playlists = List.from(_playlists.where((p) => p.title.toLowerCase().contains(_filter.toLowerCase()))); + List playlists = List.from(_playlists + .where((p) => p.title.toLowerCase().contains(_filter.toLowerCase()))); switch (_sort.type) { case SortType.DEFAULT: break; case SortType.USER: - playlists.sort((a, b) => (a.user.name??deezerAPI.userName).toLowerCase().compareTo((b.user.name??deezerAPI.userName).toLowerCase())); + playlists.sort((a, b) => (a.user.name ?? deezerAPI.userName) + .toLowerCase() + .compareTo((b.user.name ?? deezerAPI.userName).toLowerCase())); break; case SortType.TRACK_COUNT: playlists.sort((a, b) => b.trackCount - a.trackCount); break; case SortType.ALPHABETIC: - playlists.sort((a, b) => a.title.toLowerCase().compareTo(b.title.toLowerCase())); + playlists.sort( + (a, b) => a.title.toLowerCase().compareTo(b.title.toLowerCase())); + break; + default: break; } - if (_sort.reverse) - return playlists.reversed.toList(); + if (_sort.reverse) return playlists.reversed.toList(); return playlists; } @@ -941,197 +944,198 @@ class _LibraryPlaylistsState extends State { void initState() { //Restore sort int index = Sorting.index(SortSourceTypes.PLAYLISTS); - if (index != null) - _sort = cache.sorts[index]; + if (index != null) _sort = cache.sorts[index]; _load(); super.initState(); } Playlist get favoritesPlaylist => Playlist( - id: deezerAPI.favoritesPlaylistId, - title: 'Favorites'.i18n, - user: User(name: deezerAPI.userName), - image: ImageDetails(thumbUrl: 'assets/favorites_thumb.jpg'), - tracks: [], - trackCount: 1, - duration: Duration(seconds: 0) - ); - + id: deezerAPI.favoritesPlaylistId, + title: 'Favorites'.i18n, + user: User(name: deezerAPI.userName), + image: ImageDetails(thumbUrl: 'assets/favorites_thumb.jpg'), + tracks: [], + trackCount: 1, + duration: Duration(seconds: 0)); @override Widget build(BuildContext context) { return Scaffold( - appBar: FreezerAppBar( - 'Playlists'.i18n, - actions: [ - IconButton( - icon: Icon(_sort.reverse ? FontAwesome5.sort_alpha_up : FontAwesome5.sort_alpha_down), - onPressed: () => _reverse(), - ), - PopupMenuButton( - child: Icon(Icons.sort, size: 32.0), - color: Theme.of(context).scaffoldBackgroundColor, - onSelected: (SortType s) async { - setState(() => _sort.type = s); - //Save to cache - int index = Sorting.index(SortSourceTypes.PLAYLISTS); - if (index == null) - cache.sorts.add(_sort); - else - cache.sorts[index] = _sort; + appBar: FreezerAppBar( + 'Playlists'.i18n, + actions: [ + IconButton( + icon: Icon(_sort.reverse + ? FontAwesome5.sort_alpha_up + : FontAwesome5.sort_alpha_down), + onPressed: () => _reverse(), + ), + PopupMenuButton( + child: Icon(Icons.sort, size: 32.0), + color: Theme.of(context).scaffoldBackgroundColor, + onSelected: (SortType s) async { + setState(() => _sort.type = s); + //Save to cache + int index = Sorting.index(SortSourceTypes.PLAYLISTS); + if (index == null) + cache.sorts.add(_sort); + else + cache.sorts[index] = _sort; - await cache.save(); - }, - itemBuilder: (context) => >[ - PopupMenuItem( - value: SortType.DEFAULT, - child: Text('Default'.i18n, style: popupMenuTextStyle()), + await cache.save(); + }, + itemBuilder: (context) => >[ + PopupMenuItem( + value: SortType.DEFAULT, + child: Text('Default'.i18n, style: popupMenuTextStyle()), + ), + PopupMenuItem( + value: SortType.USER, + child: Text('User'.i18n, style: popupMenuTextStyle()), + ), + PopupMenuItem( + value: SortType.TRACK_COUNT, + child: Text('Track count'.i18n, style: popupMenuTextStyle()), + ), + PopupMenuItem( + value: SortType.ALPHABETIC, + child: Text('Alphabetic'.i18n, style: popupMenuTextStyle()), + ), + ], + ), + Container(width: 8.0), + ], + ), + body: DraggableScrollbar.rrect( + controller: _scrollController, + backgroundColor: Theme.of(context).primaryColor, + child: ListView( + controller: _scrollController, + children: [ + //Search + Padding( + padding: EdgeInsets.all(8.0), + child: TextField( + onChanged: (String s) => setState(() => _filter = s), + decoration: InputDecoration( + labelText: 'Search'.i18n, + fillColor: Theme.of(context).bottomAppBarColor, + filled: true, + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.grey)), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Colors.grey)), + )), ), - PopupMenuItem( - value: SortType.USER, - child: Text('User'.i18n, style: popupMenuTextStyle()), + ListTile( + title: Text('Create new playlist'.i18n), + leading: + LeadingIcon(Icons.playlist_add, color: Color(0xff009a85)), + onTap: () async { + if (settings.offlineMode) { + Fluttertoast.showToast( + msg: 'Cannot create playlists in offline mode'.i18n, + gravity: ToastGravity.BOTTOM); + return; + } + MenuSheet m = MenuSheet(context); + await m.createPlaylist(); + await _load(); + }, ), - PopupMenuItem( - value: SortType.TRACK_COUNT, - child: Text('Track count'.i18n, style: popupMenuTextStyle()), - ), - PopupMenuItem( - value: SortType.ALPHABETIC, - child: Text('Alphabetic'.i18n, style: popupMenuTextStyle()), + FreezerDivider(), + + if (!settings.offlineMode && _playlists == null) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircularProgressIndicator(), + ], + ), + + //Favorites playlist + PlaylistTile( + favoritesPlaylist, + onTap: () async { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => + PlaylistDetails(favoritesPlaylist))); + }, + onHold: () { + MenuSheet m = MenuSheet(context); + favoritesPlaylist.library = true; + m.defaultPlaylistMenu(favoritesPlaylist); + }, ), + + if (_playlists != null) + ...List.generate(_sorted.length, (int i) { + Playlist p = (_sorted ?? [])[i]; + return PlaylistTile( + p, + onTap: () => Navigator.of(context).push(MaterialPageRoute( + builder: (context) => PlaylistDetails(p))), + onHold: () { + MenuSheet m = MenuSheet(context); + m.defaultPlaylistMenu(p, onRemove: () { + setState(() => _playlists.remove(p)); + }, onUpdate: () { + _load(); + }); + }, + ); + }), + + FutureBuilder( + future: downloadManager.getOfflinePlaylists(), + builder: (context, snapshot) { + if (snapshot.hasError || !snapshot.hasData) + return Container( + height: 0, + width: 0, + ); + if (snapshot.data.length == 0) + return Container( + height: 0, + width: 0, + ); + + List playlists = snapshot.data; + return Column( + children: [ + FreezerDivider(), + Text( + 'Offline playlists'.i18n, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 24.0, fontWeight: FontWeight.bold), + ), + ...List.generate(playlists.length, (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, onRemove: () { + setState(() { + playlists.remove(p); + _playlists.remove(p); + }); + }); + }, + ); + }) + ], + ); + }, + ) ], ), - Container(width: 8.0), - ], - ), - body: DraggableScrollbar.rrect( - controller: _scrollController, - backgroundColor: Theme.of(context).primaryColor, - child: ListView( - controller: _scrollController, - children: [ - //Search - Padding( - padding: EdgeInsets.all(8.0), - child: TextField( - onChanged: (String s) => setState(() => _filter = s), - decoration: InputDecoration( - labelText: 'Search'.i18n, - fillColor: Theme.of(context).bottomAppBarColor, - filled: true, - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.grey) - ), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.grey) - ), - ) - ), - ), - ListTile( - title: Text('Create new playlist'.i18n), - leading: LeadingIcon(Icons.playlist_add, color: Color(0xff009a85)), - onTap: () async { - if (settings.offlineMode) { - Fluttertoast.showToast( - msg: 'Cannot create playlists in offline mode'.i18n, - gravity: ToastGravity.BOTTOM - ); - return; - } - MenuSheet m = MenuSheet(context); - await m.createPlaylist(); - await _load(); - }, - ), - FreezerDivider(), - - if (!settings.offlineMode && _playlists == null) - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CircularProgressIndicator(), - ], - ), - - //Favorites playlist - PlaylistTile( - favoritesPlaylist, - onTap: () async { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => PlaylistDetails(favoritesPlaylist) - )); - }, - onHold: () { - MenuSheet m = MenuSheet(context); - favoritesPlaylist.library = true; - m.defaultPlaylistMenu(favoritesPlaylist); - }, - ), - - if (_playlists != null) - ...List.generate(_sorted.length, (int i) { - Playlist p = (_sorted??[])[i]; - return PlaylistTile( - p, - onTap: () => Navigator.of(context).push(MaterialPageRoute( - builder: (context) => PlaylistDetails(p) - )), - onHold: () { - MenuSheet m = MenuSheet(context); - m.defaultPlaylistMenu( - p, - onRemove: () {setState(() => _playlists.remove(p));}, - onUpdate: () {_load();}); - }, - ); - }), - - FutureBuilder( - future: downloadManager.getOfflinePlaylists(), - builder: (context, snapshot) { - if (snapshot.hasError || !snapshot.hasData) return Container(height: 0, width: 0,); - if (snapshot.data.length == 0) return Container(height: 0, width: 0,); - - List playlists = snapshot.data; - return Column( - children: [ - FreezerDivider(), - Text( - 'Offline playlists'.i18n, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 24.0, - fontWeight: FontWeight.bold - ), - ), - ...List.generate(playlists.length, (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, onRemove: () { - setState(() { - playlists.remove(p); - _playlists.remove(p); - }); - }); - }, - ); - }) - ], - ); - }, - ) - - ], - ), - )); + )); } } @@ -1158,31 +1162,29 @@ class _HistoryScreenState extends State { ], ), body: DraggableScrollbar.rrect( - controller: _scrollController, - backgroundColor: Theme.of(context).primaryColor, - child: ListView.builder( controller: _scrollController, - itemCount: (cache.history??[]).length, - itemBuilder: (BuildContext context, int i) { - Track t = cache.history[cache.history.length - i - 1]; - return TrackTile( - t, - onTap: () { - playerHelper.playFromTrackList(cache.history.reversed.toList(), t.id, QueueSource( - id: null, - text: 'History'.i18n, - source: 'history' - )); - }, - onHold: () { - MenuSheet m = MenuSheet(context); - m.defaultTrackMenu(t); - }, - ); - }, - ) - ), + backgroundColor: Theme.of(context).primaryColor, + child: ListView.builder( + controller: _scrollController, + itemCount: (cache.history ?? []).length, + itemBuilder: (BuildContext context, int i) { + Track t = cache.history[cache.history.length - i - 1]; + return TrackTile( + t, + onTap: () { + playerHelper.playFromTrackList( + cache.history.reversed.toList(), + t.id, + QueueSource( + id: null, text: 'History'.i18n, source: 'history')); + }, + onHold: () { + MenuSheet m = MenuSheet(context); + m.defaultTrackMenu(t); + }, + ); + }, + )), ); } } - diff --git a/lib/ui/menu.dart b/lib/ui/menu.dart index 6042196..8be282b 100644 --- a/lib/ui/menu.dart +++ b/lib/ui/menu.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:flutter/cupertino.dart'; import 'package:freezer/main.dart'; import 'package:wakelock/wakelock.dart'; import 'package:flutter/material.dart'; @@ -697,14 +696,14 @@ class _SleepTimerDialogState extends State { ], ), actions: [ - FlatButton( + TextButton( child: Text('Dismiss'.i18n), onPressed: () { Navigator.of(context).pop(); }, ), if (cache.sleepTimer != null) - FlatButton( + TextButton( child: Text('Cancel current timer'.i18n), onPressed: () { cache.sleepTimer.cancel(); @@ -714,7 +713,7 @@ class _SleepTimerDialogState extends State { }, ), - FlatButton( + TextButton( child: Text('Save'.i18n), onPressed: () { Duration duration = Duration(hours: hours, minutes: minutes); @@ -891,11 +890,11 @@ class _CreatePlaylistDialogState extends State { ], ), actions: [ - FlatButton( + TextButton( child: Text('Cancel'.i18n), onPressed: () => Navigator.of(context).pop(), ), - FlatButton( + TextButton( child: Text(edit ? 'Update'.i18n : 'Create'.i18n), onPressed: () async { if (edit) { diff --git a/lib/ui/player_screen.dart b/lib/ui/player_screen.dart index 5fe8c45..26df621 100644 --- a/lib/ui/player_screen.dart +++ b/lib/ui/player_screen.dart @@ -63,13 +63,15 @@ class _PlayerScreenState extends State { if (settings.blurPlayerBackground) SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( statusBarColor: palette.dominantColor.color.withOpacity(0.25), - systemNavigationBarColor: Color.alphaBlend(palette.dominantColor.color.withOpacity(0.25), Theme.of(context).scaffoldBackgroundColor) + systemNavigationBarColor: Color.alphaBlend(palette.dominantColor.color.withOpacity(0.25), Theme.of(context).scaffoldBackgroundColor), + systemNavigationBarIconBrightness: ThemeData.estimateBrightnessForColor(palette.dominantColor.color) == Brightness.light ? Brightness.dark : Brightness.light )); //Color gradient if (!settings.blurPlayerBackground) { SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( statusBarColor: palette.dominantColor.color.withOpacity(0.7), + statusBarIconBrightness: ThemeData.estimateBrightnessForColor(palette.dominantColor.color.withOpacity(0.7)) == Brightness.light ? Brightness.dark : Brightness.light )); setState(() => _bgGradient = LinearGradient( begin: Alignment.topCenter, @@ -425,7 +427,7 @@ class _QualityInfoWidgetState extends State { _load(); if (streamSubscription == null) streamSubscription = AudioService.currentMediaItemStream.listen((event) async { - await _load(); + _load(); }); super.initState(); } @@ -439,7 +441,7 @@ class _QualityInfoWidgetState extends State { @override Widget build(BuildContext context) { - return FlatButton( + return TextButton( child: Text(value), onPressed: () { Navigator.of(context).push(MaterialPageRoute(builder: (context) => QualitySettings())); @@ -641,10 +643,10 @@ class _BigAlbumArtState extends State { //Top row containing QueueSource, queue... class PlayerScreenTopRow extends StatelessWidget { - double textSize; - double iconSize; - double textWidth; - bool short; + final double textSize; + final double iconSize; + final double textWidth; + final bool short; PlayerScreenTopRow({this.textSize, this.iconSize, this.textWidth, this.short}); @override diff --git a/lib/ui/search.dart b/lib/ui/search.dart index c0f032e..0c340e1 100644 --- a/lib/ui/search.dart +++ b/lib/ui/search.dart @@ -7,7 +7,6 @@ import 'package:flutter/src/services/keyboard_key.dart'; import 'package:freezer/api/cache.dart'; import 'package:freezer/api/download.dart'; import 'package:freezer/api/player.dart'; -import 'package:freezer/main.dart'; import 'package:freezer/ui/details_screens.dart'; import 'package:freezer/ui/elements.dart'; import 'package:freezer/ui/home_screen.dart'; @@ -59,7 +58,7 @@ class _SearchScreenState extends State { List _suggestions = []; bool _cancel = false; bool _showCards = true; - FocusNode _focus = FocusNode(); + //FocusNode _focus = FocusNode(); void _submit(BuildContext context, {String query}) async { if (query != null) _query = query; @@ -94,7 +93,7 @@ class _SearchScreenState extends State { } //Load search suggestions - Future> _loadSuggestions() async { + Future _loadSuggestions() async { if (_query == null || _query.length < 2 || _query.startsWith('http')) return null; String q = _query; await Future.delayed(Duration(milliseconds: 300)); diff --git a/lib/ui/settings_screen.dart b/lib/ui/settings_screen.dart index 41dec4d..9a9a06d 100644 --- a/lib/ui/settings_screen.dart +++ b/lib/ui/settings_screen.dart @@ -2,7 +2,6 @@ import 'package:audio_service/audio_service.dart'; import 'package:country_pickers/country.dart'; import 'package:country_pickers/country_picker_dialog.dart'; import 'package:filesize/filesize.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; @@ -23,7 +22,6 @@ import 'package:package_info/package_info.dart'; import 'package:path_provider_ex/path_provider_ex.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:freezer/translations.i18n.dart'; -import 'package:clipboard/clipboard.dart'; import 'package:scrobblenaut/scrobblenaut.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -106,7 +104,7 @@ class _SettingsScreenState extends State { title: Text('Language'.i18n), content: Text('Language changed, please restart Freezer to apply!'.i18n), actions: [ - FlatButton( + TextButton( child: Text('OK'), onPressed: () { Navigator.of(context).pop(); @@ -387,7 +385,7 @@ class _FontSelectorState extends State { title: Text('Warning'.i18n), content: Text("This app isn't made for supporting many fonts, it can break layouts and overflow. Use at your own risk!".i18n), actions: [ - FlatButton( + TextButton( onPressed: () async { setState(() => settings.font = font); await settings.save(); @@ -398,7 +396,7 @@ class _FontSelectorState extends State { }, child: Text('Apply'.i18n), ), - FlatButton( + TextButton( onPressed: () { Navigator.of(context).pop(); widget.callback(); @@ -739,8 +737,8 @@ class _DeezerSettingsState extends State { class FilenameTemplateDialog extends StatefulWidget { - String initial; - Function onSave; + final String initial; + final Function onSave; FilenameTemplateDialog(this.initial, this.onSave, {Key key}): super(key: key); @override @@ -782,22 +780,22 @@ class _FilenameTemplateDialogState extends State { ], ), actions: [ - FlatButton( + TextButton( child: Text('Cancel'.i18n), onPressed: () => Navigator.of(context).pop(), ), - FlatButton( + TextButton( child: Text('Reset'.i18n), onPressed: () { _controller.value = _controller.value.copyWith(text: '%artist% - %title%'); _new = '%artist% - %title%'; }, ), - FlatButton( + TextButton( child: Text('Clear'.i18n), onPressed: () => _controller.clear(), ), - FlatButton( + TextButton( child: Text('Save'.i18n), onPressed: () async { widget.onSave(_new); @@ -907,7 +905,7 @@ class _DownloadsSettingsState extends State { title: Text('Warning'.i18n), content: Text('Using too many concurrent downloads on older/weaker devices might cause crashes!'.i18n), actions: [ - FlatButton( + TextButton( child: Text('Dismiss'.i18n), onPressed: () => Navigator.of(context).pop(), ) @@ -1195,7 +1193,7 @@ class _GeneralSettingsState extends State { subtitle: Text('Copy userToken/ARL Cookie for use in other apps.'.i18n), leading: Icon(Icons.lock), onTap: () async { - await FlutterClipboard.copy(settings.arl); + Clipboard.setData(ClipboardData(text: settings.arl)); await Fluttertoast.showToast( msg: 'Copied'.i18n, ); @@ -1251,7 +1249,7 @@ class _GeneralSettingsState extends State { // content: Text('Due to plugin incompatibility, login using browser is unavailable without restart.'.i18n), content: Text('Restart of app is required to properly log out!'.i18n), actions: [ - FlatButton( + TextButton( child: Text('Cancel'.i18n), onPressed: () => Navigator.of(context).pop(), ), @@ -1262,7 +1260,7 @@ class _GeneralSettingsState extends State { // Navigator.of(context).pop(); // }, // ), - FlatButton( + TextButton( child: Text('Log out & Exit'.i18n), onPressed: () async { try {AudioService.stop();} catch (e) {} @@ -1329,11 +1327,11 @@ class _LastFMLoginState extends State { ], ), actions: [ - FlatButton( + TextButton( child: Text('Cancel'.i18n), onPressed: () => Navigator.of(context).pop(), ), - FlatButton( + TextButton( child: Text('Login'.i18n), onPressed: () async { LastFM last; @@ -1398,7 +1396,6 @@ class _DirectoryPickerState extends State { IconButton( icon: Icon(Icons.sd_card), onPressed: () { - String path = ''; //Chose storage showDialog( context: context, diff --git a/lib/ui/updater.dart b/lib/ui/updater.dart index 73df158..3a5cabd 100644 --- a/lib/ui/updater.dart +++ b/lib/ui/updater.dart @@ -1,6 +1,5 @@ import 'dart:io'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:freezer/api/cache.dart'; @@ -168,12 +167,12 @@ class _UpdaterScreenState extends State { //Available download if (_versionDownload != null) Column(children: [ - RaisedButton( + ElevatedButton( child: Text('Download'.i18n + ' (${_versionDownload.version})'), onPressed: _buttonEnabled ? () { setState(() => _buttonEnabled = false); _download(); - }:null + } : null ), Padding( padding: EdgeInsets.all(8.0), diff --git a/pubspec.yaml b/pubspec.yaml index c8a8f20..6a4d521 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -58,7 +58,6 @@ dependencies: marquee: ^1.5.2 flutter_cache_manager: ^1.4.1 cached_network_image: ^2.3.2+1 - clipboard: ^0.1.2+8 i18n_extension: ^4.0.0 fluttericon: ^1.0.7 url_launcher: ^5.7.2