import 'package:flutter/material.dart'; import '../layout/adaptive_card_grid.dart'; import '../theme/app_theme.dart'; class AppSkeleton extends StatefulWidget { final double? width; final double? height; final double radius; const AppSkeleton({super.key, this.width, this.height, this.radius = 8}); @override State createState() => _AppSkeletonState(); } class _AppSkeletonState extends State with SingleTickerProviderStateMixin { late final AnimationController _c = AnimationController( vsync: this, duration: const Duration(milliseconds: 1100), )..repeat(reverse: true); @override void dispose() { _c.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final base = Theme.of(context).colorScheme.onSurface; return AnimatedBuilder( animation: _c, builder: (context, _) { final alpha = 0.04 + 0.05 * _c.value; return Container( width: widget.width, height: widget.height, decoration: BoxDecoration( color: base.withValues(alpha: alpha), borderRadius: BorderRadius.circular(widget.radius), ), ); }, ); } } class SkeletonGrid extends StatelessWidget { final int itemCount; final double itemHeight; final double minCardWidth; final EdgeInsetsGeometry padding; const SkeletonGrid({ super.key, this.itemCount = 6, this.itemHeight = 84, this.minCardWidth = kRichCardMinWidth, this.padding = const EdgeInsets.symmetric(horizontal: 24), }); @override Widget build(BuildContext context) { final tokens = context.appTokens; return Padding( padding: padding, child: LayoutBuilder( builder: (_, constraints) { final columns = cardColumnsFor( constraints.maxWidth, minCardWidth: minCardWidth, ); final width = (constraints.maxWidth - kCardGap * (columns - 1)) / columns; return Wrap( spacing: kCardGap, runSpacing: kCardGap, children: [ for (var i = 0; i < itemCount; i++) SizedBox( width: width, child: AppSkeleton( height: itemHeight, radius: tokens.radiusCard, ), ), ], ); }, ), ); } }