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

Flutter实现内容溢出时显示剩余项数量的Row组件

Flutter 实现溢出隐藏并显示剩余项数量的 Row 组件

要实现这种"内容溢出时隐藏多余项并显示剩余数量"的Row组件,核心是先测量每个子项的宽度,结合可用空间计算能容纳的项数,再选择性渲染。以下是完整的可复用组件实现:

完整代码

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

class OverflowRow extends StatefulWidget {
  final List<String> texts;
  final TextStyle? textStyle;
  final TextStyle? remainingStyle;

  const OverflowRow({
    super.key,
    required this.texts,
    this.textStyle,
    this.remainingStyle,
  });

  @override
  State<OverflowRow> createState() => _OverflowRowState();
}

class _OverflowRowState extends State<OverflowRow> {
  int _visibleCount = 0;
  int get _remainingCount => widget.texts.length - _visibleCount;

  @override
  void didUpdateWidget(covariant OverflowRow oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.texts != widget.texts) _calculateVisibleItems();
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _calculateVisibleItems();
  }

  void _calculateVisibleItems() {
    if (widget.texts.isEmpty) {
      setState(() => _visibleCount = 0);
      return;
    }

    final renderBox = context.findRenderObject() as RenderBox?;
    if (renderBox == null) return;

    final availableWidth = renderBox.size.width;
    final textPainter = TextPainter(textDirection: TextDirection.ltr);

    // 先计算剩余数量组件的宽度(含额外间距)
    textPainter.text = TextSpan(
      text: "+$_remainingCount",
      style: widget.remainingStyle ?? widget.textStyle,
    );
    textPainter.layout();
    final remainingWidth = textPainter.width + 8;

    double totalWidth = 0;
    int count = 0;

    // 逐个累加文本宽度,直到空间不足
    for (final text in widget.texts) {
      textPainter.text = TextSpan(text: text, style: widget.textStyle);
      textPainter.layout();
      final textWidth = textPainter.width + 8;

      if (totalWidth + textWidth + remainingWidth <= availableWidth) {
        totalWidth += textWidth;
        count++;
      } else {
        break;
      }
    }

    // 所有项都能放下时,不显示剩余计数
    setState(() => _visibleCount = count == widget.texts.length ? widget.texts.length : count);
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        // 布局完成后重新计算,确保尺寸准确
        WidgetsBinding.instance.addPostFrameCallback((_) => _calculateVisibleItems());

        final children = <Widget>[];

        // 添加可见的文本项
        for (int i = 0; i < _visibleCount; i++) {
          children.add(
            Padding(
              padding: const EdgeInsets.only(right: 8),
              child: Text(widget.texts[i], style: widget.textStyle),
            ),
          );
        }

        // 添加剩余数量标识(如果有剩余)
        if (_remainingCount > 0) {
          children.add(
            Text(
              "+$_remainingCount",
              style: widget.remainingStyle ?? widget.textStyle,
            ),
          );
        }

        return Row(children: children);
      },
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final texts = ["Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig", "Grape"];
    return Scaffold(
      appBar: AppBar(title: const Text("溢出Row示例")),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: OverflowRow(
          texts: texts,
          textStyle: const TextStyle(fontSize: 16),
          remainingStyle: const TextStyle(fontSize: 16, color: Colors.grey),
        ),
      ),
    );
  }
}

关键说明

  • 尺寸测量:用TextPainter预计算文本和剩余计数组件的宽度,避免实际渲染后才发现空间不足
  • 动态适配:通过didChangeDependencies、didUpdateWidget和LayoutBuilder确保布局或数据变化时重新计算可见项数
  • 间距处理:给文本和剩余计数添加额外间距,提升视觉体验
  • 边界情况:当所有文本都能容纳时,自动隐藏剩余计数组件

内容的提问来源于stack exchange,提问作者Łukasz Mysłajek

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 09:20:22