@@ -37,11 +37,13 @@ import 'package:musify/utilities/app_utils.dart';
3737import 'package:musify/utilities/flutter_toast.dart' ;
3838import 'package:musify/utilities/offline_playlist_dialogs.dart' ;
3939import 'package:musify/utilities/playlist_dialogs.dart' ;
40+ import 'package:musify/utilities/song_filtering.dart' ;
4041import 'package:musify/utilities/sort_utils.dart' ;
41- import 'package:musify/widgets/custom_search_bar.dart' ;
4242import 'package:musify/widgets/edit_playlist_dialog.dart' ;
4343import 'package:musify/widgets/playlist_cube.dart' ;
44+ import 'package:musify/widgets/playlist_page/empty_playlist_state.dart' ;
4445import 'package:musify/widgets/playlist_page/playlist_header.dart' ;
46+ import 'package:musify/widgets/playlist_page/search_bar_section.dart' ;
4547import 'package:musify/widgets/song_bar.dart' ;
4648import 'package:musify/widgets/sort_chips.dart' ;
4749import 'package:musify/widgets/spinner.dart' ;
@@ -83,19 +85,13 @@ class _PlaylistPageState extends State<PlaylistPage> {
8385 );
8486
8587 // Search
86- String _searchQuery = '' ;
88+ final ValueNotifier < String > _searchQueryNotifier = ValueNotifier ( '' ) ;
8789 late final TextEditingController _searchController;
8890 late final FocusNode _searchFocusNode;
8991
90- List <dynamic > get _sourceList {
92+ List <dynamic > _getSourceList ( String searchQuery) {
9193 final list = _playlist? ['list' ] as List <dynamic >? ?? [];
92- if (_searchQuery.isEmpty) return list;
93- final q = _searchQuery.toLowerCase ();
94- return list.where ((s) {
95- final title = (s['title' ] ?? '' ).toString ().toLowerCase ();
96- final artist = (s['artist' ] ?? '' ).toString ().toLowerCase ();
97- return title.contains (q) || artist.contains (q);
98- }).toList ();
94+ return filterSongsByQuery (list, searchQuery);
9995 }
10096
10197 @override
@@ -110,6 +106,7 @@ class _PlaylistPageState extends State<PlaylistPage> {
110106 void dispose () {
111107 _searchController.dispose ();
112108 _searchFocusNode.dispose ();
109+ _searchQueryNotifier.dispose ();
113110 super .dispose ();
114111 }
115112
@@ -171,53 +168,29 @@ class _PlaylistPageState extends State<PlaylistPage> {
171168 slivers: [
172169 SliverToBoxAdapter (child: _buildHeaderSection ()),
173170 if ((_playlist['list' ] as List ).isNotEmpty) ...[
174- SliverPadding (
175- padding: commonListViewBottomPadding,
176- sliver: SliverList .builder (
177- itemCount: _sourceList.length,
178- itemBuilder: (context, index) {
179- final isRemovable =
180- _playlist['source' ] == 'user-created' ;
181- return _buildSongListItem (
182- _sourceList[index],
183- index,
184- isRemovable,
185- );
186- },
187- ),
188- ),
189- ] else
190- SliverFillRemaining (
191- hasScrollBody: false ,
192- child: Center (
193- child: Padding (
194- padding: const EdgeInsets .symmetric (horizontal: 32 ),
195- child: Column (
196- mainAxisSize: MainAxisSize .min,
197- children: [
198- Icon (
199- FluentIcons .music_note_1_24_regular,
200- size: 64 ,
201- color: Theme .of (
202- context,
203- ).colorScheme.onSurface.withAlpha (120 ),
204- ),
205- const SizedBox (height: 16 ),
206- Text (
207- context.l10n! .noSongsInPlaylist,
208- textAlign: TextAlign .center,
209- style: TextStyle (
210- color: Theme .of (
211- context,
212- ).colorScheme.onSurfaceVariant,
213- fontSize: 16 ,
214- ),
215- ),
216- ],
171+ ValueListenableBuilder <String >(
172+ valueListenable: _searchQueryNotifier,
173+ builder: (context, searchQuery, _) {
174+ final sourceList = _getSourceList (searchQuery);
175+ return SliverPadding (
176+ padding: commonListViewBottomPadding,
177+ sliver: SliverList .builder (
178+ itemCount: sourceList.length,
179+ itemBuilder: (context, index) {
180+ final isRemovable =
181+ _playlist['source' ] == 'user-created' ;
182+ return _buildSongListItem (
183+ sourceList[index],
184+ index,
185+ isRemovable,
186+ );
187+ },
217188 ),
218- ),
219- ) ,
189+ );
190+ } ,
220191 ),
192+ ] else
193+ EmptyPlaylistState (message: context.l10n! .noSongsInPlaylist),
221194 ],
222195 )
223196 : SizedBox (
@@ -334,12 +307,11 @@ class _PlaylistPageState extends State<PlaylistPage> {
334307 ],
335308 if (songsLength > 0 ) ...[
336309 const SizedBox (height: 16 ),
337- CustomSearchBar (
338- controller: _searchController..text = _searchQuery ,
310+ SearchBarSection (
311+ controller: _searchController,
339312 focusNode: _searchFocusNode,
313+ onSearchChanged: (value) => _searchQueryNotifier.value = value,
340314 labelText: context.l10n! .search,
341- onSubmitted: (_) {},
342- onChanged: (value) => setState (() => _searchQuery = value),
343315 ),
344316 ],
345317 const SizedBox (height: 16 ),
@@ -362,7 +334,11 @@ class _PlaylistPageState extends State<PlaylistPage> {
362334 showToast (context, context.l10n! .linkCopied);
363335 }
364336 } catch (e, stackTrace) {
365- logger.log ('Error sharing playlist' , error: e, stackTrace: stackTrace);
337+ logger.log (
338+ 'Error sharing playlist' ,
339+ error: e,
340+ stackTrace: stackTrace,
341+ );
366342 if (mounted) {
367343 showToast (context, context.l10n! .error);
368344 }
@@ -696,13 +672,14 @@ class _PlaylistPageState extends State<PlaylistPage> {
696672 }
697673
698674 Widget _buildSongListItem (dynamic song, int index, bool isRemovable) {
699- final totalItems = _sourceList.length;
675+ final sourceList = _getSourceList (_searchQueryNotifier.value);
676+ final totalItems = sourceList.length;
700677 final borderRadius = getItemBorderRadius (index, totalItems);
701678 final isUserCreatedPlaylist = _playlist? ['source' ] == 'user-created' ;
702679 final playlistId = isUserCreatedPlaylist ? _playlist! ['ytid' ] : null ;
703- final isSearching = _searchQuery .isNotEmpty;
680+ final isSearching = _searchQueryNotifier.value .isNotEmpty;
704681 final playlistForQueue = isSearching
705- ? {..._playlist as Map , 'list' : _sourceList }
682+ ? {..._playlist as Map , 'list' : sourceList }
706683 : _playlist;
707684
708685 return SongBar (
0 commit comments