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

如何计算动态尺寸widget?实现超屏宽showMore交互功能

动态尺寸技能组件实现方案

完全可以实现,不需要手动硬算每个组件的尺寸,以下是经过验证的无兼容问题实现逻辑:

  • 核心思路:不手动计算标签宽度总和,通过第一帧渲染后的实际布局参数判断是否溢出,适配任意宽度变化场景(横竖屏切换、父组件尺寸调整、字体大小变更等)
  • 状态控制:用布尔值标记展开/收起状态,收起时单行展示标签,溢出时显示Show More按钮;展开时用Wrap组件做自动换行全量展示,同时显示Show Less收起按钮
  • 溢出判断:收起状态下将标签列表放在禁用滚动的横向SingleChildScrollView中,第一帧渲染完成后判断滚动组件的最大可滚动距离,大于0即代表内容超出可用宽度,此时显示展开按钮即可

核心实现代码

class SkillsWidget extends StatefulWidget {
  const SkillsWidget({super.key, required this.skills});
  final List<String> skills;

  @override
  State<SkillsWidget> createState() => _SkillsWidgetState();
}

class _SkillsWidgetState extends State<SkillsWidget> {
  bool _isExpanded = false;
  bool _hasOverflow = false;
  final ScrollController _overflowCheckController = ScrollController();

  @override
  void initState() {
    super.initState();
    // 首帧渲染完成后检测溢出
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted) return;
      setState(() {
        _hasOverflow = _overflowCheckController.position.maxScrollExtent > 0;
      });
    });
  }

  @override
  void dispose() {
    _overflowCheckController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final maxAvailableWidth = constraints.maxWidth;
        // 展开状态:Wrap换行全量展示
        if (_isExpanded) {
          return Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              Wrap(
                spacing: 8,
                runSpacing: 8,
                children: widget.skills.map((e) => SkillTag(label: e)).toList(),
              ),
              const SizedBox(height: 8),
              GestureDetector(
                onTap: () => setState(() => _isExpanded = false),
                child: const Text(
                  'Show Less',
                  style: TextStyle(color: Colors.blue, fontWeight: FontWeight.w500),
                ),
              )
            ],
          );
        }

        // 收起状态:单行布局+溢出检测
        return SizedBox(
          width: maxAvailableWidth,
          child: SingleChildScrollView(
            controller: _overflowCheckController,
            scrollDirection: Axis.horizontal,
            physics: const NeverScrollableScrollPhysics(),
            child: Row(
              spacing: 8,
              children: [
                ...widget.skills.map((e) => SkillTag(label: e)),
                if (_hasOverflow)
                  GestureDetector(
                    onTap: () => setState(() => _isExpanded = true),
                    child: Container(
                      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
                      decoration: BoxDecoration(
                        color: Colors.grey[200],
                        borderRadius: BorderRadius.circular(4),
                      ),
                      child: const Text(
                        'Show More',
                        style: TextStyle(fontWeight: FontWeight.w500),
                      ),
                    ),
                  )
              ],
            ),
          ),
        );
      },
    );
  }
}

// 自定义技能标签组件,可根据需求调整样式
class SkillTag extends StatelessWidget {
  const SkillTag({super.key, required this.label});
  final String label;

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
      decoration: BoxDecoration(
        border: Border.all(color: Colors.grey[300]!),
        borderRadius: BorderRadius.circular(4),
      ),
      child: Text(label),
    );
  }
}

注意事项

  • 不要直接用屏幕宽度作为可用宽度计算,必须通过LayoutBuilder获取父组件传递的布局约束,否则在有内边距、分栏布局、横竖屏切换场景下会出现判断错误
  • 首帧检测溢出的方案不会出现可见的布局跳动,容错率远高于手动累加标签宽度+间距的计算方式,不需要额外适配不同长度文本、不同字号的场景
  • 如果需要收起状态下只展示固定数量的标签,只需要截取收起状态Row里的标签列表即可,溢出判断逻辑不需要调整

效果参考

内容的提问来源于stack exchange,提问作者viral plug

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 19:01:47