判断文字行数,加展开、收起

在Flutter中判断文本是否超出三行,可以使用以下几种方法:
方法一:使用TextPainter(推荐)

class TextLineChecker extends StatelessWidget {
  final String text;
  final TextStyle style;
  final double maxWidth;
  
  const TextLineChecker({
    Key? key,
    required this.text,
    required this.style,
    required this.maxWidth,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        // 显示文本
        Container(
          width: maxWidth,
          child: Text(
            text,
            style: style,
            maxLines: 3,
            overflow: TextOverflow.ellipsis,
          ),
        ),
        
        // 判断是否超过三行
        FutureBuilder<bool>(
          future: _isTextExceedingThreeLines(),
          builder: (context, snapshot) {
            if (snapshot.hasData && snapshot.data == true) {
              return TextButton(
                onPressed: () {
                  // 处理展开/收起逻辑
                },
                child: Text('展开'),
              );
            }
            return SizedBox.shrink();
          },
        ),
      ],
    );
  }

  Future<bool> _isTextExceedingThreeLines() async {
    final TextPainter textPainter = TextPainter(
      text: TextSpan(text: text, style: style),
      maxLines: 3,
      textDirection: TextDirection.ltr,
    )..layout(maxWidth: maxWidth);

    // 判断是否被截断
    return textPainter.didExceedMaxLines;
  }
}

方法二:同步判断(使用StatefulWidget)

class TextLineCheckWidget extends StatefulWidget {
  final String text;
  final TextStyle style;
  final double maxWidth;
  
  const TextLineCheckWidget({
    Key? key,
    required this.text,
    required this.style,
    required this.maxWidth,
  }) : super(key: key);

  @override
  _TextLineCheckWidgetState createState() => _TextLineCheckWidgetState();
}

class _TextLineCheckWidgetState extends State<TextLineCheckWidget> {
  bool _isExceeding = false;
  bool _isExpanded = false;
  
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _checkTextLines();
    });
  }

  void _checkTextLines() {
    final TextPainter textPainter = TextPainter(
      text: TextSpan(
        text: widget.text,
        style: widget.style,
      ),
      maxLines: 3,
      textDirection: TextDirection.ltr,
    )..layout(maxWidth: widget.maxWidth);
    
    setState(() {
      _isExceeding = textPainter.didExceedMaxLines;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          widget.text,
          style: widget.style,
          maxLines: _isExpanded ? null : 3,
          overflow: _isExpanded ? null : TextOverflow.ellipsis,
        ),
        
        if (_isExceeding)
          TextButton(
            onPressed: () {
              setState(() {
                _isExpanded = !_isExpanded;
              });
            },
            child: Text(_isExpanded ? '收起' : '展开'),
          ),
      ],
    );
  }
}

方法三:使用LayoutBuilder获取宽度

class TextLineChecker extends StatefulWidget {
  final String text;
  final TextStyle style;
  
  const TextLineChecker({
    Key? key,
    required this.text,
    required this.style,
  }) : super(key: key);

  @override
  _TextLineCheckerState createState() => _TextLineCheckerState();
}

class _TextLineCheckerState extends State<TextLineChecker> {
  bool _isExceeding = false;
  bool _isExpanded = false;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        // 获取可用宽度
        final maxWidth = constraints.maxWidth;
        
        // 检查是否超过三行
        WidgetsBinding.instance.addPostFrameCallback((_) {
          _checkTextLines(maxWidth);
        });

        return Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              widget.text,
              style: widget.style,
              maxLines: _isExpanded ? null : 3,
              overflow: _isExpanded ? null : TextOverflow.ellipsis,
            ),
            
            if (_isExceeding)
              TextButton(
                onPressed: () {
                  setState(() {
                    _isExpanded = !_isExpanded;
                  });
                },
                child: Text(_isExpanded ? '收起' : '展开'),
              ),
          ],
        );
      },
    );
  }

  void _checkTextLines(double maxWidth) {
    final TextPainter textPainter = TextPainter(
      text: TextSpan(
        text: widget.text,
        style: widget.style,
      ),
      maxLines: 3,
      textDirection: TextDirection.ltr,
    )..layout(maxWidth: maxWidth);
    
    if (_isExceeding != textPainter.didExceedMaxLines) {
      setState(() {
        _isExceeding = textPainter.didExceedMaxLines;
      });
    }
  }
}

方法四:简单的工具函数

class TextUtils {
  static Future<bool> isTextExceedingLines({
    required String text,
    required TextStyle style,
    required double maxWidth,
    int maxLines = 3,
  }) async {
    final TextPainter textPainter = TextPainter(
      text: TextSpan(text: text, style: style),
      maxLines: maxLines,
      textDirection: TextDirection.ltr,
    )..layout(maxWidth: maxWidth);
    
    return textPainter.didExceedMaxLines;
  }
}

// 使用示例
class MyWidget extends StatelessWidget {
  final String text = "这是一段很长的文本...";
  
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<bool>(
      future: TextUtils.isTextExceedingLines(
        text: text,
        style: TextStyle(fontSize: 16),
        maxWidth: 300,
      ),
      builder: (context, snapshot) {
        return Column(
          children: [
            Text(
              text,
              maxLines: 3,
              overflow: TextOverflow.ellipsis,
            ),
            if (snapshot.data == true)
              Text('文本超过三行'),
          ],
        );
      },
    );
  }
}

注意事项
1.TextPainter 需要在布局之后才能准确计算

2.使用 addPostFrameCallback 确保在布局完成后计算

3.考虑文本方向(LTR/RTL)

4.注意字体大小、行高等因素会影响行数计算

5.如果使用 maxLines: null,didExceedMaxLines 始终返回 false

推荐使用方法二或方法三,它们提供了完整的展开/收起功能,并且考虑了实际布局情况。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容