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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 17:30:47