Flutter实现带复选框的搜索筛选列表组件方案咨询
问题描述
我想创建一个这样的组件:包含输入框,输入内容后会筛选列表并在输入框下方展示结果;结果列表需要带复选框,支持多选操作。请问Flutter有没有适用的内置Widget?或者有哪些第三方包可以实现这个功能?
参考效果:
我尝试用RawAutoComplete实现,代码如下:
class SearchBarDemo extends StatelessWidget { const SearchBarDemo({super.key}); static List<String> suggestons = [ "USA", "UK", "Uganda", "Uruguay", "United Arab Emirates" ]; @override Widget build(BuildContext context) { return Scaffold( body: RawAutocomplete( optionsBuilder: (textEditingValue) { if (textEditingValue.text == '') { return const Iterable<String>.empty(); } else { List<String> matches = <String>[]; matches.addAll(suggestons); matches.retainWhere((s) { return s .toLowerCase() .contains(textEditingValue.text.toLowerCase()); }); return matches; } }, fieldViewBuilder: (context, textEditingController, focusNode, onFieldSubmitted) { return TextField( decoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(7), ), hintText: 'Search', contentPadding: EdgeInsets.symmetric( vertical: 8, horizontal: 4), // EdgeInsets.only(top: 8, left: 5), prefixIcon: Container( margin: EdgeInsets.symmetric(vertical: 8, horizontal: 4), decoration: BoxDecoration( shape: BoxShape.rectangle, border: Border( right: BorderSide( color: Colors.grey.shade400, ), ), ), child: Icon( Icons.search, color: Colors.grey.shade400, ), ), ), controller: textEditingController, focusNode: focusNode, onSubmitted: (String value) {}, ); }, onSelected: (selection) {}, optionsViewBuilder: (context, onSelected, options) { return Material( child: SingleChildScrollView( child: Column( children: options.map((opt) { return InkWell( onTap: () { onSelected(opt); }, child: Column( children: [ Container( height: 50, width: 250, alignment: Alignment.topLeft, child: Card( child: SizedBox( child: ListTile( title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( opt, style: TextStyle(fontSize: 12), ), Transform.scale( scale: 0.8, child: Checkbox( value: false, onChanged: (val) {}, ), ), ], ), ), ), ), ), ], ), ); }).toList(), ), ), ); }, ), ); } }
当前运行效果不符合预期:结果区域占满整个屏幕,内容居中显示。
解决方案
一、优化内置RawAutoComplete组件
你的代码问题出在optionsViewBuilder的布局约束和多选状态管理上,以下是修复后的完整代码:
class SearchBarDemo extends StatefulWidget { const SearchBarDemo({super.key}); @override State<SearchBarDemo> createState() => _SearchBarDemoState(); } class _SearchBarDemoState extends State<SearchBarDemo> { static List<String> suggestions = [ "USA", "UK", "Uganda", "Uruguay", "United Arab Emirates" ]; final Set<String> _selectedItems = {}; final TextEditingController _textEditingController = TextEditingController(); @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24), child: RawAutocomplete<String>( textEditingController: _textEditingController, optionsBuilder: (textEditingValue) { if (textEditingValue.text.isEmpty) { return const Iterable<String>.empty(); } return suggestions.where((suggestion) => suggestion.toLowerCase().contains(textEditingValue.text.toLowerCase())); }, fieldViewBuilder: (context, controller, focusNode, onSubmitted) { return TextField( controller: controller, focusNode: focusNode, decoration: const InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(7)), ), hintText: 'Search', contentPadding: EdgeInsets.symmetric(vertical: 8, horizontal: 4), prefixIcon: Padding( padding: EdgeInsets.symmetric(vertical: 8, horizontal: 4), child: Icon(Icons.search, color: Colors.grey), ), ), ); }, optionsViewBuilder: (context, onSelected, options) { // 获取输入框尺寸,让选项列表宽度匹配输入框 final inputBoxSize = (context.findRenderObject() as RenderBox).size; return Align( alignment: Alignment.topLeft, child: Material( elevation: 4, borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(7), bottomRight: Radius.circular(7), ), child: SizedBox( width: inputBoxSize.width, child: ListView.builder( shrinkWrap: true, padding: EdgeInsets.zero, itemCount: options.length, itemBuilder: (context, index) { final option = options.elementAt(index); final isSelected = _selectedItems.contains(option); return ListTile( title: Text(option, style: const TextStyle(fontSize: 12)), trailing: Checkbox( value: isSelected, onChanged: (bool? value) { setState(() { value == true ? _selectedItems.add(option) : _selectedItems.remove(option); }); }, ), onTap: () { setState(() { isSelected ? _selectedItems.remove(option) : _selectedItems.add(option); }); }, ); }, ), ), ), ); }, ), ), ); } @override void dispose() { _textEditingController.dispose(); super.dispose(); } }
关键优化点:
- 改为
StatefulWidget,用_selectedItems集合管理多选状态 - 通过
context.findRenderObject()获取输入框宽度,让选项列表和输入框同宽 - 用
Align组件将选项列表定位在输入框下方,避免占满屏幕 - 绑定Checkbox状态,点击列表项或复选框都能切换选中状态
- 替换
Column为ListView.builder,优化长列表性能
二、第三方包推荐
如果不想手动实现复杂逻辑,可以直接使用成熟的第三方包:
- flutter_typeahead:主流自动补全组件,支持自定义选项模板,轻松实现多选复选框
- multi_select_flutter:专为多选场景设计,内置搜索筛选功能,开箱即用
- searchable_dropdown:集成搜索的下拉多选组件,配置简单,适合快速开发
这些包均可在pub.dev上找到,按照官方文档引入即可快速实现需求。
内容的提问来源于stack exchange,提问作者shuja ali
相关产品推荐
相关产品推荐

