Use case
I use the SearchableList.async widget in my app to fetch data from a database and display it to the user. But with a large database, it feels that it is not very convenient.
With the current version of the async constructor, I have to get all my objects from my database in the asyncListCallback (so all of them are copied in memory). And then when filtering, I have to iterate over all element to check if it contains the string. This is not very efficient for many elements.
Proposal
What I would like to do is to call a filtering function I wrote (which is async) that fetch a subset of elements from my database that contains the string. This function is easier to optimize, and limit jank on older phones.
As an example I wrote this code that create a search bar with an async filter function.
class SearchList extends StatefulWidget {
const SearchList({
super.key,
});
@override
State<SearchList> createState() => _SearchListState();
}
class _SearchListState extends State<SearchList> {
// This will hold the results from our database search
List<MyObjects> _filtered = [];
// The debouncer will wait 300ms after the user stops typing
final _debouncer = Debouncer(milliseconds: 300);
final TextEditingController _textController = TextEditingController();
bool _isLoading = false;
final Key _listViewKey = UniqueKey();
@override
void initState() {
super.initState();
// Fetch an initial list
_fetch("");
}
/// Fetches the database based on the search query.
Future<void> _fetch(String query) async {
if (mounted) setState(() => _isLoading = true);
// Call the DAO with the actual query. We add a limit to keep it snappy.
final results = await MyDatabase.instance.get(query, limit: 100);
if (mounted) {
setState(() {
_filtered = results;
_isLoading = false;
});
}
}
@override
void dispose() {
_debouncer.dispose();
_textController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: _textController,
onChanged: (query) {
_debouncer.run(() => _fetch(query));
},
decoration: InputDecoration(
suffixIcon: _isLoading
? const Padding(
padding: EdgeInsets.all(10.0),
child: SizedBox(
height: 10,
width: 10,
child: CircularProgressIndicator(strokeWidth: 2.0)),
)
: const Icon(Icons.search),
hintText: "Search",
border: OutlineInputBorder(
borderSide: BorderSide(
width: 8.0,
color: Theme.of(context).colorScheme.primary,
),
borderRadius: BorderRadius.circular(5.0),
),
),
),
const SizedBox(height: 10),
Expanded(
child: _filtered.isEmpty && !_isLoading
? Center(
child: Text(
"Not found",
textAlign: TextAlign.center,
),
)
: ListView.separated(
key: _listViewKey,
padding: const EdgeInsets.symmetric(vertical: 10),
itemCount: _filtered.length,
itemBuilder: (context, index) {
return MyTile(myObject: _filtered[index]);
},
separatorBuilder: (context, index) {
return const SizedBox(height: 5);
},
)),
],
);
}
}
Do you think this use case is relevant for your package ?
If yes, I will try to integrate it in the async constructor.
Use case
I use the
SearchableList.asyncwidget in my app to fetch data from a database and display it to the user. But with a large database, it feels that it is not very convenient.With the current version of the async constructor, I have to get all my objects from my database in the
asyncListCallback(so all of them are copied in memory). And then when filtering, I have to iterate over all element to check if it contains the string. This is not very efficient for many elements.Proposal
What I would like to do is to call a filtering function I wrote (which is async) that fetch a subset of elements from my database that contains the string. This function is easier to optimize, and limit jank on older phones.
As an example I wrote this code that create a search bar with an async filter function.
Do you think this use case is relevant for your package ?
If yes, I will try to integrate it in the async constructor.