Flutter新手求助:复用自定义ListView.builder展示两类商品列表
解决Flutter生鲜电商列表组件复用问题
要让ProductsListView同时适配水果和蔬菜列表,核心是让组件摆脱对固定列表的依赖,通过统一数据模型和动态传入列表参数来实现复用,具体步骤如下:
1. 统一数据模型
原代码中Fruits和Vegetables类结构完全一致,只是属性命名不同,我们可以抽象成通用的Product类,简化数据结构:
import '../utilities/constants.dart'; class Product { final String? image; final String? name; final String? category; final String? price; Product({this.image, this.name, this.category, this.price}); } // 水果列表 final Product bananas = Product( image: '${kFruitsImagesAsset}bananas.png', name: 'Bananas', category: 'Organic', price: '\$4.99', ); final Product apples = Product( image: '${kFruitsImagesAsset}apples.png', name: 'Apples', category: 'Organic', price: '\$5.00', ); final Product chikku = Product( image: '${kFruitsImagesAsset}chikku.png', name: 'Chikku', category: 'Organic', price: '\$9.00', ); final Product peaches = Product( image: '${kFruitsImagesAsset}peaches.png', name: 'Peaches', category: 'Organic', price: '\$12.00', ); List<Product> fruitsList = [bananas, apples, chikku, peaches]; // 蔬菜列表 final Product okra = Product( image: '${kVegetablesImagesAsset}okra.png', name: 'Okra', category: 'Organic', price: '\$6.99', ); final Product peas = Product( image: '${kVegetablesImagesAsset}peas.png', name: 'Peas', category: 'Organic', price: '\$10.50', ); final Product potatoes = Product( image: '${kVegetablesImagesAsset}potatoes.png', name: 'Potatoes', category: 'Organic', price: '\$5.99', ); final Product taro = Product( image: '${kVegetablesImagesAsset}taro.png', name: 'Taro', category: 'Organic', price: '\$5.50', ); List<Product> vegetablesList = [okra, peas, potatoes, taro];
2. 重构ProductsListView组件
移除原组件中无用的单个属性参数,改为接收一个List<Product>类型的列表,让组件动态渲染传入的列表数据:
import 'package:flutter/material.dart'; import 'package:grocery_store/models/products_list.dart'; import '../utilities/add_product.dart'; import '../utilities/constants.dart'; class ProductsListView extends StatelessWidget { // 新增:接收通用产品列表 final List<Product> products; const ProductsListView({ Key? key, required this.products, // 必须传入的参数 }) : super(key: key); @override Widget build(BuildContext context) { return ListView.builder( scrollDirection: Axis.horizontal, itemCount: products.length, // 使用传入列表的长度 itemBuilder: (BuildContext context, int index) { final product = products[index]; return ClipRect( child: Container( width: 140.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15.0), color: Colors.white, boxShadow: const [ BoxShadow( blurRadius: 10, color: Colors.black, ), ], ), margin: const EdgeInsets.all(10.0), child: Padding( padding: const EdgeInsets.fromLTRB(20, 10, 10, 10), child: Column( children: [ Image.asset( product.image!, height: 80.0, width: 90.0, ), const SizedBox( height: 15, ), Row( children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( product.name!, style: const TextStyle( fontSize: 15.0, fontWeight: FontWeight.bold, ), ), Text( product.category!, textAlign: TextAlign.left, style: const TextStyle( height: 1.5, color: kDarkGrey, fontSize: 12.5, fontWeight: FontWeight.bold, ), ), ], ), ], ), Row( children: [ Text( product.price!, style: const TextStyle( fontWeight: FontWeight.bold, ), ), const Spacer(), const AddProduct(), ], ) ], ), ), ), ); }, ); } }
3. 更新首页调用
在GroceryPage中调用ProductsListView时,分别传入水果列表和蔬菜列表:
// ... 其余代码不变 ... Column( children: [ const ProductCardsRow( groceryType: 'Fruits', ), SizedBox( height: 215, width: double.infinity, child: ProductsListView( products: fruitsList, // 传入水果列表 ), ), ], ), Column( children: [ const ProductCardsRow( groceryType: 'Vegetables', ), SizedBox( height: 215, width: double.infinity, child: ProductsListView( products: vegetablesList, // 传入蔬菜列表 ), ), ], ), // ... 其余代码不变 ...
这样修改后,ProductsListView就可以复用渲染任意符合Product模型的列表,不仅支持水果和蔬菜,后续新增其他品类也能直接复用该组件。
内容的提问来源于stack exchange,提问作者Ahmed Loutfy
相关产品推荐
相关产品推荐

