Files
unionflow-mobile-apps/lib/shared/widgets/loading_widget.dart
dahoud 7cd7c6fc9e feat(shared): legacy presentation/ + shared design system + widgets
- lib/presentation : pages legacy (explore/network, notifications) avec BLoC
- lib/shared/design_system : UnionFlow Design System v2 (tokens, components)
  + MD3 tokens + module_colors par feature
- lib/shared/widgets : widgets transversaux (core_card, core_shimmer,
  error_widget, loading_widget, powered_by_lions_dev, etc.)
- lib/shared/constants + utils
2026-04-15 20:27:23 +00:00

245 lines
6.5 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: 8),
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(10),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Shimmer.fromColors(
baseColor: Theme.of(context).colorScheme.surfaceContainerHighest,
highlightColor: Theme.of(context).colorScheme.surface,
child: Container(
height: itemHeight,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
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: Theme.of(context).colorScheme.surfaceContainerHighest,
highlightColor: Theme.of(context).colorScheme.surface,
child: Container(
height: height,
width: width,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
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(10),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: childAspectRatio,
),
itemCount: itemCount,
itemBuilder: (context, index) {
return Shimmer.fromColors(
baseColor: Theme.of(context).colorScheme.surfaceContainerHighest,
highlightColor: Theme.of(context).colorScheme.surface,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
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: Theme.of(context).colorScheme.surfaceContainerHighest,
highlightColor: Theme.of(context).colorScheme.surface,
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header
Container(
height: 200,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(12),
),
),
const SizedBox(height: 8),
// Title
Container(
height: 24,
width: double.infinity,
color: Theme.of(context).colorScheme.surface,
),
const SizedBox(height: 8),
// Subtitle
Container(
height: 16,
width: 200,
color: Theme.of(context).colorScheme.surface,
),
const SizedBox(height: 12),
// Content lines
...List.generate(5, (index) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Container(
height: 12,
width: double.infinity,
color: Theme.of(context).colorScheme.surface,
),
);
}),
],
),
),
);
}
}
/// 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,
),
],
],
);
}
}