import 'package:flutter/material.dart'; class ResponsiveLayout extends StatelessWidget { final Widget mobile; final Widget? tablet; final Widget? desktop; final Widget? largeDesktop; const ResponsiveLayout({ super.key, required this.mobile, this.tablet, this.desktop, this.largeDesktop, }); static bool isMobile(BuildContext context) { return MediaQuery.of(context).size.width < 768; } static bool isTablet(BuildContext context) { final width = MediaQuery.of(context).size.width; return width >= 768 && width < 1024; } static bool isDesktop(BuildContext context) { final width = MediaQuery.of(context).size.width; return width >= 1024 && width < 1440; } static bool isLargeDesktop(BuildContext context) { return MediaQuery.of(context).size.width >= 1440; } static bool isPortrait(BuildContext context) { return MediaQuery.of(context).orientation == Orientation.portrait; } static bool isLandscape(BuildContext context) { return MediaQuery.of(context).orientation == Orientation.landscape; } static double screenWidth(BuildContext context) { return MediaQuery.of(context).size.width; } static double screenHeight(BuildContext context) { return MediaQuery.of(context).size.height; } static EdgeInsets safePadding(BuildContext context) { return MediaQuery.of(context).padding; } static double safeAreaHeight(BuildContext context) { final screenHeight = MediaQuery.of(context).size.height; final paddingTop = MediaQuery.of(context).padding.top; final paddingBottom = MediaQuery.of(context).padding.bottom; return screenHeight - paddingTop - paddingBottom; } @override Widget build(BuildContext context) { final width = MediaQuery.of(context).size.width; if (width >= 1440 && largeDesktop != null) { return largeDesktop!; } else if (width >= 1024 && desktop != null) { return desktop!; } else if (width >= 768 && tablet != null) { return tablet!; } else { return mobile; } } } class ResponsiveContainer extends StatelessWidget { final Widget child; final EdgeInsetsGeometry? padding; final double? maxWidth; final bool centerContent; const ResponsiveContainer({ super.key, required this.child, this.padding, this.maxWidth, this.centerContent = true, }); @override Widget build(BuildContext context) { final screenWidth = ResponsiveLayout.screenWidth(context); final effectiveMaxWidth = maxWidth ?? (screenWidth < 768 ? screenWidth : 1200.0); Widget container = Container( width: double.infinity, constraints: BoxConstraints( maxWidth: effectiveMaxWidth, ), padding: padding ?? const EdgeInsets.all(16), child: child, ); if (centerContent && screenWidth > effectiveMaxWidth) { container = Center(child: container); } return container; } } class ResponsiveGrid extends StatelessWidget { final List children; final int mobileColumns; final int tabletColumns; final int desktopColumns; final int largeDesktopColumns; final double spacing; final double runSpacing; final EdgeInsets? padding; const ResponsiveGrid({ super.key, required this.children, this.mobileColumns = 1, this.tabletColumns = 2, this.desktopColumns = 3, this.largeDesktopColumns = 4, this.spacing = 16.0, this.runSpacing = 16.0, this.padding, }); @override Widget build(BuildContext context) { int columns; if (ResponsiveLayout.isLargeDesktop(context)) { columns = largeDesktopColumns; } else if (ResponsiveLayout.isDesktop(context)) { columns = desktopColumns; } else if (ResponsiveLayout.isTablet(context)) { columns = tabletColumns; } else { columns = mobileColumns; } return LayoutBuilder( builder: (context, constraints) { final childAspectRatio = (constraints.maxWidth - (spacing * (columns - 1))) / columns / 200; return Padding( padding: padding ?? EdgeInsets.zero, child: GridView.count( crossAxisCount: columns, crossAxisSpacing: spacing, mainAxisSpacing: runSpacing, childAspectRatio: childAspectRatio, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), children: children, ), ); }, ); } } class ResponsiveCard extends StatelessWidget { final Widget child; final EdgeInsetsGeometry? padding; final double? borderRadius; final Color? backgroundColor; final VoidCallback? onTap; const ResponsiveCard({ super.key, required this.child, this.padding, this.borderRadius, this.backgroundColor, this.onTap, }); @override Widget build(BuildContext context) { final effectiveBorderRadius = borderRadius ?? (ResponsiveLayout.isMobile(context) ? 8.0 : 12.0); final effectivePadding = padding ?? EdgeInsets.all(ResponsiveLayout.isMobile(context) ? 12.0 : 16.0); return Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(effectiveBorderRadius), ), color: backgroundColor, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(effectiveBorderRadius), child: Padding( padding: effectivePadding, child: child, ), ), ); } } class ResponsiveLayoutBuilder extends StatelessWidget { final Widget Function(BuildContext context, ScreenSize screenSize) builder; const ResponsiveLayoutBuilder({ super.key, required this.builder, }); @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { ScreenSize screenSize; if (constraints.maxWidth >= 1440) { screenSize = ScreenSize.largeDesktop; } else if (constraints.maxWidth >= 1024) { screenSize = ScreenSize.desktop; } else if (constraints.maxWidth >= 768) { screenSize = ScreenSize.tablet; } else { screenSize = ScreenSize.mobile; } return builder(context, screenSize); }, ); } } enum ScreenSize { mobile, tablet, desktop, largeDesktop, } extension ScreenSizeExtension on ScreenSize { bool get isMobile => this == ScreenSize.mobile; bool get isTablet => this == ScreenSize.tablet; bool get isDesktop => this == ScreenSize.desktop; bool get isLargeDesktop => this == ScreenSize.largeDesktop; bool get isDesktopOrLarge => isDesktop || isLargeDesktop; bool get isTabletOrLarger => isTablet || isDesktopOrLarge; }