responsive_wrapper 2.0.0 copy "responsive_wrapper: ^2.0.0" to clipboard
responsive_wrapper: ^2.0.0 copied to clipboard

A Flutter package for building responsive UIs that adapt to different screen sizes, device types, and orientations with automatic detection.

Responsive Wrapper #

A comprehensive Flutter package for building responsive UIs that adapt to different screen sizes, device types, and orientations. This package provides a clean and flexible API for creating responsive layouts with automatic device detection and orientation handling.

Features #

  • Device Type Detection: Automatically detects phone, tablet, and desktop devices
  • Orientation Support: Handle portrait and landscape orientations with different layouts
  • InheritedWidget Propagation: ScreenInfo flows down the tree — access it anywhere via context.screenInfo
  • Custom Breakpoints: Configure your own breakpoints or use built-in presets (Material, Bootstrap)
  • Parameterized Widgets: Pass data and state to your responsive layouts
  • Pre-builders: Wrap responsive content with state management, themes, and more
  • Responsive Values: Define different values for different device types and orientations
  • ResponsiveVisibility: Show or hide widgets based on device type without conditionals
  • Comprehensive Tests: Full test suite for reliable production use

Getting Started #

Add this to your pubspec.yaml:

dependencies:
  responsive_wrapper: ^2.0.0

Then run:

flutter pub get

Migration Guide (1.x → 2.0) #

Breaking Changes #

1. ResponsiveLayout defaults changed

treatLandscapePhoneAsTablet and treatPortraitTabletAsPhone now default to false (consistent with all other widgets). If you relied on the old behavior, add the flags explicitly:

// Before (implicit true)
ResponsiveLayout(phone: ..., tablet: ...)

// After — add flags if you need the old behavior
ResponsiveLayout(
  treatLandscapePhoneAsTablet: true,
  treatPortraitTabletAsPhone: true,
  phone: ...,
  tablet: ...,
)

2. ResponsiveOrientationLayoutBuilder typedef removed

Replace with ResponsiveLayoutBuilder — they were identical types.


Usage #

Basic Responsive Wrapper #

import 'package:responsive_wrapper/responsive_wrapper.dart';

ResponsiveWrapper(
  builder: (context, screenInfo) {
    return Container(
      padding: EdgeInsets.all(
        screenInfo.isPhone ? 16.0 : 24.0,
      ),
      child: Text(
        'Device: ${screenInfo.deviceType.name}',
        style: TextStyle(
          fontSize: screenInfo.isPhone ? 16.0 : 20.0,
        ),
      ),
    );
  },
)

Inside or outside a ResponsiveWrapper, use the convenience extensions directly on BuildContext:

// Checks
if (context.isPhone) { ... }
if (context.isTablet) { ... }
if (context.isDesktop) { ... }
if (context.isPortrait) { ... }
if (context.isLandscape) { ... }

// Full ScreenInfo
final info = context.screenInfo;
print(info.width);       // e.g. 390.0
print(info.shortestSide); // e.g. 390.0
print(info.aspectRatio);  // e.g. 0.46

// Device type
final type = context.deviceType; // DeviceType.phone

When inside a ResponsiveWrapper, these use the wrapper's already-resolved ScreenInfo for consistency and efficiency.

Responsive Layout #

Define different layouts for different device types:

ResponsiveLayout(
  phone: (context) => PhoneLayout(),
  tablet: (context) => TabletLayout(),
  desktop: (context) => DesktopLayout(),
)

Orientation-Aware Layouts #

Handle different orientations with specific layouts:

ResponsiveOrientationLayout(
  phonePortrait: (context) => PhonePortraitLayout(),
  phoneLandscape: (context) => PhoneLandscapeLayout(),
  tabletPortrait: (context) => TabletPortraitLayout(),
  tabletLandscape: (context) => TabletLandscapeLayout(),
  desktop: (context) => DesktopLayout(),
)

Parameterized Widgets #

Pass data to your responsive layouts:

ResponsiveWrapperWith<UserData>(
  initialParam: userData,
  builder: (context, screenInfo, userData) {
    return UserProfile(user: userData);
  },
)

ResponsiveVisibility #

Show or hide widgets based on device type without writing if statements:

// Only visible on tablet and desktop (hidden on phone)
ResponsiveVisibility(
  visibleOnPhone: false,
  child: SideNavigationPanel(),
)

// Only visible on phone
ResponsiveVisibility(
  visibleOnTablet: false,
  visibleOnDesktop: false,
  child: MobileBottomNav(),
)

// Custom widget shown when hidden
ResponsiveVisibility(
  visibleOnDesktop: false,
  replacement: SizedBox(height: 8),
  child: DesktopSpacer(),
)

Responsive Values #

Define different values for different device types and orientations.

// Simple device-only values
final padding = context.getResponsiveValueSimple<double>(
  phone: 16.0,
  tablet: 24.0,
  desktop: 32.0,
);

