Simplify state management on search suggestion screen to get rid of an edge case bug + add comments + use better variable names

This commit is contained in:
ashilkn 2023-12-23 22:18:40 +05:30
parent 7c94c579ea
commit 234c1ad15e
3 changed files with 94 additions and 167 deletions

View file

@ -1,57 +0,0 @@
import "package:flutter/cupertino.dart";
import "package:photos/models/search/search_result.dart";
import "package:photos/models/typedefs.dart";
class SearchResultsProvider extends StatefulWidget {
final Widget child;
const SearchResultsProvider({
required this.child,
super.key,
});
@override
State<SearchResultsProvider> createState() => _SearchResultsProviderState();
}
class _SearchResultsProviderState extends State<SearchResultsProvider> {
Stream<List<SearchResult>>? searchResultsStream;
@override
Widget build(BuildContext context) {
return InheritedSearchResults(
searchResultsStream,
updateSearchResults,
child: widget.child,
);
}
void updateSearchResults(Stream<List<SearchResult>> newStream) {
setState(() {
print(
"_____updating stream from ${searchResultsStream.hashCode} to ${newStream.hashCode} in inherited widget",
);
searchResultsStream = null;
searchResultsStream = newStream;
});
}
}
class InheritedSearchResults extends InheritedWidget {
final Stream<List<SearchResult>>? searchResultsStream;
final VoidCallbackParamSearchResutlsStream updateStream;
const InheritedSearchResults(
this.searchResultsStream,
this.updateStream, {
required super.child,
super.key,
});
static InheritedSearchResults of(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType<InheritedSearchResults>()!;
}
@override
bool updateShouldNotify(covariant InheritedSearchResults oldWidget) {
return searchResultsStream != oldWidget.searchResultsStream;
}
}

View file

