Universal Breakpoints

Universal Breakpoints

A simple, reusable, and platform-independent screen size breakpoints package for Flutter. Provides responsive design utilities for Android, iOS, Web, Windows, macOS, and Linux.

✨ Features

  • Platform Agnostic: Works seamlessly on all Flutter platforms (Android, iOS, Web, Windows, macOS, Linux)
  • 8 Main Breakpoint Categories: From ultra-small phones to 4K+ displays
  • 19 Ultra-Granular Sub-Categories: Fine-grained control over specific device types
  • Automatic Scaling: Built-in scaling factors for fonts, widths, and heights
  • Responsive Extensions: Convenient BuildContext and num extensions
  • Orientation Detection: Portrait/landscape and aspect ratio helpers
  • Singleton Pattern: Single instance for consistent state across your app
  • Dynamic Grid System: Responsive grid widgets with auto-adjusting columns, spacing, and animations
  • Masonry Layouts: Pinterest-style masonry grid for varied item sizes
  • Animated Grids: Multiple animation styles for grid item entrance effects
  • Zero External Dependencies: All grid features use only Flutter's built-in widgets

🎨 Demo & Documentation

🚀 Getting Started

Installation

Add to your pubspec.yaml:

dependencies:
  universal_breakpoints: ^1.0.0

Then run:

flutter pub get

Basic Setup

Initialize in your main app widget:

import 'package:universal_breakpoints/universal_breakpoints.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    UniversalBreakpoints().init(context);
    return MaterialApp(
      title: 'My App',
      home: const MyHome(),
    );
  }
}

Using ResponsiveWrapper for Dynamic Updates

The ResponsiveWrapper widget automatically handles screen size changes and rebuilds your UI dynamically when the device is resized or rotated. This is especially useful for web applications and desktop apps where users can resize the window.

Instead of manually initializing UniversalBreakpoints in each widget, wrap your app with ResponsiveWrapper:

import 'package:universal_breakpoints/universal_breakpoints.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ResponsiveWrapper(
      config: ResponsiveWrapperConfig(
        autoInitialize: true,
        debugPrint: false,  // Set to true for resize debug logs
      ),
      child: MaterialApp(
        title: 'My App',
        home: const MyHome(),
      ),
    );
  }
}

How it works:

  • Automatically initializes UniversalBreakpoints on each rebuild
  • Listens to Flutter's MediaQuery system for screen size changes
  • Triggers widget rebuilds whenever the screen is resized or orientation changes
  • All descendant widgets automatically get responsive properties updated
  • Optional debug logging to monitor resize events

Example with debug enabled:

ResponsiveWrapper(
  config: ResponsiveWrapperConfig(
    autoInitialize: true,
    debugPrint: true,  // Logs resize events to console
  ),
  child: MyApp(),
)

💻 Usage

1. BuildContext Extensions

class MyHome extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Responsive App')),
      body: context.isMobile
          ? const MobileLayout()
          : context.isTablet
              ? const TabletLayout()
              : const DesktopLayout(),
    );
  }
}

2. Conditional Rendering

class ResponsiveWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(context.isDesktop ? 32 : 16),
      child: Column(
        children: [
          if (context.isMobile) Text('Mobile View'),
          if (context.isTablet) Text('Tablet View'),
          if (context.isDesktop) Text('Desktop View'),
        ],
      ),
    );
  }
}

3. Responsive Values

class GridLayout extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    int columns = context.responsiveValue<int>(
      mobile: 1,
      smallTablet: 2,
      largeTablet: 3,
      desktop: 4,
    );

    return GridView.count(
      crossAxisCount: columns,
      children: List.generate(12, (index) => Container()),
    );
  }
}

4. Scaling Extensions

class ScaledText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          'Large Title',
          style: TextStyle(fontSize: 32.sF),
        ),
        Text(
          'Body Text',
          style: TextStyle(fontSize: 16.sF, height: 16.sFh),
        ),
        SizedBox(
          width: 200.sW,
          height: 100.sH,
          child: Container(),
        ),
      ],
    );
  }
}

5. Orientation-Specific Layouts

class OrientationAwareWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return context.isPortrait
        ? PortraitLayout()
        : LandscapeLayout();
  }
}

📖 API Reference

Screen Size Categories

context.isXXS    // Extra extra small (< 360px)
context.isXS     // Extra small (360-479px)
context.isSM     // Small (480-767px)
context.isMD     // Medium (768-1023px)
context.isLG     // Large (1024-1279px)
context.isXL     // Extra large (1280-1439px)
context.isXXL    // Extra extra large (1440-1919px)
context.isXXXL   // Ultra large (1920+px)

Device Type Detection