// With orientation variants
final fontSize = context.getResponsiveFontSize(
  phonePortrait: 16.0,
  phoneLandscape: 14.0,
  tabletPortrait: 20.0,
  tabletLandscape: 18.0,
  desktop: 24.0,
);

// Generic value with orientation
final padding = context.getResponsiveValue<EdgeInsets>(
  phonePortrait: EdgeInsets.all(16.0),
  phoneLandscape: EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
  tabletPortrait: EdgeInsets.all(24.0),
  desktop: EdgeInsets.all(32.0),
);

Using ResponsiveValue

// Resolve directly from context
final spacing = ResponsiveValue<double>(
  phone: 16.0,
  tablet: 24.0,
  desktop: 32.0,
).resolve(context);

// Or resolve by device type
final spacing = ResponsiveValue<double>(
  phone: 16.0,
  tablet: 24.0,
).getValue(DeviceType.tablet); // 24.0

Custom Breakpoints #

Configure your own breakpoints or use a named preset:

// Custom breakpoints
ResponsiveWrapper(
  breakpoints: const ResponsiveBreakpoints(
    phone: 480,
    tablet: 800,
  ),
  builder: (context, screenInfo) { ... },
)

// Built-in presets
// Material Design 3: phone < 600, tablet 600–1240, desktop >= 1240
ResponsiveWrapper(
  breakpoints: ResponsiveBreakpoints.material,
  builder: (context, screenInfo) { ... },
)

// Bootstrap-inspired: phone < 576, tablet 576–992, desktop >= 992
ResponsiveWrapper(
  breakpoints: ResponsiveBreakpoints.bootstrap,
  builder: (context, screenInfo) { ... },
)

// copyWith for small adjustments
ResponsiveWrapper(
  breakpoints: ResponsiveBreakpoints.material.copyWith(tablet: 1024),
  builder: (context, screenInfo) { ... },
)

InheritedWidget Propagation #

ResponsiveWrapper automatically sets up a ResponsiveData InheritedWidget. Any descendant can read the resolved ScreenInfo without recalculating it:

// Access anywhere in the ResponsiveWrapper subtree
final screenInfo = ResponsiveData.of(context);       // throws if not found
final screenInfo = ResponsiveData.maybeOf(context);  // returns null if not found

// Or via extension methods (preferred)
if (context.isPhone) { ... }

This is particularly useful for deeply nested widgets that need responsive behavior:

ResponsiveWrapper(
  builder: (context, screenInfo) {
    return Column(
      children: [
        Header(),        // can call context.isPhone inside
        Content(),       // same
        DeepWidget(),    // same — no props drilling needed
      ],
    );
  },
)

Pre-builders #

Pre-builders wrap responsive content with additional functionality like state management, themes, or other wrapper widgets.

Basic Pre-builder

ResponsiveWrapper(
  preBuilder: (context, child) => Container(
    decoration: BoxDecoration(
      gradient: LinearGradient(
        colors: [Colors.blue.shade100, Colors.purple.shade100],
      ),
    ),
    child: child,
  ),
  builder: (context, screenInfo) {
    return Text('Content with gradient background');
  },
)

State Management with Pre-builder

ResponsiveWrapper(
  preBuilder: (context, child) => BlocBuilder<AppCubit, AppState>(
    builder: (context, state) {
      return state.isLoading
        ? CircularProgressIndicator()
        : child;
    },
  ),
  builder: (context, screenInfo) {
    return Text('Content that depends on app state');
  },
)

Parameterized Pre-builder

ResponsiveLayoutWith<String>(
  preBuilder: (context, childBuilder) {
    final userName = UserService.getCurrentUser()?.name ?? 'Guest';
    return Container(
      decoration: BoxDecoration(border: Border.all(color: Colors.blue)),
      child: childBuilder(userName),
    );
  },
  phone: (context, userName) => Text('Hello $userName on phone!'),
  tablet: (context, userName) => Text('Hello $userName on tablet!'),
  desktop: (context, userName) => Text('Hello $userName on desktop!'),
)

Common Pre-builder Patterns

Loading States:

ResponsiveWrapper(
  preBuilder: (context, child) => FutureBuilder<Data>(
    future: dataService.fetchData(),
    builder: (context, snapshot) {
      if (!snapshot.hasData) return const CircularProgressIndicator();
      return child;
    },
  ),
  builder: (context, screenInfo) => DataWidget(),
)

Authentication Wrappers:

ResponsiveWrapper(
  preBuilder: (context, child) => Consumer<AuthProvider>(
    builder: (context, auth, _) {
      return auth.isAuthenticated ? child : LoginScreen();
    },
  ),
  builder: (context, screenInfo) => AuthenticatedContent(),
)

Understanding Pre-builders #

Pre-builders run before the main responsive builder and provide a way to inject state, context, or styling.

