/// Widgets de chargement réutilisables pour toute l'application library loading_widget; import 'package:flutter/material.dart'; import 'package:shimmer/shimmer.dart'; /// Widget de chargement simple avec CircularProgressIndicator class AppLoadingWidget extends StatelessWidget { final String? message; final double? size; const AppLoadingWidget({ super.key, this.message, this.size, }); @override Widget build(BuildContext context) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ SizedBox( width: size ?? 40, height: size ?? 40, child: CircularProgressIndicator( strokeWidth: 3, valueColor: AlwaysStoppedAnimation( Theme.of(context).colorScheme.primary, ), ), ), if (message != null) ...[ const SizedBox(height: 16), Text( message!, style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), ], ], ), ); } } /// Widget de chargement avec effet shimmer pour les listes class ShimmerListLoading extends StatelessWidget { final int itemCount; final double itemHeight; const ShimmerListLoading({ super.key, this.itemCount = 5, this.itemHeight = 80, }); @override Widget build(BuildContext context) { return ListView.builder( itemCount: itemCount, padding: const EdgeInsets.all(16), itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.only(bottom: 12), child: Shimmer.fromColors( baseColor: Colors.grey[300]!, highlightColor: Colors.grey[100]!, child: Container( height: itemHeight, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), ), ), ), ); }, ); } } /// Widget de chargement avec effet shimmer pour les cartes class ShimmerCardLoading extends StatelessWidget { final double height; final double? width; const ShimmerCardLoading({ super.key, this.height = 120, this.width, }); @override Widget build(BuildContext context) { return Shimmer.fromColors( baseColor: Colors.grey[300]!, highlightColor: Colors.grey[100]!, child: Container( height: height, width: width, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), ), ); } } /// Widget de chargement avec effet shimmer pour une grille class ShimmerGridLoading extends StatelessWidget { final int itemCount; final int crossAxisCount; final double childAspectRatio; const ShimmerGridLoading({ super.key, this.itemCount = 6, this.crossAxisCount = 2, this.childAspectRatio = 1.0, }); @override Widget build(BuildContext context) { return GridView.builder( padding: const EdgeInsets.all(16), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: crossAxisCount, crossAxisSpacing: 12, mainAxisSpacing: 12, childAspectRatio: childAspectRatio, ), itemCount: itemCount, itemBuilder: (context, index) { return Shimmer.fromColors( baseColor: Colors.grey[300]!, highlightColor: Colors.grey[100]!, child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), ), ); }, ); } } /// Widget de chargement pour les détails d'un élément class ShimmerDetailLoading extends StatelessWidget { const ShimmerDetailLoading({super.key}); @override Widget build(BuildContext context) { return Shimmer.fromColors( baseColor: Colors.grey[300]!, highlightColor: Colors.grey[100]!, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Container( height: 200, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), ), const SizedBox(height: 16), // Title Container( height: 24, width: double.infinity, color: Colors.white, ), const SizedBox(height: 8), // Subtitle Container( height: 16, width: 200, color: Colors.white, ), const SizedBox(height: 24), // Content lines ...List.generate(5, (index) { return Padding( padding: const EdgeInsets.only(bottom: 8), child: Container( height: 12, width: double.infinity, color: Colors.white, ), ); }), ], ), ), ); } } /// Widget de chargement inline (petit) class InlineLoadingWidget extends StatelessWidget { final String? message; const InlineLoadingWidget({ super.key, this.message, }); @override Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.min, children: [ SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( Theme.of(context).colorScheme.primary, ), ), ), if (message != null) ...[ const SizedBox(width: 8), Text( message!, style: Theme.of(context).textTheme.bodySmall, ), ], ], ); } }