Flutter如何创建包含图片、文字、副文本的可复用横向滚动Widget?
Flutter 可复用横向滚动Widget实现方案
实现思路
- 滚动容器优先选择
ListView并指定滚动方向为横向,如需支持大量数据懒加载用ListView.builder,少量固定数据直接用SingleChildScrollView嵌套Row即可 - 封装独立的子项组件
HorizontalScrollItem,固定包含图片、主文本、副文本三个模块,所有样式和内容支持外部传入 - 暴露通用配置参数,适配不同业务场景的定制需求
核心代码实现
子项组件代码
class HorizontalScrollItem extends StatelessWidget { final String imageUrl; final String mainText; final String secondaryText; final double itemWidth; final TextStyle? mainTextStyle; final TextStyle? secondaryTextStyle; final VoidCallback? onTap; const HorizontalScrollItem({ super.key, required this.imageUrl, required this.mainText, required this.secondaryText, required this.itemWidth, this.mainTextStyle, this.secondaryTextStyle, this.onTap, }); @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, child: SizedBox( width: itemWidth, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // 图片模块 ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.network( imageUrl, height: itemWidth * 0.6, width: itemWidth, fit: BoxFit.cover, ), ), const SizedBox(height: 8), // 主文本 Text( mainText, style: mainTextStyle ?? const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), // 副文本 Text( secondaryText, style: secondaryTextStyle ?? const TextStyle(fontSize: 12, color: Colors.grey), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ), ); } }
横向滚动容器代码
class HorizontalScrollWidget extends StatelessWidget { final List<Map<String, dynamic>> dataList; final double itemWidth; final double itemSpacing; final double containerPadding; final Widget? header; final Function(int index)? onItemTap; const HorizontalScrollWidget({ super.key, required this.dataList, required this.itemWidth, this.itemSpacing = 12, this.containerPadding = 16, this.header, this.onItemTap, }); @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ if (header != null) Padding( padding: EdgeInsets.symmetric(horizontal: containerPadding), child: header!, ), if (header != null) const SizedBox(height: 12), SizedBox( height: itemWidth * 0.6 + 50, // 图片高度+文本区域预留高度 child: ListView.separated( padding: EdgeInsets.symmetric(horizontal: containerPadding), scrollDirection: Axis.horizontal, itemCount: dataList.length, separatorBuilder: (context, index) => SizedBox(width: itemSpacing), itemBuilder: (context, index) { final item = dataList[index]; return HorizontalScrollItem( imageUrl: item['imageUrl'], mainText: item['mainText'], secondaryText: item['secondaryText'], itemWidth: itemWidth, onTap: onItemTap != null ? () => onItemTap!(index) : null, ); }, ), ), ], ); } }
使用示例
// 页面中直接调用 HorizontalScrollWidget( dataList: [ { "imageUrl": "替换为实际图片地址1", "mainText": "主文本内容1", "secondaryText": "副文本内容1" }, { "imageUrl": "替换为实际图片地址2", "mainText": "主文本内容2", "secondaryText": "副文本内容2" }, // 可继续添加更多数据项 ], itemWidth: 160, header: const Text("推荐内容", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), onItemTap: (index) { // 处理子项点击逻辑 }, )
内容的提问来源于stack exchange,提问作者Pranav Variar
相关产品推荐
相关产品推荐