Aspect Pre-builder Regular Builder
Purpose Wrap/enhance responsive content Build responsive content
Execution Runs first Runs after pre-builder
Access to Context only Context + ScreenInfo
Use Case State, theming, loading Device-specific layouts

API Reference #

Core Widgets #

Class Description
ResponsiveWrapper Core widget; sets up ResponsiveData + provides ScreenInfo via builder
ResponsiveWrapperWith<T> Parameterized version
ResponsiveLayout Device-specific layout selection
ResponsiveLayoutWith<T> Parameterized version
ResponsiveOrientationLayout Device + orientation layout selection
ResponsiveOrientationLayoutWith<T> Parameterized version
ResponsiveVisibility Show/hide child by device type

Utility Classes #

Class Description
ResponsiveData InheritedWidget propagating ScreenInfo down the tree
ResponsiveBreakpoints Breakpoint configuration with .material and .bootstrap presets
ScreenInfo Immutable screen info: deviceType, width, height, shortestSide, aspectRatio, etc.
ResponsiveValue<T> Device-specific value with fallback; has .resolve(context)
ResponsiveOrientationValue<T> Orientation-aware value with fallback
DeviceType phone, tablet, desktop

BuildContext Extensions #

Extension Description
context.screenInfo Current ScreenInfo (from tree or MediaQuery)
context.deviceType Current DeviceType
context.isPhone true if phone
context.isTablet true if tablet
context.isDesktop true if desktop
context.isPortrait true if portrait orientation
context.isLandscape true if landscape orientation
context.getResponsiveValue<T>(...) Generic responsive value with orientation
context.getResponsiveValueSimple<T>(...) Device-only responsive value
context.getResponsivePadding(...) EdgeInsets responsive value
context.getResponsiveFontSize(...) double font size responsive value

Builder Types #

Typedef Signature
ResponsiveBuilder Widget Function(BuildContext, ScreenInfo)
ResponsivePreBuilder Widget Function(BuildContext, Widget child)
ResponsiveLayoutBuilder Widget Function(BuildContext)
ResponsiveBuilderWith<T> Widget Function(BuildContext, ScreenInfo, T)
ResponsivePreBuilderWith<T> Widget Function(BuildContext, Widget Function(T) childBuilder)

Real-World Examples #

Complete App with Pre-builders #

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ResponsiveLayoutWith<UserData>(
        preBuilder: (context, childBuilder) {
          return BlocBuilder<AuthCubit, AuthState>(
            builder: (context, authState) {
              if (authState.isLoading) return LoadingScreen();
              if (!authState.isAuthenticated) return LoginScreen();
              return FutureBuilder<UserData>(
                future: UserService.getUserData(authState.userId),
                builder: (context, snapshot) {
                  if (!snapshot.hasData) return LoadingScreen();
                  if (snapshot.hasError) return ErrorScreen(error: snapshot.error);
                  return childBuilder(snapshot.data!);
                },
              );
            },
          );
        },
        phone: (context, userData) => PhoneDashboard(userData: userData),
        tablet: (context, userData) => TabletDashboard(userData: userData),
        desktop: (context, userData) => DesktopDashboard(userData: userData),
      ),
    );
  }
}

Adaptive Navigation #

Scaffold(
  bottomNavigationBar: ResponsiveVisibility(
    visibleOnTablet: false,
    visibleOnDesktop: false,
    child: BottomNavBar(),
  ),
  body: Row(
    children: [
      ResponsiveVisibility(
        visibleOnPhone: false,
        child: SideNav(),
      ),
      Expanded(child: MainContent()),
    ],
  ),
)

E-commerce Product Page #

class ProductPage extends StatelessWidget {
  final String productId;

  @override
  Widget build(BuildContext context) {
    return ResponsiveWrapper(
      preBuilder: (context, child) => BlocBuilder<ProductCubit, ProductState>(
        builder: (context, state) {
          if (state.isLoading) return const Center(child: CircularProgressIndicator());
          if (state.hasError) return ErrorWidget(state.error);
          return child;
        },
      ),
      builder: (context, screenInfo) {
        return screenInfo.isPhone
          ? PhoneProductLayout(productId: productId)
          : screenInfo.isTablet
          ? TabletProductLayout(productId: productId)
          : DesktopProductLayout(productId: productId);
      },
    );
  }
}

Contributing #

Contributions are welcome! Please feel free to submit a Pull Request.

License #

This project is licensed under the MIT License - see the LICENSE file for details.

5
likes
160
points
107
downloads

Publisher

verified publishersaktepe.com

Weekly Downloads

A Flutter package for building responsive UIs that adapt to different screen sizes, device types, and orientations with automatic detection.

Homepage
Repository (GitHub)
View/report issues

Topics

#responsive #layout #adaptive #orientation #breakpoints

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on responsive_wrapper