Flutter通用自定义DropdownFormField/DropdownWidget实现求助
通用可复用下拉表单字段实现方案
1. 定义通用选项模型
先统一封装下拉选项的数据结构,适配所有返回id+名称格式的API:
class DropdownOption { final String id; final String name; DropdownOption({required this.id, required this.name}); // 从API JSON转模型,支持自定义名称字段的key(比如userrole) factory DropdownOption.fromJson(Map<String, dynamic> json, {String nameKey = 'name'}) { return DropdownOption( id: json['id'].toString(), name: json[nameKey] as String, ); } }
2. 通用下拉组件实现
这个组件内置异步加载、状态管理、联动支持,直接复用即可:
import 'package:flutter/material.dart'; class GenericDropdownFormField extends StatefulWidget { final String hintText; final Future<List<DropdownOption>> Function() fetchOptions; final ValueChanged<DropdownOption?> onChanged; final DropdownOption? initialValue; final bool isEnabled; const GenericDropdownFormField({ super.key, required this.hintText, required this.fetchOptions, required this.onChanged, this.initialValue, this.isEnabled = true, }); @override State<GenericDropdownFormField> createState() => _GenericDropdownFormFieldState(); } class _GenericDropdownFormFieldState extends State<GenericDropdownFormField> { List<DropdownOption> _options = []; DropdownOption? _selectedValue; bool _isLoading = false; @override void initState() { super.initState(); _selectedValue = widget.initialValue; _loadOptions(); } @override void didUpdateWidget(covariant GenericDropdownFormField oldWidget) { super.didUpdateWidget(oldWidget); // 依赖条件变化时(比如州ID变更),自动重新加载选项 if (oldWidget.fetchOptions != widget.fetchOptions) { _loadOptions(); } } Future<void> _loadOptions() async { if (!widget.isEnabled) return; setState(() => _isLoading = true); try { _options = await widget.fetchOptions(); // 校验选中值是否在新选项列表中,无效则清空 if (_selectedValue != null && !_options.any((opt) => opt.id == _selectedValue!.id)) { _selectedValue = null; widget.onChanged(null); } } catch (e) { debugPrint('下拉选项加载失败: $e'); // 可根据需求添加错误提示(比如Toast) } finally { setState(() => _isLoading = false); } } @override Widget build(BuildContext context) { return DropdownButtonFormField<DropdownOption>( value: _selectedValue, hint: Text(widget.hintText), isEnabled: widget.isEnabled && !_isLoading, decoration: const InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), ), items: _options.map((option) { return DropdownMenuItem( value: option, child: Text(option.name), ); }).toList(), onChanged: (value) { setState(() => _selectedValue = value); widget.onChanged(value); }, disabledHint: _isLoading ? const CircularProgressIndicator(size: 20) : Text(widget.hintText), ); } }
3. 页面中使用组件(含联动示例)
以下是州、城市、角色下拉框的完整使用示例,API请求部分用模拟数据,实际项目替换为真实接口即可:
import 'package:flutter/material.dart'; class LocationAndRoleForm extends StatefulWidget { const LocationAndRoleForm({super.key}); @override State<LocationAndRoleForm> createState() => _LocationAndRoleFormState(); } class _LocationAndRoleFormState extends State<LocationAndRoleForm> { DropdownOption? _selectedState; DropdownOption? _selectedCity; DropdownOption? _selectedRole; // 模拟获取州列表的API请求 Future<List<DropdownOption>> _fetchStates() async { await Future.delayed(const Duration(milliseconds: 500)); return [ DropdownOption(id: '1', name: '加利福尼亚州'), DropdownOption(id: '2', name: '德克萨斯州'), DropdownOption(id: '3', name: '纽约州'), ]; } // 模拟根据州ID获取城市列表的API请求 Future<List<DropdownOption>> _fetchCities() async { if (_selectedState == null) return []; await Future.delayed(const Duration(milliseconds: 500)); switch (_selectedState!.id) { case '1': return [ DropdownOption(id: '101', name: '洛杉矶'), DropdownOption(id: '102', name: '旧金山'), ]; case '2': return [ DropdownOption(id: '201', name: '休斯顿'), DropdownOption(id: '202', name: '达拉斯'), ]; case '3': return [ DropdownOption(id: '301', name: '纽约市'), DropdownOption(id: '302', name: '布法罗'), ]; default: return []; } } // 模拟获取用户角色列表的API请求(注意指定nameKey为userrole) Future<List<DropdownOption>> _fetchRoles() async { await Future.delayed(const Duration(milliseconds: 500)); final jsonList = [ {'id': 'r1', 'userrole': 'president'}, {'id': 'r2', 'userrole': 'member'}, {'id': 'r3', 'userrole': 'secretary'}, ]; return jsonList.map((json) => DropdownOption.fromJson(json, nameKey: 'userrole')).toList(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('通用下拉组件示例')), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ GenericDropdownFormField( hintText: '选择州', fetchOptions: _fetchStates, onChanged: (value) { setState(() { _selectedState = value; _selectedCity = null; // 切换州后清空城市选择 }); }, ), const SizedBox(height: 16), GenericDropdownFormField( hintText: '选择城市', fetchOptions: _fetchCities, onChanged: (value) => setState(() => _selectedCity = value), isEnabled: _selectedState != null, // 未选州时禁用 ), const SizedBox(height: 16), GenericDropdownFormField( hintText: '选择用户角色', fetchOptions: _fetchRoles, onChanged: (value) => setState(() => _selectedRole = value), ), const SizedBox(height: 24), // 展示选中结果 Text('选中的州: ${_selectedState?.name ?? '未选择'}'), Text('选中的城市: ${_selectedCity?.name ?? '未选择'}'), Text('选中的角色: ${_selectedRole?.name ?? '未选择'}'), ], ), ), ); } }
核心特性说明
- 适配所有
id+名称格式的下拉场景,支持自定义名称字段键 - 内置加载状态、错误处理,无需重复写逻辑
- 自动支持联动场景(如州-城市),依赖条件变化时自动刷新选项
- 支持禁用状态、初始值、提示文字等自定义配置
内容的提问来源于stack exchange,提问作者gaurachand
相关产品推荐
相关产品推荐

