Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example/lib/widgets/async_searchable_listview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class AsyncSearchableListview extends StatelessWidget {
},
asyncListFilter: (query, list) async {
await Future.delayed(const Duration(seconds: 3));
var result = actors
var result = list
.where((element) =>
element.name.contains(query) ||
element.lastName.contains(query))
Expand Down
40 changes: 35 additions & 5 deletions lib/searchable_listview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,7 @@ class _SearchableListState<T> extends State<SearchableList<T>> {
CancelableOperation<List<T>?>? _activeOperation;
late Debouncer? _debouncer;
List<ExpansionTileController> expansionTileControllers = [];
bool isAsyncCallBackRunning = true;

@override
void initState() {
Expand All @@ -541,7 +542,7 @@ class _SearchableListState<T> extends State<SearchableList<T>> {
if (widget.asyncListCallback != null) {
// Load the initial list for the async constructor
WidgetsBinding.instance.addPostFrameCallback((_) async {
_asyncFilter("");
await _asyncInitialList();
if (mounted) setState(() {});
});
_debouncer = Debouncer(milliseconds: widget.asyncDebounceTime);
Expand Down Expand Up @@ -612,7 +613,7 @@ class _SearchableListState<T> extends State<SearchableList<T>> {
if (asyncError) {
return widget.errorWidget ?? const DefaultErrorWidget();
}
if (_activeOperation != null) {
if (_activeOperation != null || isAsyncCallBackRunning) {
return widget.loadingWidget ?? const DefaultLoadingWidget();
}
return renderSearchableListView();
Expand Down Expand Up @@ -799,8 +800,9 @@ class _SearchableListState<T> extends State<SearchableList<T>> {
} else if (widget.asyncListCallback != null) {
// Debouncer always has a value in this case
_debouncer!.run(() async {
if (mounted) setState(() {});
_activeOperation?.cancel();
_asyncFilter(value);
await _asyncFilter(value);
if (mounted) setState(() {});
});
} else {
Expand Down Expand Up @@ -862,6 +864,11 @@ class _SearchableListState<T> extends State<SearchableList<T>> {
}

Future<void> _asyncFilter(String value) async {
if (isAsyncCallBackRunning) {
// Wait for the initial list before filtering
return;
}

final operation = CancelableOperation.fromFuture(
widget.asyncListFilter!(
value,
Expand All @@ -879,11 +886,34 @@ class _SearchableListState<T> extends State<SearchableList<T>> {
asyncError = true;
}
} finally {
// Make sure to not interrupt an other operation
// Make sure to not interrupt another operation
if (_activeOperation == operation) {
_activeOperation = null;
}
}
setState(() {});
}

Future<void> _asyncInitialList() async {
isAsyncCallBackRunning = true;
// First get the initial list
try {
final initialData = await widget.asyncListCallback!();
if (initialData != null) {
asyncListResult = initialData;
} else {
asyncError = true;
}
Comment thread
julien-levarlet marked this conversation as resolved.
} catch (e) {
asyncError = true;
return;
}
Comment thread
julien-levarlet marked this conversation as resolved.
isAsyncCallBackRunning = false;
// Then check if a query was typed during the initial list loading to filter if needed
if ((widget.searchTextController?.text ?? "") != "") {
await _asyncFilter(widget.searchTextController!.text);
} else {
filtredAsyncListResult.clear();
filtredAsyncListResult.addAll(asyncListResult);
}
}
}
Loading