context.isMobile      // < 768px
context.isTablet      // 768-1279px
context.isDesktop     // 1280+px
context.isLargeScreen // 1440+px

Ultra-Granular Sub-Categories

context.isUltraCompact       // < 320px
context.isCompactPhone       // 320-374px
context.isStandardPhone      // 375-413px
context.isLargePhone         // 414-479px
context.isPhablet            // 480-567px
context.isSmallTablet        // 568-667px
context.isStandardTablet     // 668-767px
context.isLargeTablet        // 768-833px
context.isExtraLargeTablet   // 834-1023px
context.isSmallDesktop       // 1024-1279px
context.isStandardDesktop    // 1280-1365px
context.isLargeDesktop       // 1366-1439px
context.isExtraLargeDesktop  // 1440-1535px
context.isWidescreen         // 1536-1679px
context.isFullHD             // 1680-1919px
context.isQHD                // 1920-2559px
context.isUltraWide          // 2560-3439px
context.isUltraHD            // 3440-3839px
context.isSuperUltraWide     // 3840+px

Orientation & Aspect Ratio

context.isPortrait         // Height > Width
context.isLandscape        // Width > Height
context.isUltraWideAspect  // Aspect ratio > 2.0
context.isStandardAspect   // Aspect ratio 1.3-1.8
context.isTallAspect       // Aspect ratio < 1.3

Scaling Extensions

16.sF   // Scaled font size
100.sW  // Scaled width
50.sH   // Scaled height
14.sFh  // Calculated line height

Direct Access

final config = UniversalBreakpoints();

config.screenWidth          // Current screen width
config.screenHeight         // Current screen height
config.textScaleFactor      // Font scaling factor
config.widthScaleFactor     // Width scaling factor
config.heightScaleFactor    // Height scaling factor
config.screenType           // Type as string
config.screenSizeCategory   // Main category
config.screenSizeSubCategory // Sub-category

config.scaledFontSize(16)   // Scale font size
config.scaledWidth(100)     // Scale width
config.scaledHeight(50)     // Scale height

📏 Breakpoint Values

Category Breakpoint Range Purpose
xxs 360px 0-359px Extra small phones
xs 480px 360-479px Small phones
sm 768px 480-767px Large phones
md 1024px 768-1023px Tablets
lg 1280px 1024-1279px Large tablets/desktops
xl 1440px 1280-1439px Desktop
xxl 1920px 1440-1919px Large desktop
xxxl 2560px 1920+px 4K+ displays

🎯 Advanced Examples

Complex Responsive Layout

class ComplexResponsiveLayout extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Padding(
        padding: EdgeInsets.all(context.isMobile ? 16.0 : 32.0),
        child: Column(
          children: [
            Text(
              'Welcome',
              style: TextStyle(
                fontSize: context.isMobile ? 24.sF : 32.sF,
                fontWeight: FontWeight.bold,
              ),
            ),
            SizedBox(height: 20.sH),
            GridView.count(
              crossAxisCount: context.responsiveValue<int>(
                mobile: 1,
                tablet: 2,
                desktop: 3,
              ),
              mainAxisSpacing: 16.sH,
              crossAxisSpacing: 16.sW,
              children: List.generate(9, (index) {
                return Container(
                  width: double.infinity,
                  height: context.isMobile ? 150.sH : 200.sH,
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(8),
                    color: Colors.blue[100],
                  ),
                );
              }),
            ),
          ],
        ),
      ),
    );
  }
}

Adaptive Navigation

class AdaptiveNavigation extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    if (context.isMobile) {
      return BottomNavigationBar(
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
          BottomNavigationBarItem(icon: Icon(Icons.search), label: 'Search'),
          BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
        ],
      );
    } else if (context.isTablet) {
      return Row(
        children: [
          NavigationRail(destinations: [...]),
          Expanded(child: Content()),
        ],
      );
    } else {
      return Row(
        children: [
          SideNavigation(),
          Expanded(child: Content()),
        ],
      );
    }
  }
}

🔲 Dynamic Grid System

Universal Breakpoints includes a powerful dynamic grid system with multiple responsive grid widgets. All features use only Flutter's built-in widgets—no external dependencies required.

Basic Dynamic Grid

The DynamicGrid widget automatically adjusts columns, spacing, and sizing based on screen breakpoints.

DynamicGrid(
  items: List.generate(12, (index) => index),
  itemBuilder: (context, item, index) {
    return Card(
      child: Center(child: Text('Item ${index + 1}')),
    );
  },
  columnConfig: GridColumnConfig(
    smallMobile: 1,
    mobile: 1,
    largeMobile: 2,
    tablet: 3,
    desktop: 4,
    largeDesktop: 6,
  ),
  spacingConfig: GridSpacingConfig(
    smallMobile: 8,
    mobile: 12,
    tablet: 16,
    desktop: 24,
  ),
  enableAnimations: true,
)

