Files
wortis_tpe/lib/widgets/responsive_helper.dart

378 lines
12 KiB
Dart

// ===== lib/utils/responsive_helper.dart =====
// Système de responsivité centralisé pour tablettes et rotation
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:math' as math;
/// Gestionnaire principal de la responsivité et rotation
class ResponsiveHelper {
// =================== CONSTANTES DE BREAKPOINTS ===================
/// Breakpoints pour différentes tailles d'écran
static const double phoneMaxWidth = 400;
static const double tabletMinWidth = 600;
static const double desktopMinWidth = 1024;
/// Hauteurs critiques pour détection d'orientation
static const double portraitMinHeight = 600;
static const double landscapeMaxHeight = 500;
// =================== DÉTECTION DE TYPE D'APPAREIL ===================
/// Détermine si c'est un petit téléphone
static bool isSmallPhone(BuildContext context) {
return MediaQuery.of(context).size.width < phoneMaxWidth;
}
/// Détermine si c'est un téléphone standard
static bool isPhone(BuildContext context) {
return MediaQuery.of(context).size.width < tabletMinWidth;
}
/// Détermine si c'est une tablette
static bool isTablet(BuildContext context) {
final width = MediaQuery.of(context).size.width;
return width >= tabletMinWidth && width < desktopMinWidth;
}
/// Détermine si c'est un grand écran/desktop
static bool isDesktop(BuildContext context) {
return MediaQuery.of(context).size.width >= desktopMinWidth;
}
// =================== DÉTECTION D'ORIENTATION ===================
/// Détection précise de l'orientation
static bool isPortrait(BuildContext context) {
final size = MediaQuery.of(context).size;
return size.height > size.width;
}
/// Détection du mode paysage
static bool isLandscape(BuildContext context) {
final size = MediaQuery.of(context).size;
return size.width > size.height;
}
/// Détection spéciale pour tablette en mode paysage
static bool isTabletLandscape(BuildContext context) {
return isTablet(context) && isLandscape(context);
}
/// Détection spéciale pour téléphone en mode paysage
static bool isPhoneLandscape(BuildContext context) {
return isPhone(context) && isLandscape(context);
}
// =================== CONFIGURATION D'ORIENTATION ===================
/// Force l'orientation portrait et paysage pour tablettes
static void enableAllOrientations() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
}
/// Force uniquement le mode portrait (pour certains écrans si nécessaire)
static void enablePortraitOnly() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
}
/// Force uniquement le mode paysage
static void enableLandscapeOnly() {
SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
}
// =================== DIMENSIONS RESPONSIVES ===================
/// Obtient la largeur responsive selon l'appareil
static double getResponsiveWidth(BuildContext context, {
double? phone,
double? tablet,
double? desktop,
}) {
if (isDesktop(context)) return desktop ?? tablet ?? phone ?? 0;
if (isTablet(context)) return tablet ?? phone ?? 0;
return phone ?? 0;
}
/// Obtient la hauteur responsive selon l'appareil
static double getResponsiveHeight(BuildContext context, {
double? phone,
double? tablet,
double? desktop,
}) {
if (isDesktop(context)) return desktop ?? tablet ?? phone ?? 0;
if (isTablet(context)) return tablet ?? phone ?? 0;
return phone ?? 0;
}
/// Padding responsive adaptatif
static EdgeInsets getResponsivePadding(BuildContext context, {
EdgeInsets? phone,
EdgeInsets? tablet,
EdgeInsets? desktop,
}) {
if (isDesktop(context)) return desktop ?? tablet ?? phone ?? EdgeInsets.zero;
if (isTablet(context)) return tablet ?? phone ?? EdgeInsets.zero;
return phone ?? EdgeInsets.zero;
}
/// Taille de police responsive
static double getResponsiveFontSize(BuildContext context, {
double? phone,
double? tablet,
double? desktop,
}) {
if (isDesktop(context)) return desktop ?? tablet ?? phone ?? 14;
if (isTablet(context)) return tablet ?? phone ?? 14;
return phone ?? 14;
}
// =================== GRILLES RESPONSIVES ===================
/// Nombre de colonnes selon la taille d'écran
static int getGridColumns(BuildContext context, {
int phoneColumns = 2,
int tabletPortraitColumns = 3,
int tabletLandscapeColumns = 4,
int desktopColumns = 5,
}) {
if (isDesktop(context)) return desktopColumns;
if (isTabletLandscape(context)) return tabletLandscapeColumns;
if (isTablet(context)) return tabletPortraitColumns;
return phoneColumns;
}
/// Ratio d'aspect adaptatif pour les cartes
static double getCardAspectRatio(BuildContext context) {
if (isPhoneLandscape(context)) return 2.5;
if (isTabletLandscape(context)) return 1.8;
if (isTablet(context)) return 1.4;
return 1.2; // Portrait par défaut
}
// =================== WIDGETS RESPONSIVES ===================
/// Container responsive avec padding adaptatif
static Widget responsiveContainer({
required BuildContext context,
required Widget child,
EdgeInsets? phonePadding,
EdgeInsets? tabletPadding,
EdgeInsets? desktopPadding,
Color? backgroundColor,
BorderRadius? borderRadius,
}) {
return Container(
padding: getResponsivePadding(
context,
phone: phonePadding ?? const EdgeInsets.all(16),
tablet: tabletPadding ?? const EdgeInsets.all(24),
desktop: desktopPadding ?? const EdgeInsets.all(32),
),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: borderRadius ?? BorderRadius.circular(12),
),
child: child,
);
}
/// Texte responsive avec tailles adaptatives
static Widget responsiveText(
String text, {
required BuildContext context,
double? phoneSize,
double? tabletSize,
double? desktopSize,
FontWeight? fontWeight,
Color? color,
TextAlign? textAlign,
int? maxLines,
TextOverflow? overflow,
}) {
return Text(
text,
style: TextStyle(
fontSize: getResponsiveFontSize(
context,
phone: phoneSize ?? 14,
tablet: tabletSize ?? 16,
desktop: desktopSize ?? 18,
),
fontWeight: fontWeight,
color: color,
),
textAlign: textAlign,
maxLines: maxLines,
overflow: overflow,
);
}
// =================== LAYOUTS RESPONSIVES ===================
/// Layout responsive en colonnes
static Widget responsiveRow({
required BuildContext context,
required List<Widget> children,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
bool forceColumnOnPhone = true,
}) {
if (forceColumnOnPhone && isPhone(context)) {
return Column(
mainAxisAlignment: mainAxisAlignment,
crossAxisAlignment: crossAxisAlignment,
children: children,
);
}
return Row(
mainAxisAlignment: mainAxisAlignment,
crossAxisAlignment: crossAxisAlignment,
children: children,
);
}
/// GridView responsive adaptatif
static Widget responsiveGridView({
required BuildContext context,
required List<Widget> children,
int? phoneColumns,
int? tabletPortraitColumns,
int? tabletLandscapeColumns,
int? desktopColumns,
double childAspectRatio = 1.0,
double crossAxisSpacing = 8.0,
double mainAxisSpacing = 8.0,
EdgeInsets? padding,
ScrollPhysics? physics,
}) {
final columns = getGridColumns(
context,
phoneColumns: phoneColumns ?? 2,
tabletPortraitColumns: tabletPortraitColumns ?? 3,
tabletLandscapeColumns: tabletLandscapeColumns ?? 4,
desktopColumns: desktopColumns ?? 5,
);
return GridView.count(
shrinkWrap: true,
physics: physics ?? const NeverScrollableScrollPhysics(),
crossAxisCount: columns,
childAspectRatio: childAspectRatio,
crossAxisSpacing: crossAxisSpacing,
mainAxisSpacing: mainAxisSpacing,
padding: padding ?? getResponsivePadding(
context,
phone: const EdgeInsets.all(16),
tablet: const EdgeInsets.all(24),
),
children: children,
);
}
// =================== NAVIGATION RESPONSIVE ===================
/// Détermine si on doit utiliser un Drawer ou une BottomNavigationBar
static bool shouldUseDrawer(BuildContext context) {
return isTabletLandscape(context) || isDesktop(context);
}
/// Détermine si on doit utiliser une navigation rail (côté gauche)
static bool shouldUseNavigationRail(BuildContext context) {
return isTabletLandscape(context) || isDesktop(context);
}
// =================== ORIENTATIONS SPÉCIFIQUES ===================
/// Initialise les orientations selon l'appareil
static void initializeOrientations(BuildContext context) {
if (isTablet(context) || isDesktop(context)) {
enableAllOrientations();
} else {
// Pour les téléphones, on peut choisir de limiter ou non
enableAllOrientations(); // Ou enablePortraitOnly() selon les besoins
}
}
// =================== UTILITAIRES DIVERS ===================
/// Obtient la largeur maximale recommandée pour le contenu
static double getMaxContentWidth(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
if (isDesktop(context)) {
return math.min(screenWidth * 0.8, 1200); // Max 1200px sur desktop
} else if (isTablet(context)) {
return screenWidth * 0.9; // 90% sur tablette
}
return screenWidth; // 100% sur téléphone
}
/// Centre le contenu avec une largeur maximale
static Widget centerContent({
required BuildContext context,
required Widget child,
double? maxWidth,
}) {
return Center(
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: maxWidth ?? getMaxContentWidth(context),
),
child: child,
),
);
}
/// Détermine l'espacement entre les éléments
static double getSpacing(BuildContext context, {
double? small,
double? medium,
double? large,
}) {
if (isDesktop(context)) return large ?? 24;
if (isTablet(context)) return medium ?? 16;
return small ?? 12;
}
}
// =================== EXTENSION POUR MEDIAQUERY ===================
/// Extension pour simplifier l'utilisation de ResponsiveHelper
extension ResponsiveExtension on BuildContext {
bool get isSmallPhone => ResponsiveHelper.isSmallPhone(this);
bool get isPhone => ResponsiveHelper.isPhone(this);
bool get isTablet => ResponsiveHelper.isTablet(this);
bool get isDesktop => ResponsiveHelper.isDesktop(this);
bool get isPortrait => ResponsiveHelper.isPortrait(this);
bool get isLandscape => ResponsiveHelper.isLandscape(this);
bool get isTabletLandscape => ResponsiveHelper.isTabletLandscape(this);
bool get isPhoneLandscape => ResponsiveHelper.isPhoneLandscape(this);
double responsiveWidth({double? phone, double? tablet, double? desktop}) =>
ResponsiveHelper.getResponsiveWidth(this, phone: phone, tablet: tablet, desktop: desktop);
double responsiveHeight({double? phone, double? tablet, double? desktop}) =>
ResponsiveHelper.getResponsiveHeight(this, phone: phone, tablet: tablet, desktop: desktop);
double responsiveFontSize({double? phone, double? tablet, double? desktop}) =>
ResponsiveHelper.getResponsiveFontSize(this, phone: phone, tablet: tablet, desktop: desktop);
EdgeInsets responsivePadding({EdgeInsets? phone, EdgeInsets? tablet, EdgeInsets? desktop}) =>
ResponsiveHelper.getResponsivePadding(this, phone: phone, tablet: tablet, desktop: desktop);
}