Skip to content

Commit

Permalink
feat: use search floating button as submit beside closing search page…
Browse files Browse the repository at this point in the history
… (switches dynamically)
  • Loading branch information
MSOB7YY committed Nov 22, 2024
1 parent 66cc92b commit e210ece
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 60 deletions.
1 change: 1 addition & 0 deletions lib/controller/scroll_search_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class ScrollSearchController {
final currentSearchType = SearchType.localTracks.obs;

final isGlobalSearchMenuShown = false.obs;
final latestSubmittedYTSearch = ''.obs;
final TextEditingController searchTextEditingController = TextEditingController();

final Map<LibraryTab, Rx<bool>> isSearchBoxVisibleMap = <LibraryTab, Rx<bool>>{};
Expand Down
175 changes: 116 additions & 59 deletions lib/ui/pages/main_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,65 +59,7 @@ class MainPage extends StatelessWidget {
),
);

final searchProgressWidget = Builder(builder: (context) {
return CircularProgressIndicator(
strokeWidth: 2.0,
strokeCap: StrokeCap.round,
color: context.theme.colorScheme.onSecondaryContainer.withOpacity(0.4),
);
});

final fabChild = Builder(
builder: (context) => NamidaTooltip(
message: () => ScrollSearchController.inst.isGlobalSearchMenuShown.value ? lang.CLEAR : settings.floatingActionButton.value.toText(),
child: FloatingActionButton(
heroTag: 'main_page_fab_hero',
backgroundColor: Color.alphaBlend(CurrentColor.inst.currentColorScheme.withOpacity(0.6), context.theme.cardColor),
onPressed: () {
final fab = settings.floatingActionButton.value;
final isMenuOpened = ScrollSearchController.inst.isGlobalSearchMenuShown.value;
if (fab == FABType.search || isMenuOpened) {
final isOpen = ScrollSearchController.inst.searchBarKey.currentState?.isOpen ?? false;
if (isOpen && !isMenuOpened) {
SearchSortController.inst.prepareResources();
ScrollSearchController.inst.showSearchMenu();
ScrollSearchController.inst.searchBarKey.currentState?.focusNode.requestFocus();
} else {
isMenuOpened ? SearchSortController.inst.disposeResources() : SearchSortController.inst.prepareResources();
ScrollSearchController.inst.toggleSearchMenu();
ScrollSearchController.inst.searchBarKey.currentState?.openCloseSearchBar();
}
} else if (fab == FABType.shuffle || fab == FABType.play) {
Player.inst.playOrPause(0, SelectedTracksController.inst.getCurrentAllTracks(), QueueSource.allTracks, shuffle: fab == FABType.shuffle);
}
},
child: ObxO(
rx: ScrollSearchController.inst.isGlobalSearchMenuShown,
builder: (context, isGlobalSearchMenuShown) => isGlobalSearchMenuShown
? ObxO(
rx: SearchSortController.inst.runningSearchesTempCount,
builder: (context, runningSearchesCount) => Stack(
alignment: Alignment.center,
children: [
const Icon(
Broken.search_status_1,
color: Color.fromRGBO(255, 255, 255, 0.8),
),
if (runningSearchesCount > 0) searchProgressWidget,
],
),
)
: ObxO(
rx: settings.floatingActionButton,
builder: (context, fabButton) => Icon(
fabButton.toIcon(),
color: const Color.fromRGBO(255, 255, 255, 0.8),
),
),
),
),
),
);
final fabChild = _MainPageFABButton();
final mainChild = Scaffold(
resizeToAvoidBottomInset: false,
appBar: PreferredSize(
Expand Down Expand Up @@ -269,10 +211,124 @@ class MainPage extends StatelessWidget {
}
}

class _MainPageFABButton extends StatefulWidget {
const _MainPageFABButton();

@override
State<_MainPageFABButton> createState() => __MainPageFABButtonState();
}

class __MainPageFABButtonState extends State<_MainPageFABButton> {
@override
void initState() {
super.initState();
ScrollSearchController.inst.searchTextEditingController.addListener(_onControllerValueChangedListener);
ScrollSearchController.inst.latestSubmittedYTSearch.addListener(_onControllerValueChangedListener);
_onControllerValueChangedListener();
}

@override
void dispose() {
ScrollSearchController.inst.searchTextEditingController.removeListener(_onControllerValueChangedListener);
ScrollSearchController.inst.latestSubmittedYTSearch.removeListener(_onControllerValueChangedListener);
super.dispose();
}

bool _shouldShowSubmitSearch = false;

void _onControllerValueChangedListener() {
final val = ScrollSearchController.inst.searchTextEditingController.text;
final latestSubmitted = ScrollSearchController.inst.latestSubmittedYTSearch.value;

if (val == latestSubmitted || val.isEmpty) {
if (_shouldShowSubmitSearch != false) {
setState(() => _shouldShowSubmitSearch = false);
}
} else {
if (_shouldShowSubmitSearch != true) {
setState(() => _shouldShowSubmitSearch = true);
}
}
}

@override
Widget build(BuildContext context) {
final searchProgressWidget = Builder(builder: (context) {
return CircularProgressIndicator(
strokeWidth: 2.0,
strokeCap: StrokeCap.round,
color: context.theme.colorScheme.onSecondaryContainer.withOpacity(0.4),
);
});
return Builder(
builder: (context) => NamidaTooltip(
message: () => ScrollSearchController.inst.isGlobalSearchMenuShown.value ? lang.CLEAR : settings.floatingActionButton.value.toText(),
child: FloatingActionButton(
heroTag: 'main_page_fab_hero',
backgroundColor: Color.alphaBlend(CurrentColor.inst.currentColorScheme.withOpacity(0.6), context.theme.cardColor),
onPressed: () {
final fab = settings.floatingActionButton.value;
final isMenuOpened = ScrollSearchController.inst.isGlobalSearchMenuShown.value;
if (fab == FABType.search || isMenuOpened) {
if (_shouldShowSubmitSearch && ScrollSearchController.inst.currentSearchType.value == SearchType.youtube) {
ScrollSearchController.inst.searchBarWidget.submit(ScrollSearchController.inst.searchTextEditingController.text);
return;
}
final isOpen = ScrollSearchController.inst.searchBarKey.currentState?.isOpen ?? false;
if (isOpen && !isMenuOpened) {
SearchSortController.inst.prepareResources();
ScrollSearchController.inst.showSearchMenu();
ScrollSearchController.inst.searchBarKey.currentState?.focusNode.requestFocus();
} else {
isMenuOpened ? SearchSortController.inst.disposeResources() : SearchSortController.inst.prepareResources();
ScrollSearchController.inst.toggleSearchMenu();
ScrollSearchController.inst.searchBarKey.currentState?.openCloseSearchBar();
}
} else if (fab == FABType.shuffle || fab == FABType.play) {
Player.inst.playOrPause(0, SelectedTracksController.inst.getCurrentAllTracks(), QueueSource.allTracks, shuffle: fab == FABType.shuffle);
}
},
child: ObxO(
rx: ScrollSearchController.inst.isGlobalSearchMenuShown,
builder: (context, isGlobalSearchMenuShown) => isGlobalSearchMenuShown
? ObxO(
rx: SearchSortController.inst.runningSearchesTempCount,
builder: (context, runningSearchesCount) => Stack(
alignment: Alignment.center,
children: [
ObxO(
rx: ScrollSearchController.inst.currentSearchType,
builder: (context, currentSearchType) => Icon(
_shouldShowSubmitSearch && currentSearchType == SearchType.youtube ? Broken.search_normal : Broken.shield_slash,
color: Color.fromRGBO(255, 255, 255, 0.8),
),
),
if (runningSearchesCount > 0) searchProgressWidget,
],
),
)
: ObxO(
rx: settings.floatingActionButton,
builder: (context, fabButton) => Icon(
fabButton.toIcon(),
color: const Color.fromRGBO(255, 255, 255, 0.8),
),
),
),
),
),
);
}
}

class NamidaSearchBar extends StatelessWidget {
final GlobalKey<SearchBarAnimationState> searchBarKey;
const NamidaSearchBar({super.key, required this.searchBarKey});

void submit(String val) {
_onSubmitted(val);
}

void _onSubmitted(String val) {
final didOpen = NamidaLinkUtils.tryOpeningPlaylistOrVideo(val);
if (didOpen) {
Expand All @@ -281,6 +337,7 @@ class NamidaSearchBar extends StatelessWidget {
}

if (ScrollSearchController.inst.currentSearchType.value == SearchType.youtube) {
ScrollSearchController.inst.latestSubmittedYTSearch.value = val;
ScrollSearchController.inst.ytSearchKey.currentState?.fetchSearch(customText: val);
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/ui/pages/search_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class SearchPage extends StatelessWidget {
ScrollSearchController.inst.currentSearchType.value = SearchType.youtube;
final searchValue = ScrollSearchController.inst.ytSearchKey.currentState?.currentSearchText;
if (SearchSortController.inst.lastSearchText != searchValue) {
ScrollSearchController.inst.latestSubmittedYTSearch.value = SearchSortController.inst.lastSearchText;
ScrollSearchController.inst.ytSearchKey.currentState?.fetchSearch(customText: SearchSortController.inst.lastSearchText);
}
break;
Expand Down
1 change: 1 addition & 0 deletions lib/youtube/pages/yt_search_results_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class YoutubeSearchResultsPageState extends State<YoutubeSearchResultsPage> with

Future<void> fetchSearch({String customText = ''}) async {
final newSearch = customText == '' ? widget.searchTextCallback?.call() ?? '' : customText;
if (_latestSearched == newSearch && _searchResult != null) return;
_latestSearched = newSearch;

YTLocalSearchController.inst.search(newSearch);
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: namida
description: A Beautiful and Feature-rich Music Player, With YouTube & Video Support Built in Flutter
publish_to: "none"
version: 4.7.19-beta+241122158
version: 4.7.2-beta+241122174

environment:
sdk: ">=3.4.0 <4.0.0"
Expand Down

0 comments on commit e210ece

Please sign in to comment.