Features:

  • Auto-adjusting column count per breakpoint
  • Responsive item spacing
  • Smooth animations on layout changes
  • Customizable aspect ratio for items
  • Optional SliverGrid support for custom scroll behavior

Masonry Grid

Create Pinterest-style masonry layouts that adapt to different screen sizes.

MasonryDynamicGrid(
  items: List.generate(20, (index) => index),
  itemBuilder: (context, item, index) {
    return Card(child: Center(child: Text('$item')));
  },
  columnConfig: GridColumnConfig(
    mobile: 2,
    tablet: 3,
    desktop: 4,
  ),
  spacingConfig: GridSpacingConfig(defaultSpacing: 12),
)

Features:

  • Natural column-based masonry layout
  • Responsive column count
  • Flexible item heights
  • Smooth animations

Animated Grid

Display grid items with smooth entrance animations. Choose from multiple animation styles.

AnimatedDynamicGrid(
  items: List.generate(12, (index) => index),
  itemBuilder: (context, item, index) {
    return Card(child: Center(child: Text('Item $index')));
  },
  columnConfig: GridColumnConfig(
    mobile: 1,
    tablet: 2,
    desktop: 3,
  ),
  itemStyle: AnimatedGridItemStyle.fadeAndScale,
  animationDuration: const Duration(milliseconds: 500),
)

Animation Styles:

  • scaleIn: Items scale up on entrance
  • fadeIn: Items fade in smoothly
  • slideInFromLeft: Items slide from the left
  • slideInFromTop: Items slide from the top
  • fadeAndScale: Combined fade and scale effect

Grid Configuration

GridColumnConfig

Fine-tune column counts for each device size:

GridColumnConfig(
  ultraCompact: 1,
  compact: 1,
  standard: 2,
  largePhone: 2,
  phablet: 2,
  smallTablet: 3,
  largeTablet: 3,
  smallDesktop: 4,
  standardDesktop: 5,
  largeDesktop: 6,
  ultraWide: 8,
)

GridSpacingConfig

Customize spacing at different breakpoints:

GridSpacingConfig(
  ultraCompactSpacing: 4,
  compactSpacing: 8,
  standardSpacing: 12,
  largePhoneSpacing: 12,
  tabletSpacing: 16,
  desktopSpacing: 24,
  ultraWideSpacing: 40,
)

Dynamic Grid Properties

DynamicGrid(
  items: [],                              // Required: List of items
  itemBuilder: (context, item, i) {},     // Required: Widget builder
  columnConfig: GridColumnConfig(...),    // Optional: Custom columns
  spacingConfig: GridSpacingConfig(...),  // Optional: Custom spacing
  itemAspectRatio: 1.0,                  // Default: square items
  enableAnimations: true,                 // Default: true
  animationDuration: Duration(...),       // Default: 300ms
  animationCurve: Curves.easeInOut,      // Default curve
  useSliverGrid: false,                  // Use SliverGrid instead
  scrollPhysics: BouncingScrollPhysics(), // Scroll behavior
  padding: EdgeInsets.all(16),           // Grid padding
  mainAxisSpacing: 12,                   // Override spacing
  crossAxisSpacing: 12,                  // Override spacing
)

Real-World Example

class ProductGrid extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DynamicGrid(
      items: products,
      itemBuilder: (context, product, index) {
        return ProductCard(product: product);
      },
      columnConfig: GridColumnConfig(
        mobile: 1,
        largeMobile: 2,
        tablet: 3,
        desktop: 4,
        largeDesktop: 6,
      ),
      spacingConfig: GridSpacingConfig(
        mobile: 8,
        tablet: 12,
        desktop: 16,
        defaultSpacing: 12,
      ),
      itemAspectRatio: 0.75,
      padding: EdgeInsets.all(16.0),
      enableAnimations: true,
    );
  }
}

↩️ Backwards Compatibility

For code using the old SizeConfig name:

typedef SizeConfig = UniversalBreakpoints;

SizeConfig().init(context);       // Still works
UniversalBreakpoints().init(context); // New name

✅ Platform Support

  • Android
  • iOS
  • Web (Chrome, Firefox, Safari, Edge)
  • Windows
  • macOS
  • Linux

📦 Package Info

  • Minimal Dependencies: Only depends on Flutter
  • Zero Configuration: Works out of the box
  • Production Ready: Used in multiple production applications
  • Fully Documented: Complete API documentation

🤝 Contributing

Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.

📄 License

MIT License - See LICENSE file for details.


For more information, visit pub.flutter-io.cn