Flutter Web自定义搜索组件选中搜索结果需点击两次问题求助
问题根因
- 每次页面触发build时,
FutureBuilder都会重新执行loadData()方法生成新的Future实例,没有对已加载的搜索结果做缓存。你给TextEditingController加了全局监听,每次文本变化/输入框失焦都会触发setState重建页面,导致搜索结果区域频繁销毁重建。 - 输入完成后输入框处于获焦状态,第一次点击结果项时系统会优先触发输入框失焦逻辑,失焦立即触发
setState重建页面,原本渲染的结果项被销毁,对应的onTap事件直接丢失,第二次点击时没有重建逻辑打断,才能正常触发点击事件。
修复方案
- 新增变量缓存搜索请求的Future实例,仅在输入文本实际变化时才重新发起搜索请求,避免每次build都重复加载
- 优化监听逻辑,同时点击结果项时优先清除输入框焦点,避免失焦触发不必要的重建
修复后完整代码
import 'package:flutter/material.dart'; void main() { runApp(MaterialApp( home: SearchView(), )); } class SearchView extends StatefulWidget { @override State<SearchView> createState() => _SearchViewState(); } class _SearchViewState extends State<SearchView> { String searchResult = ''; final textController = TextEditingController(); final List<String> data = ['Result 1', 'Result 2', 'Result 3', 'Result 4']; // 新增变量缓存搜索Future实例 Future<List<String>>? _searchFuture; Future<List<String>> loadData() { return Future.delayed(const Duration(seconds: 1), () { if (textController.text.trim().isNotEmpty) { return data; } else { return []; } }); } @override void initState() { super.initState(); textController.addListener(_onTextChanged); } void _onTextChanged() { print('text changed'); // 仅在文本变化时更新搜索Future,无意义的全局setState setState(() { _searchFuture = loadData(); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Brand'), ), body: SingleChildScrollView( child: Column( children: [ TextFormField( controller: textController, ), FutureBuilder( // 绑定缓存的Future实例,不会每次build都重新生成 future: _searchFuture, builder: (BuildContext context, AsyncSnapshot<List<String>> snapshot) { if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) { print("future build"); return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ for (String result in snapshot.data!) InkWell( onTap: () { print('Clicked'); // 点击时先清除焦点,避免失焦触发重建导致事件丢失 FocusScope.of(context).unfocus(); setState(() { textController.clear(); searchResult = result; }); }, child: Padding( padding: const EdgeInsets.all(16.0), child: Text(result), ), ), ], ); } else if(snapshot.connectionState == ConnectionState.waiting){ return const CircularProgressIndicator(); }else{ return const SizedBox.shrink(); } }, ), Text('Search result is $searchResult'), ], ), ), ); } }
内容的提问来源于stack exchange,提问作者rosh-dev851
相关产品推荐
相关产品推荐

