diff --git a/lib/providers/playlist_provider.dart b/lib/providers/playlist_provider.dart index 7ce0641..09c2d42 100644 --- a/lib/providers/playlist_provider.dart +++ b/lib/providers/playlist_provider.dart @@ -33,7 +33,7 @@ class PlaylistProvider with ChangeNotifier { List get playlists => _playlists; - List get standardPlaylist => + List get standardPlaylists => _playlists.where((playlist) => playlist.isStandard).toList(); Future populatePlaylist({required Playlist playlist}) async { diff --git a/lib/router.dart b/lib/router.dart index 5309d86..3b7eff4 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -71,7 +71,7 @@ class AppRouter { } Future openNowPlayingScreen(BuildContext context) async { - showModalBottomSheet( + await showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, @@ -85,7 +85,7 @@ class AppRouter { } Future showCreatePlaylistSheet(BuildContext context) async { - showModalBottomSheet( + await showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, diff --git a/lib/ui/screens/add_to_playlist.dart b/lib/ui/screens/add_to_playlist.dart index 1401f35..7765f75 100644 --- a/lib/ui/screens/add_to_playlist.dart +++ b/lib/ui/screens/add_to_playlist.dart @@ -1,6 +1,8 @@ import 'package:app/models/playlist.dart'; import 'package:app/models/song.dart'; import 'package:app/providers/playlist_provider.dart'; +import 'package:app/router.dart'; +import 'package:app/ui/screens/playlists.dart'; import 'package:app/ui/widgets/bottom_space.dart'; import 'package:app/ui/widgets/message_overlay.dart'; import 'package:app/ui/widgets/playlist_row.dart'; @@ -10,66 +12,72 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; -class AddToPlaylistScreen extends StatefulWidget { +class AddToPlaylistScreen extends StatelessWidget { static const routeName = '/add-to-playlist'; - const AddToPlaylistScreen({Key? key}) : super(key: key); + final AppRouter router; - @override - _AddToPlaylistScreenState createState() => _AddToPlaylistScreenState(); -} - -class _AddToPlaylistScreenState extends State { - late Song song; - late PlaylistProvider playlistProvider; - late List _playlists = []; - - @override - void initState() { - super.initState(); - playlistProvider = context.read(); - setState(() => _playlists = playlistProvider.standardPlaylist); - } + const AddToPlaylistScreen({ + Key? key, + this.router = const AppRouter(), + }) : super(key: key); @override Widget build(BuildContext context) { - song = ModalRoute.of(context)!.settings.arguments as Song; + Song song = ModalRoute.of(context)!.settings.arguments as Song; return Scaffold( body: CupertinoTheme( - data: CupertinoThemeData( - primaryColor: Colors.white, - ), - child: CustomScrollView( - slivers: [ - CupertinoSliverNavigationBar( - backgroundColor: Colors.black, - largeTitle: const LargeTitle(text: 'Add to a Playlist'), - ), - SliverList( - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) => PlaylistRow( - playlist: _playlists[index], - onTap: () { - playlistProvider.addSongToPlaylist( - song: song, - playlist: _playlists[index], - ); - HapticFeedback.mediumImpact(); - Navigator.pop(context); - showOverlay( - context, - icon: CupertinoIcons.text_badge_plus, - caption: 'Added', - message: 'Song added to playlist.', - ); - }, + data: CupertinoThemeData(primaryColor: Colors.white), + child: Consumer( + builder: (context, provider, navigationBar) { + if (provider.standardPlaylists.isEmpty) { + return NoPlaylistsScreen( + onTap: () => router.showCreatePlaylistSheet(context), + ); + } + + return CustomScrollView( + slivers: [ + navigationBar!, + SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + Playlist playlist = provider.standardPlaylists[index]; + + return PlaylistRow( + playlist: playlist, + onTap: () { + provider.addSongToPlaylist( + song: song, + playlist: playlist, + ); + HapticFeedback.mediumImpact(); + Navigator.pop(context); + showOverlay( + context, + icon: CupertinoIcons.text_badge_plus, + caption: 'Added', + message: 'Song added to playlist.', + ); + }, + ); + }, + childCount: provider.standardPlaylists.length, + ), ), - childCount: _playlists.length, - ), + const BottomSpace(), + ], + ); + }, + child: CupertinoSliverNavigationBar( + backgroundColor: Colors.black, + largeTitle: const LargeTitle(text: 'Add to a Playlist'), + trailing: IconButton( + onPressed: () => router.showCreatePlaylistSheet(context), + icon: const Icon(CupertinoIcons.add_circled), ), - const BottomSpace(), - ], + ), ), ), ); diff --git a/lib/ui/screens/playlists.dart b/lib/ui/screens/playlists.dart index bd22e9e..f56ad59 100644 --- a/lib/ui/screens/playlists.dart +++ b/lib/ui/screens/playlists.dart @@ -41,33 +41,8 @@ class _PlaylistsScreenState extends State { child: Consumer( builder: (context, provider, navigationBar) { if (provider.playlists.isEmpty) { - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: AppDimensions.horizontalPadding, - ), - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () => widget.router.showCreatePlaylistSheet(context), - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon( - CupertinoIcons.exclamationmark_square, - size: 56.0, - color: AppColors.red, - ), - const SizedBox(height: 16.0), - Text( - 'No playlists', - style: Theme.of(context).textTheme.headline5, - ), - const SizedBox(height: 16.0), - const Text('Tap to create a playlist.'), - ], - ), - ), - ), + return NoPlaylistsScreen( + onTap: () => widget.router.showCreatePlaylistSheet(context), ); } @@ -76,26 +51,30 @@ class _PlaylistsScreenState extends State { navigationBar!, SliverList( delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) => Dismissible( - direction: DismissDirection.endToStart, - confirmDismiss: (_) async => await confirmDelete( - context, - playlist: provider.playlists[index], - ), - onDismissed: (_) => provider.remove( - playlist: provider.playlists[index], - ), - background: Container( - alignment: AlignmentDirectional.centerEnd, - color: Colors.red, - child: const Padding( - padding: EdgeInsets.only(right: 28), - child: Icon(CupertinoIcons.delete), + (BuildContext context, int index) { + Playlist playlist = provider.playlists[index]; + + return Dismissible( + direction: DismissDirection.endToStart, + confirmDismiss: (_) async => await confirmDelete( + context, + playlist: playlist, + ), + onDismissed: (_) => provider.remove( + playlist: playlist, + ), + background: Container( + alignment: AlignmentDirectional.centerEnd, + color: Colors.red, + child: const Padding( + padding: EdgeInsets.only(right: 28), + child: Icon(CupertinoIcons.delete), + ), ), - ), - key: ValueKey(provider.playlists[index]), - child: PlaylistRow(playlist: provider.playlists[index]), - ), + key: ValueKey(playlist), + child: PlaylistRow(playlist: playlist), + ); + }, childCount: provider.playlists.length, ), ), @@ -154,3 +133,41 @@ class _PlaylistsScreenState extends State { ); } } + +class NoPlaylistsScreen extends StatelessWidget { + final void Function() onTap; + + const NoPlaylistsScreen({Key? key, required this.onTap}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: AppDimensions.horizontalPadding, + ), + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: onTap, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + CupertinoIcons.exclamationmark_square, + size: 56.0, + color: AppColors.red, + ), + const SizedBox(height: 16.0), + Text( + 'No playlists', + style: Theme.of(context).textTheme.headline5, + ), + const SizedBox(height: 16.0), + const Text('Tap to create a playlist.'), + ], + ), + ), + ), + ); + } +} diff --git a/test/ui/screens/create_playlist_sheet_test.mocks.dart b/test/ui/screens/create_playlist_sheet_test.mocks.dart index 2a71128..0d88a42 100644 --- a/test/ui/screens/create_playlist_sheet_test.mocks.dart +++ b/test/ui/screens/create_playlist_sheet_test.mocks.dart @@ -41,7 +41,7 @@ class MockPlaylistProvider extends _i1.Mock implements _i4.PlaylistProvider { (super.noSuchMethod(Invocation.getter(#playlists), returnValue: <_i3.Playlist>[]) as List<_i3.Playlist>); @override - List<_i3.Playlist> get standardPlaylist => + List<_i3.Playlist> get standardPlaylists => (super.noSuchMethod(Invocation.getter(#standardPlaylist), returnValue: <_i3.Playlist>[]) as List<_i3.Playlist>); @override diff --git a/test/ui/widgets/playlist_row_test.mocks.dart b/test/ui/widgets/playlist_row_test.mocks.dart index b32bcdd..0fbbe25 100644 --- a/test/ui/widgets/playlist_row_test.mocks.dart +++ b/test/ui/widgets/playlist_row_test.mocks.dart @@ -41,7 +41,7 @@ class MockPlaylistProvider extends _i1.Mock implements _i4.PlaylistProvider { (super.noSuchMethod(Invocation.getter(#playlists), returnValue: <_i3.Playlist>[]) as List<_i3.Playlist>); @override - List<_i3.Playlist> get standardPlaylist => + List<_i3.Playlist> get standardPlaylists => (super.noSuchMethod(Invocation.getter(#standardPlaylist), returnValue: <_i3.Playlist>[]) as List<_i3.Playlist>); @override