@ -33,7 +33,6 @@ import 'package:photos/services/local_sync_service.dart';
import "package:photos/services/notification_service.dart";
import 'package:photos/services/update_service.dart';
import 'package:photos/services/user_service.dart';
import "package:photos/states/search_results_state.dart";
import 'package:photos/states/user_details_state.dart';
import 'package:photos/theme/colors.dart';
import "package:photos/theme/effects.dart";
@ -393,90 +392,88 @@ class _HomeWidgetState extends State<HomeWidget> {
!LocalSyncService.instance.hasGrantedLimitedPermissions() &&
CollectionsService.instance.getActiveCollections().isEmpty;
return SearchResultsProvider(
child: Stack(
children: [
Builder(
builder: (context) {
return ExtentsPageView(
onPageChanged: (page) {
Bus.instance.fire(
TabChangedEvent(
page,
TabChangedEventSource.pageView,
),
);
},
controller: _pageController,
openDrawer: Scaffold.of(context).openDrawer,
physics: const BouncingScrollPhysics(),
children: [
_showShowBackupHook
? const StartBackupHookWidget(headerWidget: _headerWidget)
: HomeGalleryWidget(
header: _headerWidget,
footer: const SizedBox(
height: 160,
),
selectedFiles: _selectedFiles,
),
_userCollectionsTab,
_sharedCollectionTab,
_searchTab,
],
);
},
),
Align(
alignment: Alignment.bottomCenter,
child: ValueListenableBuilder(
valueListenable: isOnSearchTabNotifier,
builder: (context, value, child) {
return Container(
decoration: value
? BoxDecoration(
color: getEnteColorScheme(context).backgroundElevated,
boxShadow: shadowFloatFaintLight,
)
: null,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
value
? const SearchWidget()
.animate()
.fadeIn(
duration: const Duration(milliseconds: 225),
curve: Curves.easeInOutSine,
)
.scale(
begin: const Offset(0.8, 0.8),
end: const Offset(1, 1),
duration: const Duration(
milliseconds: 225,
),
curve: Curves.easeInOutSine,
)
.slide(
begin: const Offset(0, 0.4),
curve: Curves.easeInOutSine,
duration: const Duration(
milliseconds: 225,
),
)
: const SizedBox.shrink(),
HomeBottomNavigationBar(
_selectedFiles,
selectedTabIndex: _selectedTabIndex,
),
],
return Stack(
children: [
Builder(
builder: (context) {
return ExtentsPageView(
onPageChanged: (page) {
Bus.instance.fire(
TabChangedEvent(
page,
TabChangedEventSource.pageView,
),
);
},
),
controller: _pageController,
openDrawer: Scaffold.of(context).openDrawer,
physics: const BouncingScrollPhysics(),
children: [
_showShowBackupHook
? const StartBackupHookWidget(headerWidget: _headerWidget)
: HomeGalleryWidget(
header: _headerWidget,
footer: const SizedBox(
height: 160,
),
selectedFiles: _selectedFiles,
),
_userCollectionsTab,
_sharedCollectionTab,
_searchTab,
],
);
},
),
Align(
alignment: Alignment.bottomCenter,
child: ValueListenableBuilder(
valueListenable: isOnSearchTabNotifier,
builder: (context, value, child) {
return Container(
decoration: value
? BoxDecoration(
color: getEnteColorScheme(context).backgroundElevated,
boxShadow: shadowFloatFaintLight,
)
: null,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
value
? const SearchWidget()
.animate()
.fadeIn(
duration: const Duration(milliseconds: 225),
curve: Curves.easeInOutSine,
)
.scale(
begin: const Offset(0.8, 0.8),
end: const Offset(1, 1),
duration: const Duration(
milliseconds: 225,
),
curve: Curves.easeInOutSine,
)
.slide(
begin: const Offset(0, 0.4),
curve: Curves.easeInOutSine,
duration: const Duration(
milliseconds: 225,
),
)
: const SizedBox.shrink(),
HomeBottomNavigationBar(
_selectedFiles,
selectedTabIndex: _selectedTabIndex,
),
],
),
);
},
),
],
),
),
],
);
}

View file

@ -30,43 +30,30 @@ class SearchSuggestionsWidget extends StatefulWidget {
class _SearchSuggestionsWidgetState extends State<SearchSuggestionsWidget> {
Stream<List<SearchResult>>? resultsStream;
final queueOfEvents = <List<SearchResult>>[];
final queueOfSearchResults = <List<SearchResult>>[];
var searchResultWidgets = <Widget>[];
StreamSubscription<List<SearchResult>>? subscription;
Timer? timer;
// @override
// void didChangeDependencies() {
// super.didChangeDependencies();
// searchResultWidgets.clear();
// releaseResources();
// resultsStream = InheritedSearchResults.of(context).searchResultsStream;
// subscription = resultsStream?.listen((event) {
// if (event.isNotEmpty) {
// //update a index value notifier for indexed stack here and rebuild the indexedStack widget.
// //Also, add dependecy to inherited widget on changing stream in this widget and not on the serach tab.
// queueOfEvents.add(event);
// }
// });
// generateResultWidgetsInIntervalsFromQueue();
// }
@override
void initState() {
super.initState();
SearchWidgetState.searchResultsStreamNotifier.addListener(() {
final value = SearchWidgetState.searchResultsStreamNotifier.value;
final resultsStream = SearchWidgetState.searchResultsStreamNotifier.value;
searchResultWidgets.clear();
releaseResources();
resultsStream = value;
subscription = resultsStream?.listen((event) {
if (event.isNotEmpty) {
//update a index value notifier for indexed stack here and rebuild the indexedStack widget.
//Also, add dependecy to inherited widget on changing stream in this widget and not on the serach tab.
queueOfEvents.add(event);
}
subscription = resultsStream!.listen((searchResults) {
//Currently, we add searchResults even if the list is empty. So we are adding
//empty list to the queue, which will trigger rebuilds with no change in UI
//(see [generateResultWidgetsInIntervalsFromQueue]'s setState()).
//This is needed to clear the search results in this widget when the
//search bar is cleared, and the event fired by the stream will be an
//empty list. Can optimize rebuilds if there are performance issues in future.
queueOfSearchResults.add(searchResults);
});
generateResultWidgetsInIntervalsFromQueue();
});
}
@ -81,8 +68,8 @@ class _SearchSuggestionsWidgetState extends State<SearchSuggestionsWidget> {
///generates the widgets and clears the queue and updates the UI.
void generateResultWidgetsInIntervalsFromQueue() {
timer = Timer.periodic(const Duration(milliseconds: 50), (timer) {
if (queueOfEvents.isNotEmpty) {
for (List<SearchResult> event in queueOfEvents) {
if (queueOfSearchResults.isNotEmpty) {
for (List<SearchResult> event in queueOfSearchResults) {
for (SearchResult result in event) {
searchResultWidgets.add(
SearchResultsWidgetGenerator(result).animate().fadeIn(
@ -92,7 +79,7 @@ class _SearchSuggestionsWidgetState extends State<SearchSuggestionsWidget> {
);
}
}
queueOfEvents.clear();
queueOfSearchResults.clear();
setState(() {});
}
});