import 'package:flutter/material.dart'; class SelectInputField extends StatefulWidget { final List options; final String? value; final ValueChanged? onChanged; final String hintText; final String labelText; final bool enabled; final EdgeInsetsGeometry? padding; final InputBorder? border; const SelectInputField({ Key? key, required this.options, this.value, this.onChanged, this.hintText = '请选择或输入', this.labelText = '', this.enabled = true, this.padding, this.border, }) : super(key: key); @override State createState() => _SelectInputFieldState(); } class _SelectInputFieldState extends State { final TextEditingController _controller = TextEditingController(); final FocusNode _focusNode = FocusNode(); bool _showDropdown = false; String? _selectedValue; @override void initState() { super.initState(); _selectedValue = widget.value; _controller.text = widget.value ?? ''; } @override void didUpdateWidget(SelectInputField oldWidget) { super.didUpdateWidget(oldWidget); if (widget.value != oldWidget.value) { _selectedValue = widget.value; _controller.text = widget.value ?? ''; } } void _onTextChanged(String text) { setState(() { _selectedValue = text.isEmpty ? null : text; _showDropdown = text.isNotEmpty; }); widget.onChanged?.call(text.isEmpty ? null : text); } void _onOptionSelected(String option) { setState(() { _selectedValue = option; _controller.text = option; _showDropdown = false; }); widget.onChanged?.call(option); _focusNode.unfocus(); } void _onFieldTap() { setState(() { _showDropdown = !_showDropdown; }); } void _onFocusChange(bool hasFocus) { setState(() { _showDropdown = hasFocus; }); } List get _filteredOptions { if (_controller.text.isEmpty) { return widget.options; } return widget.options .where((option) => option.toLowerCase().contains(_controller.text.toLowerCase())) .toList(); } @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (widget.labelText.isNotEmpty) ...[ Text( widget.labelText, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Colors.grey[700], ), ), const SizedBox(height: 8), ], Stack( children: [ TextField( controller: _controller, focusNode: _focusNode, onChanged: _onTextChanged, onTap: _onFieldTap, enabled: widget.enabled, decoration: InputDecoration( hintText: widget.hintText, border: widget.border ?? const OutlineInputBorder(), contentPadding: const EdgeInsets.symmetric( horizontal: 12, vertical: 14, ), suffixIcon: IconButton( icon: Icon( _showDropdown ? Icons.arrow_drop_up : Icons.arrow_drop_down, color: Colors.grey[600], ), onPressed: () { setState(() { _showDropdown = !_showDropdown; }); if (_showDropdown) { _focusNode.requestFocus(); } else { _focusNode.unfocus(); } }, ), ), ), if (_showDropdown && _filteredOptions.isNotEmpty) Positioned( top: 60, left: 0, right: 0, child: _buildDropdownList(), ), ], ), ], ); } Widget _buildDropdownList() { return Material( elevation: 4, borderRadius: BorderRadius.circular(8), child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey[300]!), ), constraints: const BoxConstraints(maxHeight: 200), child: ListView.builder( shrinkWrap: true, padding: EdgeInsets.zero, itemCount: _filteredOptions.length, itemBuilder: (context, index) { final option = _filteredOptions[index]; return ListTile( title: Text(option), onTap: () => _onOptionSelected(option), dense: true, visualDensity: VisualDensity.compact, ); }, ), ), ); } @override void dispose() { _controller.dispose(); _focusNode.dispose(); super.dispose(); } }