如何在Flutter的TextFormField内实现输入建议功能?
非列表形式的TextFormField输入建议实现方案
根据你的需求,以下是几种替代普通列表的输入建议实现方式:
方案1:输入框内实时补全提示
实现类似浏览器地址栏的灰色补全效果,用户输入时自动补全剩余部分,光标停留在输入内容末尾,补全内容为灰色,可直接输入覆盖或按回车确认。
代码示例:
final TextEditingController _controller = TextEditingController(); String _suggestionSuffix = ''; void _handleTextChange(String value) { // 替换为你的实际建议获取逻辑 List<String> allSuggestions = ['苹果', '应用程序', '预约']; _suggestionSuffix = ''; for (var suggestion in allSuggestions) { if (suggestion.startsWith(value) && value.isNotEmpty) { _suggestionSuffix = suggestion.substring(value.length); break; } } // 更新输入框内容,利用composing属性标记补全部分 _controller.value = TextEditingValue( text: value + _suggestionSuffix, selection: TextSelection.collapsed(offset: value.length), composing: TextRange(start: value.length, end: value.length + _suggestionSuffix.length), ); } // 在Widget中使用: TextFormField( controller: _controller, onChanged: _handleTextChange, decoration: const InputDecoration( border: OutlineInputBorder(), hintText: "输入关键词", ), )
方案2:横向滚动标签式建议
将建议以可点击标签的形式展示在输入框下方,横向滚动布局,点击标签直接填充内容到输入框。
代码示例:
// 假设你已有dashboardLayouts、originalDashboardLayouts和controller变量 Column( mainAxisSize: MainAxisSize.min, children: [ TextFormField( controller: controller, onChanged: (value) { // 过滤匹配的建议数据 setState(() { dashboardLayouts = originalDashboardLayouts.where((item) { final commonName = (item['dashBoardData'] as DashboardInfo).commonName.toLowerCase(); return commonName.contains(value.toLowerCase()); }).toList(); }); }, decoration: const InputDecoration( border: OutlineInputBorder(), hintText: "输入关键词", ), ), const SizedBox(height: 10), SizedBox( height: 42, child: SingleChildScrollView( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 8), child: Row( children: dashboardLayouts!.map((item) { final name = (item['dashBoardData'] as DashboardInfo).commonName; return Padding( padding: const EdgeInsets.only(right: 6), child: ElevatedButton( onPressed: () { controller.text = name; setState(() => dashboardLayouts = []); }, style: ElevatedButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), ), child: Text(name), ), ); }).toList(), ), ), ), ], )
方案3:悬浮式Wrap布局建议
通过Overlay实现悬浮在输入框下方的Wrap布局建议,自动换行,不占用固定列表高度。
代码示例:
final GlobalKey _textFieldKey = GlobalKey(); OverlayEntry? _suggestionOverlay; final TextEditingController controller = TextEditingController(); List<dynamic>? dashboardLayouts; void _showFloatingSuggestions(String value) { // 过滤匹配的建议名称 final matchedNames = dashboardLayouts!.map((item) { return (item['dashBoardData'] as DashboardInfo).commonName.toString(); }).where((name) => name.contains(value)).toList(); // 清空已有浮层 _suggestionOverlay?.remove(); if (matchedNames.isEmpty) { _suggestionOverlay = null; return; } // 获取输入框位置 final renderBox = _textFieldKey.currentContext!.findRenderObject() as RenderBox; final fieldOffset = renderBox.localToGlobal(Offset.zero); // 创建浮层 _suggestionOverlay = OverlayEntry( builder: (context) => Positioned( left: fieldOffset.dx, top: fieldOffset.dy + renderBox.size.height + 6, width: renderBox.size.width, child: Material( elevation: 4, borderRadius: BorderRadius.circular(8), child: Padding( padding: const EdgeInsets.all(8), child: Wrap( spacing: 8, runSpacing: 4, children: matchedNames.map((name) { return GestureDetector( onTap: () { controller.text = name; _suggestionOverlay?.remove(); _suggestionOverlay = null; }, child: Chip( label: Text(name), padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), ), ); }).toList(), ), ), ), ), ); Overlay.of(context)!.insert(_suggestionOverlay!); } // 记得在dispose中清理浮层 @override void dispose() { _suggestionOverlay?.remove(); controller.dispose(); super.dispose(); } // 在Widget中使用: TextFormField( key: _textFieldKey, controller: controller, onChanged: (value) => _showFloatingSuggestions(value), decoration: const InputDecoration( border: OutlineInputBorder(), hintText: "输入关键词", ), )
内容的提问来源于stack exchange,提问作者Rohan Jariwala
相关产品推荐
相关产品推荐

