在我的 search_user.dart 中,我希望当前用户每次更新文本字段中的输入时都能看到可能匹配的列表。现在,我担心我会不必要地多次查询 Firestore 来执行此操作,如果我有超过 1000 个用户,我可能会收到相当昂贵的 Firebase 账单。我很想知道是否有人有更好的解决方案或知道可以帮助我处理此 UI 反馈的扩展/包。
此文件也不支持用户名和/或名称搜索。如果有人有解决方案,我很想听听!
// ---- search_user.dart ----
// method for updating Future<QuerySnapshot>? _searchResults
// I could be wrong, but I'm using the username from Provider instead of calling
// firebase in hopes that I don't make an unecessary read...
void _search() {
final userUsername =
Provider.of<UserProvider>(context, listen: false).user?.username;
setState(() {
_searchResults = FirebaseFirestore.instance
.collection('users')
.where('username', isNotEqualTo: userUsername)
.where('username',
isGreaterThanOrEqualTo:
"@${_searchController.text.toLowerCase()}")
.get();
});
}
// search text field widget
// to give the user a sense of feedback, im using the onChanged to render the list
// of users every time they add a letter. I feel this is better for the client but
// I fear that I'm making too many reads at a time and would love to know of a better
// solution. Im open to third party extensions or packages
Expanded(
child: TextField(
controller: _searchController,
style: Theme.of(context).textTheme.labelMedium,
decoration: InputDecoration(
hintText: 'Search by username',
hintStyle: Theme.of(context).textTheme.labelMedium,
filled: true,
fillColor: Theme.of(context).colorScheme.onSecondary,
prefixIcon: Icon(Icons.search,
color: Theme.of(context).iconTheme.color, size: 25),
suffixIcon: Visibility(
visible: _searchController.text.isNotEmpty,
child: IconButton(
onPressed: () => _searchController.clear(),
icon: Icon(Icons.cancel,
color: Theme.of(context).iconTheme.color, size: 25),
),
),
contentPadding: EdgeInsets.zero,
// contentPadding: EdgeInsets.zero,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30),
borderSide: BorderSide.none,
),
),
onChanged: (value) {
_search();
},
),
),
我尝试将搜索限制为仅当用户单击搜索按钮时才显示结果,但是,我认为此 UI 反馈对于不太了解他们正在查找的确切用户名的用户来说还不够。使用 onChanged 似乎是提供此类客户端反馈的唯一合适方法,但是,当需要从 Firestore 读取用户文档时,这可能是一个糟糕的选择。
是的,还有更好的方法。
您列出了 2 个问题:
对于问题 1,您可以在 Firestore 文档字段中创建一个名为“searchTerms”的字段,例如,包含带有该文档的可能搜索词的字符串列表。然后,当您创建或更新其中任何一个时,您可以使用名称和用户名的可能值更新此列表:
对于问题2,您可以实现一个去抖动器。这允许应用程序等到用户停止输入指定的持续时间后再执行搜索,从而减少触发的搜索数量
import 'dart:async';
class Debouncer {
final int milliseconds;
VoidCallback action;
Timer _timer;
Debouncer({this.milliseconds});
run(VoidCallback action) {
if (_timer != null) {
_timer.cancel();
}
_timer = Timer(Duration(milliseconds: milliseconds), action);
}
}
用途:
final _debouncer = Debouncer(milliseconds: 500);
List<Widget> _searchResults = [];
void _onSearchChanged(String query) {
_debouncer.run(() {
setState(() {
_searchResult = query;
// Your search logic here
final userUsername =
Provider.of<UserProvider>(context, listen: false).user?.username;
_searchResults = FirebaseFirestore.instance
.collection('users')
.where('username', isNotEqualTo: userUsername)
.where(ClientsDBFieldsNames.searchTerms,
arrayContains: query);
.get();
});
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Debouncer Example'),
),
body: Column(
children: [
Padding(
padding: EdgeInsets.all(8.0),
child: TextField(
onChanged: _onSearchChanged,
decoration: InputDecoration(
labelText: 'Search',
suffixIcon: Icon(Icons.search),
),
),
),
Expanded(
child: Center(
child: Text(_searchResult),
),
),
],
),
);
}
}
附注:
从 UI 直接访问 Firestore 并不是一个好的做法。建议学习设计模式和依赖倒置。
您可以为整个应用程序创建一个单例 Debouncer,以限制对 Firestore 的调用。这可以根据您传递 ID 的函数来完成,并且去抖器根据建立的频率和限制确定它是否可以执行。这样,例如,在无限循环的情况下,您将不会收到比必要的大得多的账单。