Flutter实现点击按钮后将JSON数据筛选展示到ListView
Flutter 车辆信息列表实现方案
1. 实体类增加展示字段筛选逻辑
首先在你已经写好的Vehicle实体类中新增筛选方法,统一处理「isVisible为true、值为基础类型」的过滤规则,不要把筛选逻辑散落在UI代码里。
- 如果你的车辆属性是硬编码在实体类中的(比如year/make/model这些属性提前写死),用下面的写法:
// 放入Vehicle类内部 List<MapEntry<String, dynamic>> getDisplayFields() { // 所有待校验的字段统一配置,label是页面展示的中文名,value对应实体属性,isVisible对应接口返回的字段显隐标识 final allFields = <Map<String, dynamic>>[ {'label': '年款', 'value': year, 'isVisible': yearIsVisible}, {'label': '品牌', 'value': make, 'isVisible': makeIsVisible}, {'label': '型号', 'value': model, 'isVisible': modelIsVisible}, {'label': '车身形式', 'value': bodyType, 'isVisible': bodyTypeIsVisible}, {'label': '燃油类型', 'value': fuelType, 'isVisible': fuelTypeIsVisible}, {'label': '市场估值', 'value': valuation, 'isVisible': valuationIsVisible}, // 其他需要判断展示的字段按相同格式补充即可 ]; // 执行过滤:保留显隐为true、值为基础类型(字符串/数字/布尔值,排除对象、数组等复杂结构)的字段 return allFields .where((field) => field['isVisible'] == true && (field['value'] is String || field['value'] is num || field['value'] is bool)) .map((field) => MapEntry(field['label'] as String, field['value'])) .toList(); }
- 如果接口返回的是动态字段结构(比如每个字段以
{"fieldName":"Year","value":"2024","isVisible":true}的格式存在数组里),直接用更简洁的筛选逻辑:
// 放入Vehicle类内部,假设你用List<dynamic> responseFields存储接口返回的动态字段数组 List<MapEntry<String, dynamic>> getDisplayFields() { return responseFields .where((field) => field['isVisible'] == true && (field['value'] is String || field['value'] is num || field['value'] is bool)) .map((field) => MapEntry(field['fieldName'].toString(), field['value'])) .toList(); }
2. 页面层处理异步请求状态
点击按钮触发请求时,必须处理加载中、加载失败、空数据、正常展示四种状态,避免UI报错:
class VehicleListPage extends StatefulWidget { const VehicleListPage({super.key}); @override State<VehicleListPage> createState() => _VehicleListPageState(); } class _VehicleListPageState extends State<VehicleListPage> { List<Vehicle> _vehicleList = []; bool _isLoading = false; String? _errorMsg; // 按钮触发的请求方法 Future<void> _fetchVehicleData() async { setState(() { _isLoading = true; _errorMsg = null; }); try { // 替换成你已经实现的Dio请求方法 final result = await signInData(); setState(() { _vehicleList = result; _isLoading = false; }); } catch (e) { setState(() { _errorMsg = '加载失败:${e.toString()}'; _isLoading = false; }); } }
3. 构建ListView列表渲染
用ListView.builder做列表懒加载,每辆车对应一个卡片组件,卡片内部遍历筛选后的字段生成信息行:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('车辆信息列表')), body: Column( children: [ Padding( padding: const EdgeInsets.all(12), child: SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _isLoading ? null : _fetchVehicleData, child: Text(_isLoading ? '数据加载中...' : '获取车辆信息'), ), ), ), Expanded(child: _buildContentArea()), ], ), ); } // 抽离内容区,按不同状态返回对应组件 Widget _buildContentArea() { if (_isLoading) return const Center(child: CircularProgressIndicator()); if (_errorMsg != null) return Center(child: Text(_errorMsg!)); if (_vehicleList.isEmpty) return const Center(child: Text('暂无数据,请点击按钮获取车辆信息')); return ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 12), itemCount: _vehicleList.length, itemBuilder: (context, index) { final vehicle = _vehicleList[index]; final displayFields = vehicle.getDisplayFields(); return Card( margin: const EdgeInsets.only(bottom: 12), elevation: 2, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 卡片标题,展示车辆核心身份信息 Text( '${vehicle.year} ${vehicle.make} ${vehicle.model}', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), const Divider(height: 24), // 遍历筛选后的字段,生成每一行的标签+值 ...displayFields.map((item) => Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('${item.key}:', style: const TextStyle(color: Colors.grey)), // 估值类数字可以在这里单独做格式化,比如拼接货币符号、保留两位小数 Text(item.value.toString()), ], ), )) ], ), ), ); }, ); } }
避坑提示
- 不要直接在
build方法里调用网络请求,否则每次页面重绘都会重复触发请求 - 字段筛选逻辑统一收敛在实体类中,后续新增/删减展示字段只需要修改实体类配置,不需要动UI代码
- 数字类的估值字段如果需要格式化,可以在渲染时判断类型,调用
toStringAsFixed(2)保留小数位,再拼接对应的货币单位 - 如果后续字段的
isVisible状态会动态变化,只要重新调用setState触发重绘,列表会自动按新的筛选规则更新展示内容
内容的提问来源于stack exchange,提问作者SaDev
相关产品推荐
相关产品推荐

