You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
  }
}

关键优化点:

  1. 改为StatefulWidget,用_selectedItems集合管理多选状态
  2. 通过context.findRenderObject()获取输入框宽度,让选项列表和输入框同宽
  3. 用Align组件将选项列表定位在输入框下方,避免占满屏幕
  4. 绑定Checkbox状态,点击列表项或复选框都能切换选中状态
  5. 替换Column为ListView.builder,优化长列表性能

二、第三方包推荐

如果不想手动实现复杂逻辑,可以直接使用成熟的第三方包:

  • flutter_typeahead:主流自动补全组件,支持自定义选项模板,轻松实现多选复选框
  • multi_select_flutter:专为多选场景设计,内置搜索筛选功能,开箱即用
  • searchable_dropdown:集成搜索的下拉多选组件,配置简单,适合快速开发

这些包均可在pub.dev上找到,按照官方文档引入即可快速实现需求。


内容的提问来源于stack exchange,提问作者shuja ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 20:50:29