Flutter ListView因字段空值不显示数据的解决方法
解决API返回null导致ListView无法显示的问题
问题根源分析
你的代码存在两个核心问题导致ListView无法正常显示:
ProductoModelo构造函数中,sub_cat2和sub_cat3被标记为required,但它们是可空类型String?,这种写法容易引发初始化逻辑冲突;FutureBuilder仅处理了snapshot.hasData的成功场景,完全忽略了加载中和请求失败的状态,当API解析出错时,UI没有任何输出。
解决方案
1. 修正模型类构造函数
根据需求选择以下两种方式之一:
方式一:保留可空类型,移除required修饰符
因为sub_cat2和sub_cat3允许为空,无需强制传值:
class ProductoModelo { ProductoModelo({ required this.id, required this.sku, required this.nombre, required this.descripcion, required this.destacado, required this.visible_en_tienda, required this.imagen, required this.categoria, required this.sub_cat, this.sub_cat2, // 移除required this.sub_cat3, // 移除required }); String id; String sku; String nombre; String descripcion; String destacado; String visible_en_tienda; String imagen; String categoria; String sub_cat; String? sub_cat2; String? sub_cat3; // ... fromJson和toJson方法保持不变 }
方式二:改为非空字符串,确保始终有值
如果希望这两个字段永远是非空状态,修改类型并保留required:
class ProductoModelo { String id; String sku; String nombre; String descripcion; String destacado; String visible_en_tienda; String imagen; String categoria; String sub_cat; String sub_cat2; // 改为非空String String sub_cat3; // 改为非空String ProductoModelo({ required this.id, required this.sku, required this.nombre, required this.descripcion, required this.destacado, required this.visible_en_tienda, required this.imagen, required this.categoria, required this.sub_cat, required this.sub_cat2, required this.sub_cat3, }); factory ProductoModelo.fromJson(Map<String, dynamic> json) => ProductoModelo( id: json["id"], sku: json["sku"], nombre: json["nombre"], descripcion: json["descripcion"], destacado: json["destacado"], visible_en_tienda: json["visible_en_tienda"], imagen: json["imagen"], categoria: json["categoria"], sub_cat: json["sub_cat"], sub_cat2: json["sub_cat2"] ?? "", sub_cat3: json["sub_cat3"] ?? "", ); // ... toJson方法保持不变 }
2. 完善FutureBuilder的状态处理
添加加载中、错误、无数据的UI逻辑,确保所有场景都有响应:
Expanded( child: Container( child: FutureBuilder( future: fetchProductos(), builder: (context, snapshot) { // 加载中状态 if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } // 请求失败状态 if (snapshot.hasError) { return Center(child: Text('加载失败: ${snapshot.error}')); } // 无数据状态 if (!snapshot.hasData || (snapshot.data as List).isEmpty) { return const Center(child: Text('暂无产品数据')); } // 数据加载成功 List<ProductoModelo> filteredList = snapshot.data as List<ProductoModelo>; filteredList = filteredList .where((element) => element.nombre .toLowerCase() .contains(_controller.text.toLowerCase())) .toList(); return Padding( padding: const EdgeInsets.only(bottom: 20), child: ListView.builder( itemCount: filteredList.length, shrinkWrap: true, itemBuilder: (BuildContext context, index) { ProductoModelo producto = filteredList[index]; // ... 原有UI代码保持不变 }, ), ); }, ), ), )
3. 增强网络请求的错误捕获
在fetchProductos中添加异常处理,避免解析失败导致Future崩溃:
Future<List<ProductoModelo>> fetchProductos() async { try { String url = Constantes().URLProyecto + Constantes().APICarpeta + "get_productos.php"; final response = await http.get(Uri.parse(url)); // 检查HTTP状态码 if (response.statusCode != 200) { throw Exception('请求失败,状态码: ${response.statusCode}'); } print("RESPONSE BODY productos" + response.body.toString()); return productoModeloFromJson(response.body); } catch (e) { print('加载产品出错: $e'); rethrow; // 抛出异常让FutureBuilder处理 } }
额外优化建议
- 网络图片添加占位图和错误兜底:
Container( height: 80, width: 80, child: Image.network( Constantes().URLProductos + producto.imagen, fit: BoxFit.cover, placeholder: (context, url) => const CircularProgressIndicator(), errorBuilder: (context, error, stackTrace) => const Icon(Icons.image_not_supported), ), )
内容的提问来源于stack exchange,提问作者mvasco
相关产品推荐
相关产品推荐

