Implements complete refactor of Ionic Angular logistics app to Flutter/Dart with: - Svrnty dark mode console theme (Material Design 3) - Responsive layouts (mobile, tablet, desktop) following FRONTEND standards - CQRS API integration with Result<T> error handling - OAuth2/OIDC authentication support (mocked for initial testing) - Delivery route and delivery management features - Multi-language support (EN/FR) with i18n - Native integrations (camera, phone calls, maps) - Strict typing throughout codebase - Mock data for UI testing without backend Follows all FRONTEND style guides, design patterns, and conventions. App is running in dark mode and fully responsive across all device sizes. Co-Authored-By: Claude <noreply@anthropic.com>
169 lines
4.1 KiB
Dart
169 lines
4.1 KiB
Dart
library;
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
enum DeviceType {
|
|
mobile,
|
|
tablet,
|
|
desktop,
|
|
}
|
|
|
|
class Breakpoints {
|
|
Breakpoints._();
|
|
|
|
static const double mobile = 600;
|
|
static const double tablet = 1024;
|
|
static const double desktop = 1024;
|
|
|
|
static const double mobileSmall = 360;
|
|
static const double mobileLarge = 480;
|
|
static const double tabletSmall = 600;
|
|
static const double tabletLarge = 840;
|
|
static const double desktopSmall = 1024;
|
|
static const double desktopMedium = 1440;
|
|
static const double desktopLarge = 1920;
|
|
static const double desktopUltra = 2560;
|
|
|
|
static DeviceType getDeviceType(BuildContext context) {
|
|
final double width = MediaQuery.of(context).size.width;
|
|
return getDeviceTypeFromWidth(width);
|
|
}
|
|
|
|
static DeviceType getDeviceTypeFromWidth(double width) {
|
|
if (width < mobile) {
|
|
return DeviceType.mobile;
|
|
} else if (width < desktop) {
|
|
return DeviceType.tablet;
|
|
} else {
|
|
return DeviceType.desktop;
|
|
}
|
|
}
|
|
|
|
static bool isMobile(BuildContext context) {
|
|
return getDeviceType(context) == DeviceType.mobile;
|
|
}
|
|
|
|
static bool isTablet(BuildContext context) {
|
|
return getDeviceType(context) == DeviceType.tablet;
|
|
}
|
|
|
|
static bool isDesktop(BuildContext context) {
|
|
return getDeviceType(context) == DeviceType.desktop;
|
|
}
|
|
|
|
static bool isTabletOrLarger(BuildContext context) {
|
|
final DeviceType type = getDeviceType(context);
|
|
return type == DeviceType.tablet || type == DeviceType.desktop;
|
|
}
|
|
|
|
static bool isMobileOrTablet(BuildContext context) {
|
|
final DeviceType type = getDeviceType(context);
|
|
return type == DeviceType.mobile || type == DeviceType.tablet;
|
|
}
|
|
|
|
static T adaptive<T>({
|
|
required BuildContext context,
|
|
required T mobile,
|
|
T? tablet,
|
|
T? desktop,
|
|
}) {
|
|
final DeviceType deviceType = getDeviceType(context);
|
|
|
|
switch (deviceType) {
|
|
case DeviceType.mobile:
|
|
return mobile;
|
|
case DeviceType.tablet:
|
|
return tablet ?? mobile;
|
|
case DeviceType.desktop:
|
|
return desktop ?? tablet ?? mobile;
|
|
}
|
|
}
|
|
|
|
static int getGridColumns(BuildContext context) {
|
|
final double width = MediaQuery.of(context).size.width;
|
|
|
|
if (width < mobileSmall) {
|
|
return 1;
|
|
} else if (width < mobileLarge) {
|
|
return 2;
|
|
} else if (width < tabletSmall) {
|
|
return 3;
|
|
} else if (width < tabletLarge) {
|
|
return 4;
|
|
} else if (width < desktopSmall) {
|
|
return 6;
|
|
} else if (width < desktopMedium) {
|
|
return 8;
|
|
} else {
|
|
return 12;
|
|
}
|
|
}
|
|
|
|
static EdgeInsets getAdaptivePadding(BuildContext context) {
|
|
return adaptive<EdgeInsets>(
|
|
context: context,
|
|
mobile: const EdgeInsets.all(16),
|
|
tablet: const EdgeInsets.all(24),
|
|
desktop: const EdgeInsets.all(32),
|
|
);
|
|
}
|
|
|
|
static EdgeInsets getHorizontalPadding(BuildContext context) {
|
|
return adaptive<EdgeInsets>(
|
|
context: context,
|
|
mobile: const EdgeInsets.symmetric(horizontal: 16),
|
|
tablet: const EdgeInsets.symmetric(horizontal: 32),
|
|
desktop: const EdgeInsets.symmetric(horizontal: 48),
|
|
);
|
|
}
|
|
|
|
static double getSpacing(BuildContext context) {
|
|
return adaptive<double>(
|
|
context: context,
|
|
mobile: 8,
|
|
tablet: 12,
|
|
desktop: 16,
|
|
);
|
|
}
|
|
|
|
static double getFontScale(BuildContext context) {
|
|
return adaptive<double>(
|
|
context: context,
|
|
mobile: 1.0,
|
|
tablet: 1.1,
|
|
desktop: 1.0,
|
|
);
|
|
}
|
|
}
|
|
|
|
extension ResponsiveContext on BuildContext {
|
|
DeviceType get deviceType => Breakpoints.getDeviceType(this);
|
|
|
|
bool get isMobile => Breakpoints.isMobile(this);
|
|
|
|
bool get isTablet => Breakpoints.isTablet(this);
|
|
|
|
bool get isDesktop => Breakpoints.isDesktop(this);
|
|
|
|
bool get isTabletOrLarger => Breakpoints.isTabletOrLarger(this);
|
|
|
|
bool get isMobileOrTablet => Breakpoints.isMobileOrTablet(this);
|
|
|
|
double get screenWidth => MediaQuery.of(this).size.width;
|
|
|
|
double get screenHeight => MediaQuery.of(this).size.height;
|
|
|
|
T adaptive<T>({
|
|
required T mobile,
|
|
T? tablet,
|
|
T? desktop,
|
|
}) {
|
|
return Breakpoints.adaptive<T>(
|
|
context: this,
|
|
mobile: mobile,
|
|
tablet: tablet,
|
|
desktop: desktop,
|
|
);
|
|
}
|
|
}
|