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

如何在Flutter中实现Figma中的展开式连接器设计?

自定义ExpansionTile连接器样式,还原Figma设计

核心思路

ExpansionTile默认的展开/收起箭头没法直接实现你要的两种连接器样式,核心是自定义trailing组件,根据展开状态切换绘制逻辑,用CustomPaint能精准复刻设计细节。

实现代码示例

1. 自定义连接器组件

写一个可根据展开状态切换样式的组件,用CustomPaint绘制两种状态的线条和箭头:

class CustomConnector extends StatelessWidget {
  final bool isExpanded;

  const CustomConnector({super.key, required this.isExpanded});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 24,
      height: 24,
      child: CustomPaint(
        painter: _ConnectorPainter(isExpanded: isExpanded),
      ),
    );
  }
}

class _ConnectorPainter extends CustomPainter {
  final bool isExpanded;
  final Paint _paint = Paint()
    ..color = Colors.grey.shade600
    ..strokeWidth = 2
    ..style = PaintingStyle.stroke
    ..strokeCap = StrokeCap.round;

  _ConnectorPainter({required this.isExpanded});

  @override
  void paint(Canvas canvas, Size size) {
    final centerX = size.width / 2;
    final centerY = size.height / 2;

    if (!isExpanded) {
      // 未点击状态:横线+向右箭头
      canvas.drawLine(Offset(0, centerY), Offset(centerX - 4, centerY), _paint);
      canvas.drawLine(Offset(centerX - 4, centerY), Offset(centerX, centerY - 4), _paint);
      canvas.drawLine(Offset(centerX - 4, centerY), Offset(centerX, centerY + 4), _paint);
    } else {
      // 已点击状态:竖线+向下箭头
      canvas.drawLine(Offset(centerX, 0), Offset(centerX, centerY - 4), _paint);
      canvas.drawLine(Offset(centerX, centerY - 4), Offset(centerX - 4, centerY), _paint);
      canvas.drawLine(Offset(centerX, centerY - 4), Offset(centerX + 4, centerY), _paint);
    }
  }

  @override
  bool shouldRepaint(covariant _ConnectorPainter oldDelegate) {
    return oldDelegate.isExpanded != isExpanded;
  }
}

2. 替换ExpansionTile的trailing

把自定义组件放到ExpansionTile里,绑定展开状态:

// 用StatefulWidget管理展开状态
class CustomExpansionTile extends StatefulWidget {
  const CustomExpansionTile({super.key});

  @override
  State<CustomExpansionTile> createState() => _CustomExpansionTileState();
}

class _CustomExpansionTileState extends State<CustomExpansionTile> {
  bool _isExpanded = false;

  @override
  Widget build(BuildContext context) {
    return ExpansionTile(
      title: const Text("示例标题"),
      trailing: CustomConnector(isExpanded: _isExpanded),
      onExpansionChanged: (expanded) {
        setState(() => _isExpanded = expanded);
      },
      children: const [
        Padding(
          padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
          child: Text("展开后的内容区域"),
        ),
      ],
    );
  }
}

3. 细节调整

  • 要完全匹配Figma设计,直接修改_paint里的color、strokeWidth参数即可
  • 要是需要点击反馈,可给CustomConnector套一层GestureDetector(不过ExpansionTile本身已经有点击事件)
  • 若设计有渐变、圆角等特殊效果,直接在CustomPaint的paint方法里补充绘制逻辑

简易替代方案

如果不想用CustomPaint,也可以用现成Icon组合(灵活性稍差):

  • 未展开状态:Row(children: [Icon(Icons.minor_crash, size: 16), Icon(Icons.arrow_right, size: 16)])
  • 展开状态:Column(children: [Icon(Icons.minor_crash, size: 16), Icon(Icons.arrow_drop_down, size: 16)])

内容的提问来源于stack exchange,提问作者Alvaro Dosio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 08:55:08