实现BiGram全文搜索框遇错误:Future无snap属性
Flutter BiGram全文搜索框错误修复
错误原因
nGram()是异步函数,返回类型为Future<dynamic>,不存在snap属性,直接调用nGram().snap会触发错误。nGram()内部未返回查询结果,FutureBuilder无法获取Firestore数据。- Firestore查询逻辑错误:循环中每次重新创建查询实例,最终仅应用最后一个BiGram词的条件,而非叠加所有条件。
- 初始化与搜索触发逻辑混乱:
initState调用nGram()无实际意义,搜索提交时未正确触发查询并传递结果。
修正后的完整代码
SearchScreen组件
class SearchScreen extends StatefulWidget { const SearchScreen({Key? key}) : super(key: key); @override State<SearchScreen> createState() => _SearchScreenState(); } class _SearchScreenState extends State<SearchScreen> { final TextEditingController searchController = TextEditingController(); bool isShowSearchResults = false; bool isLoading = false; QuerySnapshot? searchSnapshot; // 存储搜索结果 // 修正nGram函数,返回QuerySnapshot Future<QuerySnapshot> nGram(String searchTerm) async { setState(() { isLoading = true; }); try { Map<String, dynamic> searchWords = tokenToSearch(searchTerm: searchTerm); var query = FirebaseFirestore.instance.collection('posts').limit(30); // 叠加所有BiGram词的查询条件(AND关系) searchWords.forEach((word, _) { query = query.where("token.${word}", isEqualTo: true); }); final snap = await query.get(); setState(() { searchSnapshot = snap; }); return snap; } catch (e) { showSnackBar(context, e.toString()); rethrow; // 抛出错误让FutureBuilder捕获 } finally { setState(() { isLoading = false; }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Form( child: TextFormField( controller: searchController, decoration: const InputDecoration(labelText: '搜索描述...'), onFieldSubmitted: (String term) { setState(() { isShowSearchResults = true; }); // 触发搜索 nGram(term); }, ), ), ), body: isShowSearchResults ? isLoading ? const Center(child: CircularProgressIndicator()) : searchSnapshot != null ? ListView.builder( itemCount: searchSnapshot!.docs.length, itemBuilder: (context, index) { final post = searchSnapshot!.docs[index]; return ListTile( title: Text(post['description']), ); }, ) : const Center(child: Text('暂无搜索结果')) : FutureBuilder( future: FirebaseFirestore.instance .collection('posts') .orderBy('datePublished') .get(), builder: (context, snapshot) { if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } // 默认展示全部帖子列表 return ListView.builder( itemCount: snapshot.data!.docs.length, itemBuilder: (context, index) { final post = snapshot.data!.docs[index]; return ListTile( title: Text(post['description']), ); }, ); }, ), ); } } // 全局或局部实现的showSnackBar函数 void showSnackBar(BuildContext context, String message) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); }
tokenToSearch工具函数
Map<String, dynamic> tokenToSearch({required String searchTerm}) { final int nGramIndex = 2; final int length = searchTerm.length; Map<String, dynamic> tokenToSearch = {}; if (length < nGramIndex) { tokenToSearch[searchTerm] = true; } else { for (int i = 0; i < length - nGramIndex + 1; i++) { final String key = searchTerm.substring(i, i + nGramIndex); tokenToSearch[key] = true; } } return tokenToSearch; }
关键修改点
- 修正异步函数返回值:
nGram()改为接收搜索关键词并返回Future<QuerySnapshot>,确保数据能被正确传递给UI组件。 - 修复查询逻辑:循环中不再重新创建查询实例,而是在原查询基础上叠加
where条件,保证所有BiGram词的筛选规则生效。 - 优化状态管理:新增
searchSnapshot存储搜索结果,配合isLoading实现加载状态展示,避免重复发起请求。 - 调整触发逻辑:在输入框提交时调用
nGram()并传递关键词,同时切换到搜索结果展示状态。
内容的提问来源于stack exchange,提问作者Koujien
相关产品推荐
相关产品推荐

