Flutter:使用dropdown_textfield实现可自定义输入下拉框的问题
问题:无法添加自定义输入到Dropdown TextField的历史记录
我使用dropdown_textfield包实现了带历史记录的下拉文本框,目前能正常显示历史选项并搜索,但无法将用户输入的自定义内容添加到历史记录,只能选择现有选项。
问题原因
原代码仅在onChanged回调中处理选中现有选项的情况,但dropdown_textfield的onChanged仅在用户选中下拉列表中的选项时触发,用户手动输入的自定义文本不会触发该回调,因此无法捕获并添加新值。
解决方法
需要添加文本输入监听来捕获用户输入内容,并在合适时机(如按下回车)将新值添加到历史记录,同时修正历史加载逻辑中的问题:
1. 添加文本输入监听
给SingleValueDropDownController添加监听器,实时获取输入框文本内容。
2. 处理输入提交事件
通过textFieldDecoration的onSubmitted回调,在用户按下回车时确认添加新值。
3. 修正历史加载逻辑
原_loadHistory方法每次加载都会覆盖控制器当前值,改为仅在初始化且历史为空时设置默认值,避免干扰用户当前输入。
修改后的完整代码
import 'package:dropdown_textfield/dropdown_textfield.dart'; import 'package:flutter/material.dart'; import 'package:local_network_monitoring/utils/constants.dart'; import 'package:shared_preferences/shared_preferences.dart'; class HistoryTextField extends StatefulWidget { final String fieldType; final String hintText; final SingleValueDropDownController controller; const HistoryTextField( {super.key, required this.fieldType, required this.hintText, required this.controller}); @override State<HistoryTextField> createState() => _HistoryTextFieldState(); } class _HistoryTextFieldState extends State<HistoryTextField> { List<DropDownValueModel> _dropdownList = []; @override void initState() { super.initState(); _loadHistory(); // 添加文本输入监听 widget.controller.addListener(_onTextChanged); } @override void dispose() { widget.controller.removeListener(_onTextChanged); widget.controller.dispose(); super.dispose(); } // 监听文本输入变化 void _onTextChanged() { final inputText = widget.controller.text.trim(); if (inputText.isNotEmpty && !_dropdownList.any((item) => item.name.toLowerCase() == inputText.toLowerCase())) { // 可选:实时添加新值,或留到提交时处理 // _addNewValue(inputText); } } Future<void> _saveValues() async { SharedPreferences prefs = await SharedPreferences.getInstance(); List<String> values = _dropdownList.map((item) => item.name).toList(); prefs.setStringList(widget.fieldType, values); } void _addNewValue(String newValue) { newValue = newValue.trim(); if (newValue.isEmpty) return; // 检查值是否已存在 if (!_dropdownList.any((item) => item.name.toLowerCase() == newValue.toLowerCase())) { setState(() { _dropdownList.add(DropDownValueModel(name: newValue, value: newValue)); widget.controller.dropDownValue = DropDownValueModel(name: newValue, value: newValue); }); _saveValues(); } } void _loadHistory() async { final preferences = await SharedPreferences.getInstance(); var historyValues = preferences.getStringList(widget.fieldType) ?? []; setState(() { if (historyValues.isEmpty) { // 仅在历史为空时设置默认值 if (widget.fieldType == "communityField") { _dropdownList.add(const DropDownValueModel(name: "public", value: defaultCommunity)); } else if (widget.fieldType == "oidField") { _dropdownList.add(const DropDownValueModel(name: "defaultOID", value: defaultOID)); } else if (widget.fieldType == "ipField") { _dropdownList.add(const DropDownValueModel(name: "defaultIp", value: defaultIp)); } // 初始化控制器默认值 if (_dropdownList.isNotEmpty) { widget.controller.dropDownValue = _dropdownList.first; } } else { _dropdownList.clear(); for (var value in historyValues) { _dropdownList.add(DropDownValueModel(name: value, value: value)); } // 保留控制器当前值,不强制覆盖 } }); } @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( color: Theme.of(context).scaffoldBackgroundColor, border: Border.all(color: Theme.of(context).dividerColor), borderRadius: BorderRadius.circular(5), ), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), child: Expanded( child: DropDownTextField( textFieldDecoration: InputDecoration( hintText: widget.hintText, border: InputBorder.none, hintStyle: const TextStyle( overflow: TextOverflow.ellipsis, fontSize: 9, fontWeight: FontWeight.bold, ), // 按下回车时添加新值 onSubmitted: (value) => _addNewValue(value), ), dropDownList: _dropdownList, controller: widget.controller, onChanged: (value) { if (value != null) { widget.controller.dropDownValue = value; } }, enableSearch: true, dropDownItemCount: 5, enableSearchFilter: true, ), ), ); } }
组件使用方式(仅修正布局小问题)
class CommunitySelection extends StatelessWidget { const CommunitySelection({super.key}); @override Widget build(BuildContext context) { return BlocBuilder<CommunityCubit, CommunityState>( builder: (context, state) { return Row( children: [ const Text("Community: ", style: TextStyle(fontWeight: FontWeight.bold)), const SizedBox(width: 20), // Row布局中改为width而非height SizedBox( width: 200, child: HistoryTextField( fieldType: "communityField", hintText: "Community", controller: state.communityController, ), ), ], ); }, ); } }
内容的提问来源于stack exchange,提问作者Geo48
相关产品推荐
相关产品推荐

