import 'package:flutter/material.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:shimmer/shimmer.dart'; import '../theme/spacing.dart'; class SkeletonLineCard extends StatelessWidget { const SkeletonLineCard({ super.key, this.lineWidths = const [0.4, 1, 0.8, 0.6], this.height, }); final List lineWidths; final double? height; @override Widget build(BuildContext context) { final cs = ShadTheme.of(context).colorScheme; return Container( height: height, width: double.infinity, padding: const EdgeInsets.all(Spacing.cardPadding), decoration: BoxDecoration( color: cs.card, border: Border.all(color: cs.border, width: 1), borderRadius: BorderRadius.circular(Spacing.radiusCard), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ for (var i = 0; i < lineWidths.length; i++) ...[ _SkeletonLine(widthFactor: lineWidths[i]), if (i != lineWidths.length - 1) const SizedBox(height: 10), ], ], ), ); } } class SkeletonInstitutionCard extends StatelessWidget { const SkeletonInstitutionCard({super.key}); @override Widget build(BuildContext context) { return const SkeletonLineCard(lineWidths: [0.4, 1, 0.6]); } } class SkeletonListenCard extends StatelessWidget { const SkeletonListenCard({super.key}); @override Widget build(BuildContext context) { return const SkeletonLineCard(lineWidths: [0.4, 1, 0.6]); } } class SkeletonSectionCard extends StatelessWidget { const SkeletonSectionCard({ super.key, required this.roman, required this.lineWidths, this.baseColor, this.highlightColor, }); final String roman; final List lineWidths; final Color? baseColor; final Color? highlightColor; @override Widget build(BuildContext context) { final cs = ShadTheme.of(context).colorScheme; final brightness = ShadTheme.of(context).brightness; final resolvedBaseColor = baseColor ?? (brightness == Brightness.dark ? cs.secondary : cs.border.withValues(alpha: 0.95)); final resolvedHighlightColor = highlightColor ?? (brightness == Brightness.dark ? cs.card : cs.secondary.withValues(alpha: 0.95)); return Container( width: double.infinity, padding: const EdgeInsets.symmetric( horizontal: Spacing.cardPadding, vertical: 16, ), decoration: BoxDecoration( color: cs.card, border: Border.all(color: cs.border, width: 1), borderRadius: BorderRadius.circular(Spacing.radiusCard), ), child: Shimmer.fromColors( baseColor: resolvedBaseColor, highlightColor: resolvedHighlightColor, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ for (var i = 0; i < lineWidths.length; i++) ...[ _SkeletonLine( widthFactor: lineWidths[i], height: i == 0 ? 14 : 12, ), if (i != lineWidths.length - 1) const SizedBox(height: 10), ], const SizedBox(height: 2), ], ), ), ); } } class _SkeletonLine extends StatelessWidget { const _SkeletonLine({required this.widthFactor, this.height = 12}); final double widthFactor; final double height; @override Widget build(BuildContext context) { final cs = ShadTheme.of(context).colorScheme; return FractionallySizedBox( widthFactor: widthFactor, child: Shimmer.fromColors( baseColor: cs.secondary, highlightColor: cs.muted, child: Container( height: height, decoration: BoxDecoration( color: cs.secondary, borderRadius: BorderRadius.circular(6), ), ), ), ); } } class SectionSkeletonList extends StatelessWidget { const SectionSkeletonList({ super.key, required this.count, required this.lineWidthsForIndex, this.romanLabels, }); final int count; final List Function(int index) lineWidthsForIndex; final List? romanLabels; @override Widget build(BuildContext context) { final labels = romanLabels ?? List.generate(count, (index) => _roman(index + 1)); return SliverPadding( padding: const EdgeInsets.fromLTRB(Spacing.page, 4, Spacing.page, 24), sliver: SliverList.separated( itemCount: count, separatorBuilder: (_, _) => const SizedBox(height: Spacing.cardGap), itemBuilder: (context, index) => SkeletonSectionCard( roman: labels[index], lineWidths: lineWidthsForIndex(index), ), ), ); } } class SectionSkeletonListView extends StatelessWidget { const SectionSkeletonListView({ super.key, required this.count, required this.lineWidthsForIndex, this.romanLabels, }); final int count; final List Function(int index) lineWidthsForIndex; final List? romanLabels; @override Widget build(BuildContext context) { final labels = romanLabels ?? List.generate(count, (index) => _roman(index + 1)); return ListView.separated( padding: const EdgeInsets.only(bottom: 72), itemCount: count, separatorBuilder: (_, _) => const SizedBox(height: Spacing.cardGap), itemBuilder: (context, index) => SkeletonSectionCard( roman: labels[index], lineWidths: lineWidthsForIndex(index), ), ); } } String _roman(int value) { const numerals = ['I.', 'II.', 'III.', 'IV.', 'V.', 'VI.', 'VII.', 'VIII.']; if (value <= 0 || value > numerals.length) return '$value.'; return numerals[value - 1]; }