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

如何在Flutter中实现带+号的有限dot_indicator(支持100+图片标识)

Flutter 实现带数量溢出提示的圆点指示器

核心思路

设定一个最大显示圆点数量(比如默认5个),根据总图片数量分两种情况处理:

  • 当总数量 ≤ 最大显示数:正常渲染所有圆点,高亮当前选中的索引
  • 当总数量 > 最大显示数:渲染前最大显示数-1个圆点,最后一个位置显示+剩余数量的文本,同时处理选中索引超出可见范围时的高亮逻辑

完整代码实现

import 'package:flutter/material.dart';

class LimitedDotIndicator extends StatelessWidget {
  final int totalCount;
  final int currentIndex;
  final int maxVisibleDots;
  final Color selectedColor;
  final Color unselectedColor;
  final double dotSize;
  final double spacing;

  const LimitedDotIndicator({
    super.key,
    required this.totalCount,
    required this.currentIndex,
    this.maxVisibleDots = 5,
    this.selectedColor = Colors.blue,
    this.unselectedColor = Colors.grey,
    this.dotSize = 10.0,
    this.spacing = 8.0,
  });

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: _buildIndicatorChildren(),
    );
  }

  List<Widget> _buildIndicatorChildren() {
    List<Widget> children = [];

    if (totalCount <= maxVisibleDots) {
      // 数量不多,显示所有圆点
      for (int i = 0; i < totalCount; i++) {
        children.add(
          _buildDot(isSelected: i == currentIndex),
        );
        if (i != totalCount - 1) {
          children.add(SizedBox(width: spacing));
        }
      }
    } else {
      // 数量过多,显示部分圆点+剩余数量文本
      final visibleDotCount = maxVisibleDots - 1;
      // 渲染前visibleDotCount个圆点
      for (int i = 0; i < visibleDotCount; i++) {
        children.add(
          _buildDot(isSelected: i == currentIndex),
        );
        children.add(SizedBox(width: spacing));
      }
      // 渲染剩余数量提示文本
      final remainingCount = totalCount - visibleDotCount;
      final isTextSelected = currentIndex >= visibleDotCount;
      children.add(
        Text(
          "+$remainingCount",
          style: TextStyle(
            color: isTextSelected ? selectedColor : unselectedColor,
            fontSize: dotSize * 1.2,
            fontWeight: isTextSelected ? FontWeight.bold : FontWeight.normal,
          ),
        ),
      );
    }

    return children;
  }

  Widget _buildDot({required bool isSelected}) {
    return Container(
      width: dotSize,
      height: dotSize,
      decoration: BoxDecoration(
        color: isSelected ? selectedColor : unselectedColor,
        borderRadius: BorderRadius.circular(dotSize / 2),
      ),
    );
  }
}

// 使用示例
class DemoPage extends StatefulWidget {
  const DemoPage({super.key});

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  int currentIndex = 0;
  // 模拟100+的图片数量
  final int totalImages = 120;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Dot Indicator Demo")),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          // 当前图片索引文本
          Text("当前第 ${currentIndex + 1} 张图片"),
          const SizedBox(height: 20),
          // 使用自定义指示器
          LimitedDotIndicator(
            totalCount: totalImages,
            currentIndex: currentIndex,
            maxVisibleDots: 5,
          ),
          const SizedBox(height: 20),
          // 切换按钮
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: currentIndex > 0
                    ? () => setState(() => currentIndex--)
                    : null,
                child: const Text("上一张"),
              ),
              const SizedBox(width: 20),
              ElevatedButton(
                onPressed: currentIndex < totalImages - 1
                    ? () => setState(() => currentIndex++)
                    : null,
                child: const Text("下一张"),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

关键细节说明

  • 可配置参数:你可以通过调整maxVisibleDots来设置最多显示的圆点数量,还能自定义圆点颜色、大小、间距等样式
  • 选中状态处理:当选中的索引超出了显示的圆点范围(比如第6张及以后,当maxVisibleDots=5时),剩余数量的文本会高亮,提示当前处于后面的图片组
  • 布局适配:使用mainAxisSize: MainAxisSize.min让指示器只占用必要的宽度,避免多余留白

内容的提问来源于stack exchange,提问作者Saurav Prakash

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 21:40:24