Flutter:如何从API获取数据填充下拉框并获取选中项
解决方案
1. 定义Station数据模型
先创建对应API返回结构的数据模型,方便后续解析和处理:
class Station { final int id; final String name; final String address; final String imageUrl; Station({ required this.id, required this.name, required this.address, required this.imageUrl, }); // 根据你的API返回格式,调整JSON解析逻辑 factory Station.fromJson(Map<String, dynamic> json) { return Station( id: json['id'], name: json['name'], address: json['address'], imageUrl: json['image_url'] ?? 'assets/images/poynt.jpg', // 本地图作为默认值 ); } }
2. 修改下拉组件,支持动态数据与选中回调
把硬编码的下拉项改成接收外部传入的stations列表,同时添加回调传递选中的Station对象:
class PoyntsDropDownWidget extends StatefulWidget { final List<Station> stations; final Station? selectedStation; final Function(Station) onStationSelected; const PoyntsDropDownWidget({ super.key, required this.stations, required this.selectedStation, required this.onStationSelected, }); @override State<PoyntsDropDownWidget> createState() => _PoyntsDropDownWidgetState(); } class _PoyntsDropDownWidgetState extends State<PoyntsDropDownWidget> { @override Widget build(BuildContext context) { return Container( height: 100, padding: const EdgeInsets.only(left: 20, right: 13), decoration: BoxDecoration( color: constants.Colors.greyXDark, borderRadius: BorderRadius.circular(20), boxShadow: const [ BoxShadow( color: Colors.black26, spreadRadius: 5, blurRadius: 12, offset: Offset(0, 0), ) ], ), child: DropdownButton<Station>( isExpanded: true, itemHeight: 100, icon: const SizedBox.shrink(), value: widget.selectedStation, onChanged: (Station? value) { if (value != null) widget.onStationSelected(value); }, underline: const SizedBox(), dropdownColor: constants.Colors.greyXDark, items: widget.stations.map((Station station) { return DropdownMenuItem<Station>( value: station, child: Row( children: [ SizedBox( height: 80, width: 80, child: ClipRRect( borderRadius: BorderRadius.circular(8), // 兼容网络图片和本地图片 child: station.imageUrl.startsWith('http') ? Image.network(station.imageUrl, fit: BoxFit.cover) : Image.asset(station.imageUrl, fit: BoxFit.cover), ), ), const SizedBox(width: 15), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( station.name, maxLines: 2, overflow: TextOverflow.ellipsis, style: constants.Styles.normalHeavyTextStyleWhite, ), const SizedBox(height: 6), FittedBox( fit: BoxFit.scaleDown, child: Text( station.address, style: constants.Styles.smallBookTextStyleWhite, ), ), ], ), ), ], ), ); }).toList(), ), ); } }
3. 在页面中集成下拉组件并展示选中数据
在你的状态管理页面(比如Bloc/Cubit对应的UI)中,保存选中的Station状态,传递给下拉组件并展示对应数据:
// 在页面State中添加选中状态变量 Station? _selectedStation; // 处理StationLoaded状态时的逻辑 if (state is StationLoaded) { // 初始化选中第一个站点(可选) _selectedStation ??= state.stations.isNotEmpty ? state.stations[0] : null; return Column( children: [ // 集成下拉组件 PoyntsDropDownWidget( stations: state.stations, selectedStation: _selectedStation, onStationSelected: (Station station) { setState(() => _selectedStation = station); }, ), // 展示选中站点的对应数据 if (_selectedStation != null) Container( margin: const EdgeInsets.only(top: 20), padding: const EdgeInsets.all(16), color: constants.Colors.greyXDark, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '选中站点:${_selectedStation!.name}', style: constants.Styles.normalHeavyTextStyleWhite, ), const SizedBox(height: 8), Text( '地址:${_selectedStation!.address}', style: constants.Styles.smallBookTextStyleWhite, ), // 可添加更多需要展示的字段 ], ), ), // 保留原来的ListView部分 Expanded( child: Container( height: MediaQuery.of(context).size.height, decoration: const BoxDecoration( color: constants.Colors.greyXDark, border: Border( left: BorderSide(width: 2, color: constants.Colors.purpleMain), right: BorderSide(width: 2, color: constants.Colors.purpleMain), bottom: BorderSide(width: 2, color: constants.Colors.purpleMain), ), ), child: Padding( padding: const EdgeInsets.only(top: 40, left: 14, right: 14), child: MediaQuery.removePadding( context: context, removeTop: true, child: ListView.builder( physics: const BouncingScrollPhysics(), itemCount: state.stations.length + 1, itemBuilder: (context, index) { if (index == state.stations.length) { // 原来的底部内容逻辑 return const SizedBox(); } else { // 原来的列表项渲染逻辑 return ListTile( title: Text(state.stations[index].name), ); } }, ), ), ), ), ), ], ); }
关键说明
- 下拉组件不再依赖硬编码数据,通过
stations参数接收API返回列表,复用性更强 - 直接用
Station对象作为下拉项的value,避免索引映射的麻烦,选中后直接拿到完整数据 - 选中回调
onStationSelected将选中的Station传递给父页面,父页面保存状态后即可展示对应内容 - 图片部分兼容网络图和本地图,根据API返回的
imageUrl自动切换
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

