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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 16:45:38