Flutter可展开收起文本组件 收起时底部模糊效果未恢复问题
问题根因
遮罩无法恢复是颜色赋值逻辑写错了:
- 初始时
fadeStrong、fadeWeak是半透黑色,收起状态遮罩正常 - 第一次点展开,你直接把两个颜色变量改成了透明值,这时候变量存的已经不是原来的半透黑了
- 第二次点收起,判断分支写的是
hiddenText ? fadeStrong = fadeStrong,等于把已经是透明的值又赋给自己,根本没重置回初始颜色,遮罩当然出不来
另外你现在叠两个纯色黑块的实现不算真模糊,视觉生硬;还有拿字符数和像素高度比着截断文本,单位不统一,截断位置会乱。
修复代码
直接去掉手动修改颜色变量的逻辑,遮罩显隐完全跟着hiddenText状态走,同时把纯色遮罩换成渐变效果更自然,修复后完整代码如下:
import 'dart:ui'; import 'package:flutter/material.dart'; class ExpandableText extends StatefulWidget { final String text; const ExpandableText({Key? key, required this.text}) : super(key: key); @override State<ExpandableText> createState() => _ExpandableTextState(); } class _ExpandableTextState extends State<ExpandableText> { late String firstHalf; late String secondHalf; bool hiddenText = true; final double textHeight = 180; // 固定遮罩初始颜色,设为final禁止运行时修改 final Color fadeStrong = Colors.black.withOpacity(0.8); final Color fadeWeak = Colors.black.withOpacity(0.6); @override void initState() { super.initState(); // 注:原逻辑拿字符数和像素高度对比单位不统一,后续可通过LayoutBuilder+TextPainter计算真实截断位置,本次先保留原有逻辑解决遮罩问题 if (widget.text.length > textHeight) { firstHalf = widget.text.substring(0, textHeight.toInt()); secondHalf = widget.text.substring(textHeight.toInt() + 1, widget.text.length); } else { firstHalf = widget.text; secondHalf = ""; } } @override Widget build(BuildContext context) { return Stack( children: [ Container( child: secondHalf.isEmpty ? Text( firstHalf, style: const TextStyle(color: Colors.white), ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( hiddenText ? ("$firstHalf...") : (firstHalf + secondHalf), style: const TextStyle(color: Colors.white, height: 1.5), ), InkWell( onTap: () { setState(() { hiddenText = !hiddenText; }); }, child: Row( mainAxisSize: MainAxisSize.min, children: [ Text( hiddenText ? 'Read more' : 'Read less', style: const TextStyle(color: Colors.blue), ), Icon( hiddenText ? Icons.arrow_drop_down : Icons.arrow_drop_up_outlined, color: Colors.blue, ) ], ), ) ], ), ), // 仅在收起+文本需要截断时渲染遮罩,展开时直接从组件树移除 if (hiddenText && secondHalf.isNotEmpty) Positioned( bottom: 40, // 位置避开下方的展开/收起按钮 left: 0, right: 0, height: 40, // 不需要真模糊效果可以删掉ClipRect和BackdropFilter层,仅保留渐变容器即可 child: ClipRect( child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 0.8, sigmaY: 0.8), child: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.black.withOpacity(0), Colors.black.withOpacity(0.6), Colors.black.withOpacity(0.8), ], ), ), ), ), ), ), ], ); } }
修复说明
- 把颜色变量设为
final常量,不再在点击事件里手动修改,从根源避免变量值被改乱 - 遮罩层通过条件判断渲染,展开时直接移除组件,不用靠改透明度隐藏
- 可选加了
BackdropFilter实现真正的文字模糊效果,搭配线性渐变过渡比原来的两个硬黑条自然很多 - 修正了Column默认交叉对齐问题,避免文本和按钮布局错位
效果参考
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

