如何在Flutter中实现类似Zalo的图片列表展示
Flutter实现Zalo风格图片列表展示效果
从参考图样式来看,Zalo的图片列表核心是根据图片数量自适应布局:单张图按比例展示,多张图以紧凑的交错/网格排列,同时支持点击预览。以下是具体实现方案:
核心布局逻辑
根据图片数量动态切换布局样式:
- 单张图片:全屏宽度的圆角矩形图
- 2张图片:左右均分的两个圆角矩形图
- 3张图片:左列1张占2行、右列2张各占1行的交错布局
- 4张及以上:3列网格,最多显示9张,超过则在最后一格标记剩余数量
代码实现
1. 添加依赖(如需交错网格与图片缓存)
在pubspec.yaml中加入依赖:
dependencies: flutter: sdk: flutter flutter_staggered_grid_view: ^0.7.0 # 交错网格布局 cached_network_image: ^3.2.3 # 图片缓存与占位
2. 布局代码实现
import 'package:flutter/material.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:cached_network_image/cached_network_image.dart'; class ZaloImageList extends StatelessWidget { final List<String> imageUrls; const ZaloImageList({super.key, required this.imageUrls}); @override Widget build(BuildContext context) { switch (imageUrls.length) { case 1: return _buildSingleImage(imageUrls[0]); case 2: return _buildTwoImages(imageUrls); case 3: return _buildThreeImages(imageUrls); default: return _buildMultiImages(imageUrls); } } // 单张图片布局 Widget _buildSingleImage(String url) { return ClipRRect( borderRadius: BorderRadius.circular(8), child: CachedNetworkImage( imageUrl: url, fit: BoxFit.cover, height: 200, width: double.infinity, placeholder: (context, url) => const Center(child: CircularProgressIndicator()), errorWidget: (context, url, error) => const Icon(Icons.error_outline), ), ); } // 两张图片布局 Widget _buildTwoImages(List<String> urls) { return Row( children: [ Expanded( child: Padding( padding: const EdgeInsets.only(right: 4), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: CachedNetworkImage( imageUrl: urls[0], fit: BoxFit.cover, height: 120, placeholder: (context, url) => const Center(child: CircularProgressIndicator()), errorWidget: (context, url, error) => const Icon(Icons.error_outline), ), ), ), ), Expanded( child: ClipRRect( borderRadius: BorderRadius.circular(8), child: CachedNetworkImage( imageUrl: urls[1], fit: BoxFit.cover, height: 120, placeholder: (context, url) => const Center(child: CircularProgressIndicator()), errorWidget: (context, url, error) => const Icon(Icons.error_outline), ), ), ), ], ); } // 三张图片交错布局 Widget _buildThreeImages(List<String> urls) { return StaggeredGrid.count( crossAxisCount: 2, mainAxisSpacing: 4, crossAxisSpacing: 4, children: [ StaggeredGridTile.count( crossAxisCellCount: 1, mainAxisCellCount: 2, child: ClipRRect( borderRadius: BorderRadius.circular(8), child: CachedNetworkImage( imageUrl: urls[0], fit: BoxFit.cover, placeholder: (context, url) => const Center(child: CircularProgressIndicator()), errorWidget: (context, url, error) => const Icon(Icons.error_outline), ), ), ), StaggeredGridTile.count( crossAxisCellCount: 1, mainAxisCellCount: 1, child: ClipRRect( borderRadius: BorderRadius.circular(8), child: CachedNetworkImage( imageUrl: urls[1], fit: BoxFit.cover, placeholder: (context, url) => const Center(child: CircularProgressIndicator()), errorWidget: (context, url, error) => const Icon(Icons.error_outline), ), ), ), StaggeredGridTile.count( crossAxisCellCount: 1, mainAxisCellCount: 1, child: ClipRRect( borderRadius: BorderRadius.circular(8), child: CachedNetworkImage( imageUrl: urls[2], fit: BoxFit.cover, placeholder: (context, url) => const Center(child: CircularProgressIndicator()), errorWidget: (context, url, error) => const Icon(Icons.error_outline), ), ), ), ], ); } // 四张及以上网格布局(最多显示9张) Widget _buildMultiImages(List<String> urls) { final displayUrls = urls.take(9).toList(); final hasMore = urls.length > 9; return GridView.count( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisCount: 3, mainAxisSpacing: 4, crossAxisSpacing: 4, children: [ ...displayUrls.map((url) => ClipRRect( borderRadius: BorderRadius.circular(8), child: CachedNetworkImage( imageUrl: url, fit: BoxFit.cover, placeholder: (context, url) => const Center(child: CircularProgressIndicator()), errorWidget: (context, url, error) => const Icon(Icons.error_outline), ), )), if (hasMore) Container( decoration: BoxDecoration( color: Colors.black.withOpacity(0.5), borderRadius: BorderRadius.circular(8), ), child: Center( child: Text( "+${urls.length - 9}", style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold), ), ), ), ], ); } }
扩展功能:图片预览
如果需要点击图片全屏预览,可以集成photo_view包,给图片添加点击事件:
// 示例:给单张图片添加预览逻辑 GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => PhotoView(imageProvider: CachedNetworkImageProvider(url)), ), ); }, child: _buildSingleImage(url), )
内容的提问来源于stack exchange,提问作者user20393696
相关产品推荐
相关产品推荐

