如何实现图示的复杂Flutter Timeline组件?已尝试指定代码
实现复杂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
相关产品推荐
相关产品推荐

