From 41e2b446e619b772829ae5add11c3c1c53a489de Mon Sep 17 00:00:00 2001 From: JothishKamal Date: Sat, 4 Jan 2025 16:56:07 +0530 Subject: [PATCH 1/2] fix: error on no playlists --- lib/providers/playlist_provider.dart | 31 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/providers/playlist_provider.dart b/lib/providers/playlist_provider.dart index 1169554..15469dc 100644 --- a/lib/providers/playlist_provider.dart +++ b/lib/providers/playlist_provider.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotify_collab_app/utils/api_util.dart'; import 'package:spotify_collab_app/view/models/playlist_success_response.dart'; @@ -28,22 +30,27 @@ class PlaylistNotifier extends StateNotifier> { String? selectedPlaylistName; Future fetchPlaylists() async { - final response = await apiUtil.get('/v1/playlists'); + try { + final response = await apiUtil.get('/v1/playlists'); - if (response.statusCode == 200) { - final playlistResponse = PlaylistSuccessResponse.fromJson(response.data); + if (response.statusCode == 200) { + final playlistResponse = + PlaylistSuccessResponse.fromJson(response.data); - if (playlistResponse.message == "Playlists successfully retrieved") { - final playlists = playlistResponse.data - ?.map((data) => Playlist( - name: data.name, - playlistUuid: data.playlistUuid, - )) - .toList() ?? - []; + if (playlistResponse.message == "Playlists successfully retrieved") { + final playlists = playlistResponse.data + ?.map((data) => Playlist( + name: data.name, + playlistUuid: data.playlistUuid, + )) + .toList() ?? + []; - state = playlists; + state = playlists; + } } + } catch (e) { + log(e.toString()); } } From e924404c7310cb66e4ccbf8bbd8491b4bb9186b3 Mon Sep 17 00:00:00 2001 From: JothishKamal Date: Wed, 8 Jan 2025 00:18:50 +0530 Subject: [PATCH 2/2] feat: image shimmer, ui fixes, loading states --- lib/providers/create_screen_provider.dart | 37 ++++++ lib/view/screens/admin_screen.dart | 56 +-------- lib/view/screens/create_screen.dart | 138 +++++++++++----------- lib/view/screens/home_screen.dart | 4 +- lib/view/widgets/music_list_item.dart | 25 ++++ pubspec.lock | 8 ++ pubspec.yaml | 2 +- 7 files changed, 141 insertions(+), 129 deletions(-) create mode 100644 lib/providers/create_screen_provider.dart diff --git a/lib/providers/create_screen_provider.dart b/lib/providers/create_screen_provider.dart new file mode 100644 index 0000000..44921f8 --- /dev/null +++ b/lib/providers/create_screen_provider.dart @@ -0,0 +1,37 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'dart:developer'; + +import 'package:spotify_collab_app/utils/api_util.dart'; + +class CreateScreenNotifier extends StateNotifier> { + CreateScreenNotifier() : super(const AsyncValue.data(null)); + + Future<({bool success, String message})> createPlaylist(String name) async { + try { + state = const AsyncValue.loading(); + + final response = await apiUtil.post('/v1/playlists', { + 'name': name, + }); + + log(response.toString()); + + if (response.statusCode == 200 && + response.data["message"] == "playlist successfully created") { + state = const AsyncValue.data(null); + return (success: true, message: 'Playlist successfully created'); + } + + state = const AsyncValue.data(null); + return (success: false, message: 'Failed to create playlist'); + } catch (e, st) { + state = AsyncValue.error(e, st); + return (success: false, message: 'Failed to create playlist'); + } + } +} + +final createScreenProvider = + StateNotifierProvider>((ref) { + return CreateScreenNotifier(); +}); diff --git a/lib/view/screens/admin_screen.dart b/lib/view/screens/admin_screen.dart index eced647..c9359de 100644 --- a/lib/view/screens/admin_screen.dart +++ b/lib/view/screens/admin_screen.dart @@ -59,7 +59,6 @@ class _AdminScreenState extends ConsumerState @override Widget build(BuildContext context) { - double width = MediaQuery.sizeOf(context).width; double height = MediaQuery.sizeOf(context).height; final currentIndex = ref.watch(tabProvider); @@ -105,9 +104,10 @@ class _AdminScreenState extends ConsumerState Padding( padding: EdgeInsets.only( top: height * 0.03, + left: height * 0.03, ), child: Align( - alignment: Alignment.topCenter, + alignment: Alignment.topLeft, child: Text( ref.watch(playlistProvider.notifier).selectedPlaylistName!, style: const TextStyle( @@ -207,11 +207,6 @@ class _AdminScreenState extends ConsumerState ), ), ), - Positioned( - right: width * 0.05, - top: height * 0.1, - child: SvgPicture.asset('assets/path.svg'), - ) ], ); } @@ -292,53 +287,6 @@ class _AdminScreenState extends ConsumerState ) : Column( children: [ - Align( - alignment: Alignment.topRight, - child: Padding( - padding: const EdgeInsets.only(right: 8.0), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Radio( - value: RequestAction.rejectAll, - groupValue: action, - onChanged: (value) => ref - .read(requestActionProvider.notifier) - .state = value!, - activeColor: Colors.red, - fillColor: const WidgetStatePropertyAll(Colors.red), - ), - const Text( - "Reject All", - style: TextStyle( - fontFamily: 'Gotham', - color: Colors.white, - fontWeight: FontWeight.w600, - fontSize: 10, - ), - ), - Radio( - value: RequestAction.acceptAll, - groupValue: action, - onChanged: (value) => ref - .read(requestActionProvider.notifier) - .state = value!, - activeColor: Colors.green, - fillColor: const WidgetStatePropertyAll(Colors.green), - ), - const Text( - "Accept All", - style: TextStyle( - fontFamily: 'Gotham', - color: Colors.white, - fontWeight: FontWeight.w600, - fontSize: 10, - ), - ), - ], - ), - ), - ), Expanded( child: ListView.separated( padding: const EdgeInsets.symmetric( diff --git a/lib/view/screens/create_screen.dart b/lib/view/screens/create_screen.dart index e401382..ac1a72b 100644 --- a/lib/view/screens/create_screen.dart +++ b/lib/view/screens/create_screen.dart @@ -1,13 +1,9 @@ -import 'dart:developer'; - -import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:go_router/go_router.dart'; -import 'package:spotify_collab_app/utils/api_util.dart'; -import 'package:spotify_collab_app/view/widgets/background_widget.dart'; +import 'package:spotify_collab_app/providers/create_screen_provider.dart'; import 'package:spotify_collab_app/view/widgets/custom_text_field.dart'; import 'package:spotify_collab_app/providers/photo_upload_notifier.dart'; @@ -19,15 +15,21 @@ class CreateScreen extends ConsumerWidget { double width = MediaQuery.sizeOf(context).width; double height = MediaQuery.sizeOf(context).height; final TextEditingController eventNameController = TextEditingController(); + final createState = ref.watch(createScreenProvider); return Scaffold( resizeToAvoidBottomInset: false, body: Stack( children: [ - BackgroundWidget( - width: width, - height: height, - color: Colors.white.withOpacity(0.35), + SizedBox( + height: double.infinity, + width: double.infinity, + child: SvgPicture.asset( + 'assets/bg.svg', + colorFilter: + const ColorFilter.mode(Color(0xffd1dfdb), BlendMode.srcIn), + fit: BoxFit.cover, + ), ), Padding( padding: EdgeInsets.symmetric( @@ -55,7 +57,7 @@ class CreateScreen extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( - 'new shared playlist', + 'New Shared Playlist', style: TextStyle( fontFamily: 'Gotham', shadows: [ @@ -114,7 +116,7 @@ class CreateScreen extends ConsumerWidget { height: height * 0.055, ), CustomTextField( - labelText: 'event name', + labelText: 'Event name', obscureText: false, textEditingController: eventNameController, ), @@ -134,80 +136,74 @@ class CreateScreen extends ConsumerWidget { const Color(0xff5822EE), ), ), - onPressed: () async { - if (eventNameController.text.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text( - 'Event name cannot be empty', - style: TextStyle( - fontFamily: 'Gotham', - color: Colors.white, - fontWeight: FontWeight.w600, - fontSize: 14, - ), - ), - backgroundColor: Colors.red, - ), - ); - } else { - Response response = - await apiUtil.post('/v1/playlists', { - 'name': eventNameController.text, - }); - log(response.toString()); - if (response.statusCode == 200) { - if (response.data["message"] == - "playlist successfully created") { + onPressed: createState.isLoading + ? null + : () async { + if (eventNameController.text.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Event name cannot be empty', + style: TextStyle( + fontFamily: 'Gotham', + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 14, + ), + ), + backgroundColor: Colors.red, + ), + ); + return; + } + + final result = await ref + .read(createScreenProvider.notifier) + .createPlaylist(eventNameController.text); + if (!context.mounted) return; + ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( + SnackBar( content: Text( - 'Playlist successfully created', - style: TextStyle( + result.message, + style: const TextStyle( fontFamily: 'Gotham', color: Colors.white, fontWeight: FontWeight.w600, fontSize: 14, ), ), - backgroundColor: Colors.green, + backgroundColor: result.success + ? Colors.green + : Colors.red.withOpacity(0.8), ), ); - context.go('/home'); - } - } else { - if (!context.mounted) return; - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: const Text( - 'Failed to create playlist', - style: TextStyle( - fontFamily: 'Gotham', - color: Colors.white, - fontWeight: FontWeight.w600, - fontSize: 14, - ), + if (result.success) { + context.go('/home'); + } + }, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 5.0, vertical: 5), + child: createState.isLoading + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.white, + ), + ) + : const Text( + 'Create event', + style: TextStyle( + fontFamily: 'Gotham', + fontSize: 16, + color: Colors.white, ), - backgroundColor: - Colors.red.withOpacity(0.8), ), - ); - } - } - }, - child: const Padding( - padding: EdgeInsets.symmetric( - horizontal: 5.0, vertical: 5), - child: Text( - 'create event', - style: TextStyle( - fontFamily: 'Gotham', - fontSize: 16, - color: Colors.white, - ), - ), ), ), ), diff --git a/lib/view/screens/home_screen.dart b/lib/view/screens/home_screen.dart index 639727c..38cfc71 100644 --- a/lib/view/screens/home_screen.dart +++ b/lib/view/screens/home_screen.dart @@ -19,7 +19,7 @@ class HomeScreen extends ConsumerWidget { appBar: AppBar( automaticallyImplyLeading: false, title: const CustomTitle( - title: "shared playlists", + title: "Shared Playlists", ), backgroundColor: Colors.transparent, ), @@ -108,5 +108,3 @@ class HomeScreen extends ConsumerWidget { ); } } - - diff --git a/lib/view/widgets/music_list_item.dart b/lib/view/widgets/music_list_item.dart index 39c309e..4d5077b 100644 --- a/lib/view/widgets/music_list_item.dart +++ b/lib/view/widgets/music_list_item.dart @@ -2,6 +2,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:shimmer/shimmer.dart'; import 'package:spotify_collab_app/providers/admin_screen_provider.dart'; import 'package:spotify_collab_app/providers/playlist_provider.dart'; @@ -71,11 +72,35 @@ class MusicListItem extends ConsumerWidget { ), ) : Container( + width: 50, + height: 50, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8.0), ), child: Image.network( imageUrl!, + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) return child; + return Shimmer.fromColors( + baseColor: Colors.grey[800]!, + highlightColor: Colors.grey[600]!, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.0), + ), + ), + ); + }, + errorBuilder: (context, error, stackTrace) { + return Container( + decoration: BoxDecoration( + color: Colors.grey[800], + borderRadius: BorderRadius.circular(8.0), + ), + child: const Icon(Icons.error_outline, color: Colors.white), + ); + }, ), ), title: Text( diff --git a/pubspec.lock b/pubspec.lock index 4d91e8f..f41231c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -472,6 +472,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.1" + shimmer: + dependency: "direct main" + description: + name: shimmer + sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9" + url: "https://pub.dev" + source: hosted + version: "3.0.0" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index bdc9963..786ce61 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,6 +43,7 @@ dependencies: shared_preferences: ^2.3.2 dio: ^5.7.0 text_marquee: ^0.0.2 + shimmer: ^3.0.0 dev_dependencies: flutter_test: @@ -191,7 +192,6 @@ flutter: - family: Product-Sans fonts: - asset: fonts/Product-Sans/Product-Sans-Regular.ttf - # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages