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

如何实现图示的复杂Flutter Timeline组件?已尝试指定代码

实现复杂Flutter时间线组件方案

参考图示的时间线样式:
复杂Flutter时间线示例

你提到已尝试过基础的时间线实现方案,以下是针对该复杂样式的落地实现思路及代码:

核心实现思路

该时间线的核心是自定义时间线条目组件,每个条目包含三部分:左侧状态节点、节点间的垂直连线、右侧内容区域。通过ListView.builder实现滚动列表,根据条目的位置(首项、中间项、末项)控制连线的显示逻辑,同时支持节点的状态切换(当前/已完成/未开始)。

1. 定义数据模型

先定义时间线条目的数据结构,包含状态、标题、时间、详情:

enum TimelineStatus { completed, current, pending }

class TimelineItemModel {
  final TimelineStatus status;
  final String title;
  final String time;
  final String description;

  TimelineItemModel({
    required this.status,
    required this.title,
    required this.time,
    required this.description,
  });
}

2. 自定义时间线条目组件

创建CustomTimelineItem组件,处理节点样式、连线逻辑:

class CustomTimelineItem extends StatelessWidget {
  final TimelineItemModel item;
  final bool isFirst;
  final bool isLast;

  const CustomTimelineItem({
    super.key,
    required this.item,
    required this.isFirst,
    required this.isLast,
  });

  @override
  Widget build(BuildContext context) {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        // 左侧节点与连线区域
        Column(
          children: [
            // 上方连线(首项不显示)
            if (!isFirst)
              Container(
                width: 2,
                height: 30,
                color: item.status == TimelineStatus.pending ? Colors.grey.shade300 : Colors.blue,
              ),
            // 状态节点
            Container(
              width: 20,
              height: 20,
              decoration: BoxDecoration(
                shape: BoxShape.circle,
                color: _getNodeColor(),
                border: item.status == TimelineStatus.pending ? Border.all(color: Colors.grey.shade300) : null,
              ),
              child: item.status == TimelineStatus.current
                  ? const Icon(Icons.check, size: 12, color: Colors.white)
                  : null,
            ),
            // 下方连线(末项不显示)
            if (!isLast)
              Container(
                width: 2,
                height: 30,
                color: item.status == TimelineStatus.pending ? Colors.grey.shade300 : Colors.blue,
              ),
          ],
        ),
        const SizedBox(width: 16),
        // 右侧内容区域
        Expanded(
          child: Padding(
            padding: const EdgeInsets.symmetric(vertical: 16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  item.title,
                  style: TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.bold,
                    color: item.status == TimelineStatus.current ? Colors.blue : Colors.black,
                  ),
                ),
                const SizedBox(height: 4),
                Text(
                  item.time,
                  style: TextStyle(fontSize: 12, color: Colors.grey.shade600),
                ),
                const SizedBox(height: 8),
                Text(
                  item.description,
                  style: TextStyle(fontSize: 14, color: Colors.grey.shade700),
                ),
              ],
            ),
          ),
        ),
      ],
    );
  }

  Color _getNodeColor() {
    switch (item.status) {
      case TimelineStatus.completed:
        return Colors.blue;
      case TimelineStatus.current:
        return Colors.blue;
      case TimelineStatus.pending:
        return Colors.white;
    }
  }
}

3. 构建时间线列表

在页面中使用ListView.builder渲染所有时间线条目:

class ComplexTimelinePage extends StatelessWidget {
  final List<TimelineItemModel> timelineItems = [
    TimelineItemModel(
      status: TimelineStatus.completed,
      title: "订单已创建",
      time: "2024-05-20 10:30",
      description: "您的订单已成功创建,等待商家确认",
    ),
    TimelineItemModel(
      status: TimelineStatus.completed,
      title: "商家已接单",
      time: "2024-05-20 10:45",
      description: "商家已确认订单,开始准备商品",
    ),
    TimelineItemModel(
      status: TimelineStatus.current,
      title: "商品已发出",
      time: "2024-05-20 14:20",
      description: "快递小哥已取件,正在配送途中",
    ),
    TimelineItemModel(
      status: TimelineStatus.pending,
      title: "快递已送达",
      time: "预计2024-05-21 09:00",
      description: "快递将在明天上午送达,请留意收件",
    ),
  ];

  ComplexTimelinePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("订单时间线")),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16),
        child: ListView.builder(
          itemCount: timelineItems.length,
          itemBuilder: (context, index) {
            return CustomTimelineItem(
              item: timelineItems[index],
              isFirst: index == 0,
              isLast: index == timelineItems.length - 1,
            );
          },
        ),
      ),
    );
  }
}

扩展优化点

  • 样式自定义:可将节点大小、颜色、连线宽度等抽成参数,方便复用不同样式的时间线
  • 动画效果:为当前节点添加缩放动画,或切换状态时添加过渡效果
  • 横向时间线:修改布局为Column配合SingleChildScrollView实现横向滚动的时间线
  • 性能优化:使用const构造函数、避免不必要的重建,大数据量时考虑使用ListView.separated

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 20:57:29