Skip to content

Commit

Permalink
feat: allow creating playlist during adding song to playlist
Browse files Browse the repository at this point in the history
  • Loading branch information
phanan committed Aug 16, 2021
1 parent c13e298 commit be4fa32
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 100 deletions.
2 changes: 1 addition & 1 deletion lib/providers/playlist_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class PlaylistProvider with ChangeNotifier {

List<Playlist> get playlists => _playlists;

List<Playlist> get standardPlaylist =>
List<Playlist> get standardPlaylists =>
_playlists.where((playlist) => playlist.isStandard).toList();

Future<Playlist> populatePlaylist({required Playlist playlist}) async {
Expand Down
4 changes: 2 additions & 2 deletions lib/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class AppRouter {
}

Future<void> openNowPlayingScreen(BuildContext context) async {
showModalBottomSheet(
await showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
Expand All @@ -85,7 +85,7 @@ class AppRouter {
}

Future<void> showCreatePlaylistSheet(BuildContext context) async {
showModalBottomSheet(
await showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
Expand Down
106 changes: 57 additions & 49 deletions lib/ui/screens/add_to_playlist.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<AddToPlaylistScreen> {
late Song song;
late PlaylistProvider playlistProvider;
late List<Playlist> _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: <Widget>[
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<PlaylistProvider>(
builder: (context, provider, navigationBar) {
if (provider.standardPlaylists.isEmpty) {
return NoPlaylistsScreen(
onTap: () => router.showCreatePlaylistSheet(context),
);
}

return CustomScrollView(
slivers: <Widget>[
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(),
],
),
),
),
);
Expand Down
109 changes: 63 additions & 46 deletions lib/ui/screens/playlists.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,33 +41,8 @@ class _PlaylistsScreenState extends State<PlaylistsScreen> {
child: Consumer<PlaylistProvider>(
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: <Widget>[
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),
);
}

Expand All @@ -76,26 +51,30 @@ class _PlaylistsScreenState extends State<PlaylistsScreen> {
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,
),
),
Expand Down Expand Up @@ -154,3 +133,41 @@ class _PlaylistsScreenState extends State<PlaylistsScreen> {
);
}
}

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: <Widget>[
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.'),
],
),
),
),
);
}
}
2 changes: 1 addition & 1 deletion test/ui/screens/create_playlist_sheet_test.mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion test/ui/widgets/playlist_row_test.mocks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit be4fa32

Please sign in to comment.