import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../providers/connectivity_provider.dart'; /// Two-state "this item is not yet synced" indicator. /// /// Renders nothing when [isPending] is false. Otherwise shows: /// - **Amber** chip with a static sync icon when the device is offline /// ("Pending sync" — queued, will sync when network returns). /// - **Blue** chip with a rotating sync icon when the device is online /// ("Syncing…" — replay-in-flight or Brick queue draining). /// /// Use [compact] for dense list rows where only the icon is needed. class SyncPendingBadge extends ConsumerWidget { /// Whether the entity associated with this badge is still queued for sync. /// Caller is responsible for deriving this from the appropriate state /// provider (e.g., `offlinePendingTasksProvider.any((t) => t.id == taskId)`). final bool isPending; /// Compact (icon-only) variant for dense list rows. final bool compact; /// Optional override label — defaults to "Pending sync" (offline) or /// "Syncing…" (online). final String? label; const SyncPendingBadge({ super.key, required this.isPending, this.compact = false, this.label, }); @override Widget build(BuildContext context, WidgetRef ref) { if (!isPending) return const SizedBox.shrink(); final isOnline = ref.watch(isOnlineProvider); final scheme = Theme.of(context).colorScheme; final Color bg; final Color fg; final IconData icon; final String text; final bool spin; if (isOnline) { // Replay-in-flight: blue, animated. bg = scheme.primaryContainer; fg = scheme.onPrimaryContainer; icon = Icons.sync; text = label ?? 'Syncing…'; spin = true; } else { // Queued offline: amber, static. bg = Colors.amber.shade100; fg = Colors.amber.shade900; icon = Icons.sync; text = label ?? 'Pending sync'; spin = false; } final iconWidget = spin ? _RotatingIcon(icon: icon, color: fg, size: compact ? 12 : 14) : Icon(icon, color: fg, size: compact ? 12 : 14); if (compact) { return Container( padding: const EdgeInsets.all(4), decoration: BoxDecoration( color: bg, borderRadius: BorderRadius.circular(8), ), child: iconWidget, ); } return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration( color: bg, borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ iconWidget, const SizedBox(width: 4), Text( text, style: TextStyle( color: fg, fontSize: 11, fontWeight: FontWeight.w600, letterSpacing: 0.3, ), ), ], ), ); } } class _RotatingIcon extends StatefulWidget { final IconData icon; final Color color; final double size; const _RotatingIcon({ required this.icon, required this.color, required this.size, }); @override State<_RotatingIcon> createState() => _RotatingIconState(); } class _RotatingIconState extends State<_RotatingIcon> with SingleTickerProviderStateMixin { late final AnimationController _ctrl; @override void initState() { super.initState(); _ctrl = AnimationController(vsync: this, duration: const Duration(seconds: 2)) ..repeat(); } @override void dispose() { _ctrl.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return RotationTransition( turns: _ctrl, child: Icon(widget.icon, color: widget.color, size: widget.size), ); } }