/// Renders Timeline Axis pieces for the FocusFlow Flutter timeline. library; import 'package:flutter/material.dart'; import '../../theme/focus_flow_tokens.dart'; import 'timeline_geometry.dart'; /// Time labels and rail shown beside the compact timeline. class TimelineAxis extends StatelessWidget { /// Creates a timeline axis for [geometry]. const TimelineAxis({required this.geometry, super.key}); /// Geometry used to position labels and tick marks. final TimelineGeometry geometry; @override Widget build(BuildContext context) { final ticks = geometry.ticks(); return SizedBox( width: FocusFlowTokens.timelineRailWidth, height: geometry.totalHeight + 24, child: Stack( children: [ Positioned( left: 92, top: 0, bottom: 0, child: Container(width: 2, color: FocusFlowTokens.rail), ), for (final tick in ticks) Positioned( top: tick.y - 10, left: 0, width: 86, child: Text( tick.label, textAlign: TextAlign.right, style: TextStyle( color: tick.major ? FocusFlowTokens.textPrimary : FocusFlowTokens.textFaint, fontSize: tick.major ? 16 : 14, fontWeight: tick.major ? FontWeight.w600 : FontWeight.w400, ), ), ), for (final tick in ticks.where((tick) => tick.major)) Positioned( top: tick.y - 6, left: 87, child: const DecoratedBox( decoration: BoxDecoration( color: FocusFlowTokens.rail, shape: BoxShape.circle, ), child: SizedBox(width: 12, height: 12), ), ), ], ), ); } } /// Background grid aligned to timeline tick marks. class TimelineGrid extends StatelessWidget { /// Creates a timeline grid for [geometry]. const TimelineGrid({required this.geometry, super.key}); /// Geometry used to position grid lines. final TimelineGeometry geometry; @override Widget build(BuildContext context) { return CustomPaint( painter: _TimelineGridPainter(geometry), size: Size.infinite, ); } } class _TimelineGridPainter extends CustomPainter { const _TimelineGridPainter(this.geometry); final TimelineGeometry geometry; @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = FocusFlowTokens.gridLine ..strokeWidth = 1; for (final tick in geometry.ticks()) { canvas.drawLine( Offset(0, tick.y), Offset(size.width, tick.y), paint ..color = tick.major ? FocusFlowTokens.gridLine.withValues(alpha: 0.72) : FocusFlowTokens.gridLine, ); } } @override bool shouldRepaint(covariant _TimelineGridPainter oldDelegate) { return oldDelegate.geometry != geometry; } }