如何通过键盘控制Flutter的Autocomplete Widget
实现RawAutocomplete的键盘导航功能
要实现Web端的键盘控制(上下箭头切换选项、回车键确认),需添加状态管理和键盘事件监听,以下是修改后的完整代码:
class _MyHomePageState extends State<MyHomePage> { final controller = TextEditingController(); final focus = FocusNode(); bool loadingAutocomplete = false; // 记录当前选中的选项索引 int _selectedOptionIndex = -1; // 存储当前的选项列表,用于键盘导航时获取对应选项 List<String> _currentOptions = []; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), ), body: RawAutocomplete( textEditingController: controller, focusNode: focus, optionsBuilder: (TextEditingValue textEditingValue) async { _currentOptions = await getCities(textEditingValue.text); // 每次获取新选项后重置选中索引 setState(() => _selectedOptionIndex = -1); return _currentOptions; }, fieldViewBuilder: (BuildContext context, TextEditingController controller, FocusNode focusNode, VoidCallback onFieldSubmitted) { return TextField( controller: controller, focusNode: focusNode, onSubmitted: (value) { // 回车时如果有选中的选项,触发选中逻辑 if (_selectedOptionIndex >= 0 && _selectedOptionIndex < _currentOptions.length) { RawAutocomplete.onSelected(_currentOptions[_selectedOptionIndex])(context); } }, // 处理键盘事件:上下箭头切换选中项 onKeyEvent: (node, event) { if (_currentOptions.isEmpty) return KeyEventResult.ignored; if (event.logicalKey == LogicalKeyboardKey.arrowDown) { setState(() { _selectedOptionIndex = (_selectedOptionIndex + 1) % _currentOptions.length; }); return KeyEventResult.handled; } else if (event.logicalKey == LogicalKeyboardKey.arrowUp) { setState(() { _selectedOptionIndex = (_selectedOptionIndex - 1) < 0 ? _currentOptions.length - 1 : _selectedOptionIndex - 1; }); return KeyEventResult.handled; } else if (event.logicalKey == LogicalKeyboardKey.enter) { if (_selectedOptionIndex >= 0 && _selectedOptionIndex < _currentOptions.length) { RawAutocomplete.onSelected(_currentOptions[_selectedOptionIndex])(context); // 选中后重置索引 setState(() => _selectedOptionIndex = -1); } return KeyEventResult.handled; } return KeyEventResult.ignored; }, decoration: const InputDecoration( labelText: 'Cities', ), ); }, optionsViewBuilder: (context, onSelected, options) => Padding( padding: const EdgeInsets.only(top: 4), child: Align( alignment: Alignment.topLeft, child: Material( color: Colors.white, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(bottom: Radius.circular(4)), ), child: SizedBox( width: 300, child: ListView.builder( padding: EdgeInsets.zero, itemCount: options.length, shrinkWrap: false, itemBuilder: (BuildContext context, int index) { final String option = options.elementAt(index); // 给当前选中的选项添加高亮背景 final isSelected = index == _selectedOptionIndex; return InkWell( onTap: () { onSelected(option); setState(() => _selectedOptionIndex = -1); }, child: Container( color: isSelected ? Colors.grey[200] : Colors.transparent, padding: const EdgeInsets.all(16.0), child: Text(option), ), ); }, ), ), ), ), ), ), ); } Future<List<String>> getCities(String value) async { await Future.delayed(const Duration(seconds: 1)); List<String> cities = [ 'New York', 'Los Angeles', 'Chicago', 'Houston', 'Phoenix', 'Philadelphia', 'San Antonio', 'San Diego', 'Dallas', 'San Jose' ]; return cities .where((element) => element.toLowerCase().contains(value.toLowerCase())) .toList(); } }
关键修改说明:
- 新增状态变量:
_selectedOptionIndex记录当前选中的选项索引,_currentOptions存储当前加载的选项列表,用于键盘导航时获取对应选项。 - 键盘事件处理:通过
onKeyEvent监听上下箭头和回车键:- 下箭头:索引自增,超出列表长度时回到第一个选项
- 上箭头:索引自减,小于0时回到最后一个选项
- 回车键:触发选中当前索引对应的选项,并重置选中状态
- 选中项高亮:在
optionsViewBuilder中,给当前选中的选项添加灰色背景,让用户直观看到选中状态 - 选项更新时重置索引:每次获取新的选项列表后,重置
_selectedOptionIndex为-1,避免旧索引干扰新列表
内容的提问来源于stack exchange,提问作者David Escariz
相关产品推荐
相关产品推荐

