245 lines
6.1 KiB
Dart
245 lines
6.1 KiB
Dart
/// 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<Color>(
|
|
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<Color>(
|
|
Theme.of(context).colorScheme.primary,
|
|
),
|
|
),
|
|
),
|
|
if (message != null) ...[
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
message!,
|
|
style: Theme.of(context).textTheme.bodySmall,
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|