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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 20:14:53