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:
parent
7c94c579ea
commit
234c1ad15e
3 changed files with 94 additions and 167 deletions
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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(() {});
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue