import 'package:flutter/material.dart'; import '../../../app/app_theme.dart'; import '../../../l10n/l10n.dart'; import '../../../models/today_progress.dart'; import '../../../widgets/app_motion.dart'; class DailyProgressRail extends StatelessWidget { const DailyProgressRail({required this.progress, super.key}); final TodayProgress progress; @override Widget build(BuildContext context) { final l10n = context.l10n; final items = [ _ProgressItem( label: l10n.progressIntention, detail: progress.hasIntention ? l10n.done : l10n.next, icon: Icons.flag_outlined, complete: progress.hasIntention, ), _ProgressItem( label: l10n.progressFocus, detail: progress.hasFocus ? l10n.minutesShort(progress.focusMinutes) : l10n.next, icon: Icons.timer_outlined, complete: progress.hasFocus, ), _ProgressItem( label: l10n.progressReflect, detail: progress.hasReflection ? l10n.done : l10n.later, icon: Icons.edit_note_rounded, complete: progress.hasReflection, ), ]; final textScale = MediaQuery.textScalerOf(context).scale(12) / 12; return LayoutBuilder( builder: (context, constraints) { if (constraints.maxWidth < 320 || textScale > 1.35) { return _VerticalProgress(items: items); } return _HorizontalProgress(items: items); }, ); } } class _HorizontalProgress extends StatelessWidget { const _HorizontalProgress({required this.items}); final List<_ProgressItem> items; @override Widget build(BuildContext context) { final theme = Theme.of(context); final scheme = theme.colorScheme; final visuals = FocusSeedVisuals.of(context); final lastCompleteIndex = items.lastIndexWhere((item) => item.complete); final progress = lastCompleteIndex <= 0 ? 0.0 : lastCompleteIndex / (items.length - 1); return Semantics( label: context.l10n.dailyStepsComplete( items.where((item) => item.complete).length, ), child: Column( children: [ SizedBox( height: 32, child: LayoutBuilder( builder: (context, constraints) { final inset = constraints.maxWidth / (items.length * 2); return Stack( alignment: Alignment.center, children: [ Positioned( left: inset, right: inset, top: 15, child: _ProgressTrack( color: scheme.outlineVariant.withValues(alpha: 0.72), ), ), Positioned( left: inset, right: inset, top: 15, child: Align( alignment: Alignment.centerLeft, child: FractionallySizedBox( widthFactor: progress.clamp(0.0, 1.0), child: _ProgressTrack( color: visuals.success, glow: visuals.success.withValues(alpha: 0.20), ), ), ), ), Positioned.fill( child: Row( children: [ for (final item in items) Expanded( child: Center(child: _ProgressDot(item: item)), ), ], ), ), ], ); }, ), ), const SizedBox(height: 6), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ for (final item in items) Expanded( child: Column( children: [ Text( item.label, maxLines: 1, overflow: TextOverflow.ellipsis, style: theme.textTheme.labelSmall?.copyWith( fontWeight: FontWeight.w800, ), ), const SizedBox(height: 1), Text( item.detail, maxLines: 1, overflow: TextOverflow.ellipsis, style: theme.textTheme.labelSmall?.copyWith( color: scheme.onSurfaceVariant, fontSize: 10.5, ), ), ], ), ), ], ), ], ), ); } } class _VerticalProgress extends StatelessWidget { const _VerticalProgress({required this.items}); final List<_ProgressItem> items; @override Widget build(BuildContext context) { return Column( children: [ for (var index = 0; index < items.length; index++) ...[ Row( children: [ _ProgressDot(item: items[index]), const SizedBox(width: 10), Expanded( child: Text( items[index].label, style: Theme.of(context).textTheme.labelLarge, ), ), Text(items[index].detail), ], ), if (index < items.length - 1) const SizedBox(height: 10), ], ], ); } } class _ProgressTrack extends StatelessWidget { const _ProgressTrack({required this.color, this.glow}); final Color color; final Color? glow; @override Widget build(BuildContext context) { return AnimatedContainer( duration: AppMotion.duration(context, AppMotion.medium), curve: AppMotion.emphasized, height: 2, decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(99), boxShadow: glow == null ? null : [BoxShadow(color: glow!, blurRadius: 10)], ), ); } } class _ProgressDot extends StatelessWidget { const _ProgressDot({required this.item}); final _ProgressItem item; @override Widget build(BuildContext context) { final theme = Theme.of(context); final scheme = theme.colorScheme; final visuals = FocusSeedVisuals.of(context); return AnimatedScale( scale: item.complete ? 1.08 : 1, duration: AppMotion.duration(context, AppMotion.fast), curve: AppMotion.settle, child: AnimatedContainer( duration: AppMotion.duration(context, AppMotion.medium), curve: AppMotion.emphasized, width: 28, height: 28, decoration: BoxDecoration( shape: BoxShape.circle, color: item.complete ? visuals.success : scheme.surfaceContainerLow, border: Border.all( color: item.complete ? visuals.success : scheme.outlineVariant, ), boxShadow: item.complete ? [ BoxShadow( color: visuals.success.withValues(alpha: 0.20), blurRadius: 8, offset: const Offset(0, 3), ), ] : null, ), child: AnimatedSwitcher( duration: AppMotion.duration(context, AppMotion.fast), switchInCurve: AppMotion.settle, switchOutCurve: Curves.easeInCubic, transitionBuilder: (child, animation) { return FadeTransition( opacity: animation, child: ScaleTransition(scale: animation, child: child), ); }, child: Icon( item.complete ? Icons.check_rounded : item.icon, key: ValueKey('${item.label}-${item.complete}'), size: 15, color: item.complete ? scheme.onPrimary : scheme.onSurfaceVariant, ), ), ), ); } } class _ProgressItem { const _ProgressItem({ required this.label, required this.detail, required this.icon, required this.complete, }); final String label; final String detail; final IconData icon; final bool complete; }