ionic-planb-logistic-app-fl.../lib/utils/responsive.dart
Claude Code 4b03e9aba5 Initial commit: Plan B Logistics Flutter app with dark mode and responsive design
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>
2025-10-31 04:58:10 -04:00

244 lines
5.9 KiB
Dart

library;
import 'package:flutter/material.dart';
import 'breakpoints.dart';
class ResponsiveSize {
ResponsiveSize._();
static double widthPercent(BuildContext context, double percent) {
assert(percent >= 0 && percent <= 100, 'Percent must be between 0-100');
return MediaQuery.of(context).size.width * (percent / 100);
}
static double heightPercent(BuildContext context, double percent) {
assert(percent >= 0 && percent <= 100, 'Percent must be between 0-100');
return MediaQuery.of(context).size.height * (percent / 100);
}
static double fontSize(BuildContext context, double baseSize) {
final double scale = Breakpoints.getFontScale(context);
final double screenWidth = MediaQuery.of(context).size.width;
double widthScale = 1.0;
if (screenWidth < 360) {
widthScale = 0.9;
} else if (screenWidth > 1920) {
widthScale = 1.1;
}
return baseSize * scale * widthScale;
}
static double getMinTapSize(BuildContext context) {
final TargetPlatform platform = Theme.of(context).platform;
switch (platform) {
case TargetPlatform.iOS:
case TargetPlatform.android:
return 48.0;
case TargetPlatform.macOS:
case TargetPlatform.windows:
case TargetPlatform.linux:
return 36.0;
default:
return 44.0;
}
}
static double iconSize(BuildContext context, {double baseSize = 24}) {
return Breakpoints.adaptive<double>(
context: context,
mobile: baseSize,
tablet: baseSize * 1.2,
desktop: baseSize,
);
}
static EdgeInsets buttonPadding(BuildContext context) {
return Breakpoints.adaptive<EdgeInsets>(
context: context,
mobile: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
tablet: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
desktop: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
);
}
static double dialogWidth(BuildContext context) {
final double screenWidth = MediaQuery.of(context).size.width;
return Breakpoints.adaptive<double>(
context: context,
mobile: screenWidth * 0.9,
tablet: screenWidth * 0.7,
desktop: 500,
);
}
static double dialogMaxHeight(BuildContext context) {
final double screenHeight = MediaQuery.of(context).size.height;
return Breakpoints.adaptive<double>(
context: context,
mobile: screenHeight * 0.8,
tablet: screenHeight * 0.75,
desktop: 720,
);
}
}
class ResponsiveSpacing {
ResponsiveSpacing._();
static double xs(BuildContext context) {
return Breakpoints.adaptive<double>(
context: context,
mobile: 4,
tablet: 6,
desktop: 8,
);
}
static double sm(BuildContext context) {
return Breakpoints.adaptive<double>(
context: context,
mobile: 8,
tablet: 10,
desktop: 12,
);
}
static double md(BuildContext context) {
return Breakpoints.adaptive<double>(
context: context,
mobile: 16,
tablet: 20,
desktop: 24,
);
}
static double lg(BuildContext context) {
return Breakpoints.adaptive<double>(
context: context,
mobile: 24,
tablet: 32,
desktop: 40,
);
}
static double xl(BuildContext context) {
return Breakpoints.adaptive<double>(
context: context,
mobile: 32,
tablet: 48,
desktop: 64,
);
}
static Widget verticalGap(BuildContext context, {double? size}) {
return SizedBox(height: size ?? md(context));
}
static Widget horizontalGap(BuildContext context, {double? size}) {
return SizedBox(width: size ?? md(context));
}
}
class ResponsiveLayout {
ResponsiveLayout._();
static int formColumns(BuildContext context) {
return Breakpoints.adaptive<int>(
context: context,
mobile: 1,
tablet: 2,
desktop: 2,
);
}
static int gridCrossAxisCount(BuildContext context, {int? maxColumns}) {
final int columns = Breakpoints.getGridColumns(context);
return maxColumns != null ? columns.clamp(1, maxColumns) : columns;
}
static double gridAspectRatio(BuildContext context) {
return Breakpoints.adaptive<double>(
context: context,
mobile: 1.0,
tablet: 1.2,
desktop: 1.5,
);
}
static bool useSingleColumn(BuildContext context) {
return Breakpoints.isMobile(context);
}
static bool useDualPane(BuildContext context) {
return Breakpoints.isTabletOrLarger(context);
}
static int getFlex(BuildContext context, {
int mobileFlex = 1,
int? tabletFlex,
int? desktopFlex,
}) {
return Breakpoints.adaptive<int>(
context: context,
mobile: mobileFlex,
tablet: tabletFlex,
desktop: desktopFlex,
);
}
}
class ResponsiveVisibility {
ResponsiveVisibility._();
static bool showOnMobile(BuildContext context) {
return Breakpoints.isMobile(context);
}
static bool showOnTablet(BuildContext context) {
return Breakpoints.isTablet(context);
}
static bool showOnDesktop(BuildContext context) {
return Breakpoints.isDesktop(context);
}
static bool hideOnMobile(BuildContext context) {
return !Breakpoints.isMobile(context);
}
static bool hideOnTablet(BuildContext context) {
return !Breakpoints.isTablet(context);
}
static bool hideOnDesktop(BuildContext context) {
return !Breakpoints.isDesktop(context);
}
static bool showOnTabletUp(BuildContext context) {
return Breakpoints.isTabletOrLarger(context);
}
static bool showOnMobileTablet(BuildContext context) {
return Breakpoints.isMobileOrTablet(context);
}
}
extension ResponsiveSizeExtension on num {
double wp(BuildContext context) {
return ResponsiveSize.widthPercent(context, toDouble());
}
double hp(BuildContext context) {
return ResponsiveSize.heightPercent(context, toDouble());
}
double rfs(BuildContext context) {
return ResponsiveSize.fontSize(context, toDouble());
}
}