Flutter中如何将TextFormField自定义为多选搜索组件?
实现带多选标签的搜索输入框方案
针对你需要的「选择搜索结果后在输入框内添加标签、支持多次选择」的需求,这里提供一个Flutter端的可行实现方案:
核心实现思路
- 用
Wrap组件组合已选标签(Chip)和输入框,实现标签换行布局 - 维护一个已选条目列表,用于渲染标签和控制输入框状态
- 监听输入框文本变化,实时触发搜索并展示结果列表
- 点击搜索结果时将条目加入已选列表,同时清空输入框
- 支持点击标签的删除按钮移除对应条目
完整代码示例
import 'package:flutter/material.dart'; void main() => runApp(const TagSearchApp()); class TagSearchApp extends StatelessWidget { const TagSearchApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text("标签搜索示例")), body: const TagSearchField(), ), ); } } class TagSearchField extends StatefulWidget { const TagSearchField({super.key}); @override State<TagSearchField> createState() => _TagSearchFieldState(); } class _TagSearchFieldState extends State<TagSearchField> { // 已选条目列表 final List<String> _selectedItems = []; // 搜索输入文本控制器 final TextEditingController _searchController = TextEditingController(); // 当前搜索结果 List<String> _searchResults = []; // 模拟全量搜索关键词池 final List<String> _allKeywords = ["Item 0", "Item 1", "Item 2", "Item 3", "Item 4", "Item 5"]; @override void initState() { super.initState(); // 监听输入变化触发搜索 _searchController.addListener(_performSearch); } @override void dispose() { _searchController.dispose(); super.dispose(); } // 执行模糊搜索逻辑 void _performSearch() { final query = _searchController.text.trim(); if (query.isEmpty) { setState(() => _searchResults = []); return; } setState(() { _searchResults = _allKeywords.where((item) => item.contains(query)).toList(); }); } // 添加选中条目到标签列表 void _addSelectedItem(String item) { if (!_selectedItems.contains(item)) { setState(() { _selectedItems.add(item); _searchController.clear(); _searchResults = []; }); } } // 删除指定标签 void _removeSelectedItem(String item) { setState(() => _selectedItems.remove(item)); } @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 自定义输入区域:标签 + 输入框 Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( border: Border.all(color: Colors.grey), borderRadius: BorderRadius.circular(8), ), child: Wrap( spacing: 8, runSpacing: 4, children: [ // 渲染已选标签 ..._selectedItems.map((item) => Chip( label: Text(item), onDeleted: () => _removeSelectedItem(item), )), // 搜索输入框 SizedBox( width: 120, child: TextField( controller: _searchController, decoration: const InputDecoration( border: InputBorder.none, hintText: "搜索...", ), ), ), ], ), ), // 搜索结果下拉列表 if (_searchResults.isNotEmpty) Container( margin: const EdgeInsets.only(top: 8), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(8), ), child: ListView.builder( shrinkWrap: true, itemCount: _searchResults.length, itemBuilder: (context, index) { final item = _searchResults[index]; return ListTile( title: Text(item), onTap: () => _addSelectedItem(item), ); }, ), ), ], ), ); } }
关键细节说明
- 标签布局:
Wrap组件实现标签自动换行,避免输入区域横向溢出 - 搜索交互:通过
TextEditingController监听输入内容,实时过滤搜索结果 - 标签管理:点击搜索结果自动添加标签,点击标签删除按钮移除条目,同时做了重复添加的判断
- 样式适配:自定义输入区域的边框样式,可根据需求调整颜色、圆角、间距等参数匹配你的UI设计
内容的提问来源于stack exchange,提问作者Quang Anh
相关产品推荐
相关产品推荐

