Popular Searches
Popular Course Categories
Popular Courses

Designing interfaces for multiple screen sizes

Designing interfaces for multiple screen sizes

Flutter Responsive Design

Designing Interfaces for Multiple Screen Sizes in Flutter

Modern Flutter applications should provide a comfortable and usable interface across phones, tablets, desktops, laptops, foldables, and resizable windows. Instead of creating completely separate applications for every screen, Flutter allows developers to build responsive and adaptive interfaces from a shared codebase.

Flutter describes responsive design as fitting the UI into the available space, while adaptive design focuses on choosing a UI that is usable and appropriate for that space. In practice, a good Flutter application generally combines both approaches.


1. What Does Designing for Multiple Screen Sizes Mean?

Designing for multiple screen sizes means creating an interface that can automatically adjust its layout, spacing, navigation, typography, controls, and content based on the available window size.

For example:

  • A phone may display one column of content.
  • A tablet may display two columns.
  • A desktop may display a sidebar and a large content area.
  • A very wide desktop window may display three or more content sections.

The important point is that layout decisions should generally be based on the space available to the application rather than simply checking whether the device is a phone, tablet, or desktop. Flutter's adaptive design guidance recommends measuring the available window using APIs such as MediaQuery.sizeOf and LayoutBuilder.


2. Why Multiple Screen Sizes Matter

A Flutter application can run in many environments, including mobile devices, tablets, foldables, desktop computers, web browsers, and resizable windows.

A fixed layout that looks good on one screen may produce:

  • Horizontal overflow
  • Text wrapping problems
  • Very small buttons
  • Large unused spaces
  • Uncomfortable navigation
  • Overly wide text fields
  • Distorted images
  • Unusable desktop layouts

Responsive and adaptive design helps prevent these problems.


3. Responsive Design vs Adaptive Design

Responsive Design Adaptive Design
Fits the interface into the available space. Chooses an appropriate interface for the available space.
Adjusts sizes, spacing and positioning. May change navigation or information architecture.
Often uses flexible widgets. Often uses different widgets or layouts at breakpoints.
Example: cards changing width. Example: NavigationBar changing to NavigationRail.

Flutter's official guidance explains that responsive design is about fitting UI into space, while adaptive design is about making the UI usable in that space.


4. The Three-Step Approach

A useful approach for designing multi-screen Flutter interfaces is:

  1. Abstract: Separate reusable data and UI components.
  2. Measure: Determine the available width and height.
  3. Branch: Choose an appropriate layout based on the available space.

Example

LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth < 600) {
      return const MobileLayout();
    } else {
      return const LargeScreenLayout();
    }
  },
)

This approach is preferable to writing separate applications for every device category.


5. Understanding Logical Pixels

Flutter layouts use logical pixels rather than directly using the physical pixel dimensions of a device.

Logical pixels make it easier to create layouts that maintain a relatively consistent visual size across different displays.

final width = MediaQuery.sizeOf(context).width;
final height = MediaQuery.sizeOf(context).height;

For responsive layout decisions, logical window dimensions are generally more useful than physical device specifications.


6. Using MediaQuery for Screen Size

MediaQuery.sizeOf(context) provides the current size of the application's window.

class ScreenInfo extends StatelessWidget {
  const ScreenInfo({super.key});

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.sizeOf(context);

    return Scaffold(
      body: Center(
        child: Text(
          'Width: ${size.width}\nHeight: ${size.height}',
          textAlign: TextAlign.center,
        ),
      ),
    );
  }
}

Useful Properties

  • MediaQuery.sizeOf(context).width - available window width.
  • MediaQuery.sizeOf(context).height - available window height.
  • MediaQuery.paddingOf(context) - system padding.
  • MediaQuery.viewInsetsOf(context) - areas covered by system UI such as the keyboard.
  • MediaQuery.textScalerOf(context) - text scaling information.

7. Using LayoutBuilder

LayoutBuilder is particularly useful when a widget needs to respond to the constraints provided by its parent.

LayoutBuilder(
  builder: (context, constraints) {
    return Text(
      'Available width: ${constraints.maxWidth}',
    );
  },
)

The builder receives a BoxConstraints object containing values such as:

  • minWidth
  • maxWidth
  • minHeight
  • maxHeight

This makes LayoutBuilder useful for reusable components that may appear inside containers of different sizes.


8. MediaQuery vs LayoutBuilder

MediaQuery LayoutBuilder
Measures the application window. Measures constraints from the parent.
Useful for app-level responsive decisions. Useful for component-level responsive decisions.
Returns a Size. Provides BoxConstraints.
Example: choose navigation style. Example: change a reusable card layout.

9. Designing Breakpoints

A breakpoint is a width at which the application changes its layout.

For example:

  • Small: less than 600 logical pixels.
  • Medium: 600 to around 1000 logical pixels.
  • Large: above around 1000 logical pixels.

These values are examples, not universal rules. Breakpoints should be selected according to the actual content and layout requirements of your application.

Widget buildResponsiveLayout(BuildContext context) {
  final width = MediaQuery.sizeOf(context).width;

  if (width < 600) {
    return const MobileLayout();
  } else if (width < 1000) {
    return const TabletLayout();
  } else {
    return const DesktopLayout();
  }
}

10. Mobile Interface Design

Mobile screens have limited horizontal space, so mobile interfaces commonly use:

  • Single-column layouts
  • Compact navigation
  • Large touch targets
  • Scrollable content
  • Bottom navigation
  • Shorter text blocks
  • Flexible widths

Mobile Example

class MobileLayout extends StatelessWidget {
  const MobileLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Mobile App'),
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: const [
          Text(
            'Welcome',
            style: TextStyle(fontSize: 24),
          ),
          SizedBox(height: 16),
          Card(
            child: ListTile(
              leading: Icon(Icons.person),
              title: Text('User Profile'),
              subtitle: Text('Manage your account'),
            ),
          ),
        ],
      ),
    );
  }
}

11. Tablet Interface Design

Tablets provide more horizontal space than phones. Instead of simply enlarging the mobile UI, developers can use the extra space for additional content.

A tablet layout may use:

  • Two-column layouts
  • NavigationRail
  • Side panels
  • Larger cards
  • Grid layouts
  • Master-detail interfaces

Tablet Example

class TabletLayout extends StatelessWidget {
  const TabletLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        SizedBox(
          width: 280,
          child: ListView(
            children: const [
              ListTile(title: Text('Dashboard')),
              ListTile(title: Text('Orders')),
              ListTile(title: Text('Customers')),
            ],
          ),
        ),
        const VerticalDivider(width: 1),
        const Expanded(
          child: Center(
            child: Text('Tablet Content Area'),
          ),
        ),
      ],
    );
  }
}

12. Desktop Interface Design

Desktop screens provide much more horizontal space. A desktop interface can take advantage of this space by displaying navigation, filters, content and additional information simultaneously.

Typical Desktop Structure

Row(
  children: [
    const SizedBox(
      width: 240,
      child: Sidebar(),
    ),
    const VerticalDivider(width: 1),
    Expanded(
      child: MainContent(),
    ),
  ],
)

Flutter's adaptive-layout tutorial demonstrates a similar large-screen pattern with a fixed-width sidebar, divider and an expanded details area.


13. Complete Mobile, Tablet and Desktop Layout

class ResponsiveHome extends StatelessWidget {
  const ResponsiveHome({super.key});

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth < 600) {
          return const MobileHome();
        }

        if (constraints.maxWidth < 1000) {
          return const TabletHome();
        }

        return const DesktopHome();
      },
    );
  }
}

class MobileHome extends StatelessWidget {
  const MobileHome({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
        child: Text('Mobile Layout'),
      ),
    );
  }
}

class TabletHome extends StatelessWidget {
  const TabletHome({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Row(
        children: [
          SizedBox(
            width: 200,
            child: Center(child: Text('Tablet Navigation')),
          ),
          Expanded(
            child: Center(child: Text('Tablet Content')),
          ),
        ],
      ),
    );
  }
}

class DesktopHome extends StatelessWidget {
  const DesktopHome({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Row(
        children: [
          SizedBox(
            width: 260,
            child: Center(child: Text('Desktop Sidebar')),
          ),
          Expanded(
            child: Center(child: Text('Desktop Content')),
          ),
        ],
      ),
    );
  }
}

14. Responsive Navigation

Navigation is one of the most important areas to adapt.

A small screen can use NavigationBar, while a larger screen can use NavigationRail.

class AdaptiveNavigation extends StatelessWidget {
  const AdaptiveNavigation({super.key});

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.sizeOf(context).width;

    if (width < 600) {
      return NavigationBar(
        destinations: const [
          NavigationDestination(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          NavigationDestination(
            icon: Icon(Icons.settings),
            label: 'Settings',
          ),
        ],
      );
    }

    return NavigationRail(
      destinations: const [
        NavigationRailDestination(
          icon: Icon(Icons.home),
          label: Text('Home'),
        ),
        NavigationRailDestination(
          icon: Icon(Icons.settings),
          label: Text('Settings'),
        ),
      ],
      selectedIndex: 0,
    );
  }
}

A 600 logical-pixel threshold is commonly used in Flutter examples for switching between compact and larger navigation patterns, but your application should choose breakpoints according to its actual layout needs.


15. Using Row and Column Responsively

One common responsive pattern is changing from a vertical layout on small screens to a horizontal layout on larger screens.

LayoutBuilder(
  builder: (context, constraints) {
    final isWide = constraints.maxWidth >= 700;

    return isWide
        ? Row(
            children: [
              Expanded(child: FirstSection()),
              Expanded(child: SecondSection()),
            ],
          )
        : Column(
            children: [
              FirstSection(),
              SecondSection(),
            ],
          );
  },
)

16. Expanded and Flexible

Hard-coded widths can cause overflow. Expanded and Flexible allow widgets to use available space more safely.

Row(
  children: [
    Expanded(
      child: Container(
        height: 100,
        child: const Text('First'),
      ),
    ),
    Expanded(
      child: Container(
        height: 100,
        child: const Text('Second'),
      ),
    ),
  ],
)

Benefits

  • Reduces overflow.
  • Uses available width efficiently.
  • Works well on different screen sizes.
  • Makes layouts easier to maintain.

17. Responsive Cards

Cards should not become extremely wide on desktop screens. A grid can control the maximum size of each item.

GridView.builder(
  padding: const EdgeInsets.all(16),
  gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
    maxCrossAxisExtent: 320,
    mainAxisSpacing: 16,
    crossAxisSpacing: 16,
    childAspectRatio: 1.4,
  ),
  itemCount: 12,
  itemBuilder: (context, index) {
    return Card(
      child: Center(
        child: Text('Card ${index + 1}'),
      ),
    );
  },
)

Using a maximum item width allows the number of columns to naturally respond to available window width instead of hard-coding a device-specific column count.


18. Responsive Product Grid

class ProductGrid extends StatelessWidget {
  const ProductGrid({super.key});

  @override
  Widget build(BuildContext context) {
    final products = List.generate(
      12,
      (index) => 'Product ${index + 1}',
    );

    return GridView.builder(
      padding: const EdgeInsets.all(16),
      gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 280,
        crossAxisSpacing: 16,
        mainAxisSpacing: 16,
        childAspectRatio: 0.8,
      ),
      itemCount: products.length,
      itemBuilder: (context, index) {
        return Card(
          child: Center(
            child: Text(products[index]),
          ),
        );
      },
    );
  }
}

19. Constraining Content on Large Screens

A common desktop design problem is allowing text fields, paragraphs, or cards to stretch across the entire screen.

A better approach is to limit the maximum width.

Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      maxWidth: 700,
    ),
    child: Padding(
      padding: const EdgeInsets.all(24),
      child: Text(
        'This content remains readable on large screens.',
      ),
    ),
  ),
)

This is especially useful for forms, articles, login pages and text-heavy screens.


20. Responsive Login Screen

class ResponsiveLogin extends StatelessWidget {
  const ResponsiveLogin({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: SingleChildScrollView(
            padding: const EdgeInsets.all(24),
            child: ConstrainedBox(
              constraints: const BoxConstraints(
                maxWidth: 450,
              ),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  const Text(
                    'Login',
                    style: TextStyle(
                      fontSize: 30,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 24),
                  const TextField(
                    decoration: InputDecoration(
                      labelText: 'Email',
                      border: OutlineInputBorder(),
                    ),
                  ),
                  const SizedBox(height: 16),
                  const TextField(
                    obscureText: true,
                    decoration: InputDecoration(
                      labelText: 'Password',
                      border: OutlineInputBorder(),
                    ),
                  ),
                  const SizedBox(height: 20),
                  SizedBox(
                    width: double.infinity,
                    child: ElevatedButton(
                      onPressed: null,
                      child: const Text('Login'),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

21. Master-Detail Layout

A master-detail interface is especially useful on tablets and desktops.

For example:

  • Left side: list of customers.
  • Right side: selected customer details.

On a phone, the list and details can be displayed as separate navigation screens.

LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth >= 700) {
      return Row(
        children: [
          SizedBox(
            width: 300,
            child: CustomerList(),
          ),
          const VerticalDivider(width: 1),
          const Expanded(
            child: CustomerDetails(),
          ),
        ],
      );
    }

    return CustomerList();
  },
)

Flutter's official adaptive-layout tutorial uses this general pattern for large screens.


22. Responsive Dashboard

Dashboards are excellent examples of multi-screen UI design.

Mobile

  • One card per row.
  • Compact navigation.
  • Scrollable content.

Tablet

  • Two cards per row.
  • Side navigation can be introduced.
  • More information can be visible simultaneously.

Desktop

  • Sidebar.
  • Multiple dashboard cards.
  • Charts and tables side-by-side.
  • Filters and actions visible together.

23. SafeArea for Different Devices

Different devices can have notches, camera cutouts, rounded corners and system UI areas. SafeArea helps keep content away from areas where it could be obscured.

Scaffold(
  body: SafeArea(
    child: YourContent(),
  ),
)

Flutter recommends using SafeArea when content needs protection from display cutouts and system UI.


24. Responsive Text

Text should remain readable across different screen sizes. Avoid unnecessarily large fixed font sizes and avoid forcing long text into very narrow areas.

LayoutBuilder(
  builder: (context, constraints) {
    final fontSize = constraints.maxWidth < 600 ? 24.0 : 32.0;

    return Text(
      'Responsive Heading',
      style: TextStyle(
        fontSize: fontSize,
        fontWeight: FontWeight.bold,
      ),
    );
  },
)

Also remember that users can change system text scaling. Your layout should therefore be tested with larger accessibility text sizes.


25. Responsive Images

Images should scale without causing overflow.

Image.network(
  'https://example.com/image.jpg',
  width: double.infinity,
  fit: BoxFit.cover,
)

Inside constrained layouts, combine flexible width with an appropriate aspect ratio.

AspectRatio(
  aspectRatio: 16 / 9,
  child: Image.network(
    imageUrl,
    fit: BoxFit.cover,
  ),
)

26. Responsive Forms

Forms should not become excessively wide on desktop screens.

Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      maxWidth: 600,
    ),
    child: Form(
      child: Column(
        children: [
          TextFormField(
            decoration: const InputDecoration(
              labelText: 'Full Name',
            ),
          ),
          const SizedBox(height: 16),
          TextFormField(
            decoration: const InputDecoration(
              labelText: 'Email',
            ),
          ),
        ],
      ),
    ),
  ),
)

27. Using Wrap for Responsive Content

Wrap is useful when items should automatically move to another line when there is not enough horizontal space.

Wrap(
  spacing: 12,
  runSpacing: 12,
  children: [
    ElevatedButton(
      onPressed: () {},
      child: const Text('Action 1'),
    ),
    ElevatedButton(
      onPressed: () {},
      child: const Text('Action 2'),
    ),
    ElevatedButton(
      onPressed: () {},
      child: const Text('Action 3'),
    ),
  ],
)

This is useful for filter chips, action buttons, tags and categories.


28. Responsive Tables

Tables that work on desktop can become difficult to use on phones because of limited horizontal space.

Possible approaches include:

  • Use horizontal scrolling for wide tables.
  • Convert rows into cards on small screens.
  • Show fewer columns on small screens.
  • Move secondary information into a details screen.
SingleChildScrollView(
  scrollDirection: Axis.horizontal,
  child: DataTable(
    columns: const [
      DataColumn(label: Text('Name')),
      DataColumn(label: Text('Email')),
      DataColumn(label: Text('Status')),
    ],
    rows: const [
      DataRow(
        cells: [
          DataCell(Text('John')),
          DataCell(Text('[email protected]')),
          DataCell(Text('Active')),
        ],
      ),
    ],
  ),
)

29. Adaptive Dialogs

A dialog can use a constrained width on large screens while remaining comfortable on mobile.

showDialog(
  context: context,
  builder: (context) {
    return AlertDialog(
      title: const Text('Confirm Action'),
      content: const Text(
        'Are you sure you want to continue?',
      ),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('Cancel'),
        ),
        ElevatedButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('Confirm'),
        ),
      ],
    );
  },
);

30. Supporting Mouse and Keyboard Input

Multi-screen applications often run on desktop and web, where users may use a mouse, trackpad and keyboard.

Consider supporting:

  • Mouse hover states
  • Keyboard navigation
  • Focus management
  • Keyboard shortcuts
  • Right-click where appropriate
  • Scroll wheel input
  • Tooltips

Flutter's adaptive design guidance recommends considering different input devices when building interfaces for larger screens.


31. Hover Effects for Desktop

class HoverCard extends StatefulWidget {
  const HoverCard({super.key});

  @override
  State createState() => _HoverCardState();
}

class _HoverCardState extends State {
  bool isHovered = false;

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      onEnter: (_) {
        setState(() {
          isHovered = true;
        });
      },
      onExit: (_) {
        setState(() {
          isHovered = false;
        });
      },
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 200),
        padding: const EdgeInsets.all(24),
        decoration: BoxDecoration(
          color: isHovered
              ? Colors.blue.shade100
              : Colors.white,
          borderRadius: BorderRadius.circular(12),
        ),
        child: const Text('Hover over me'),
      ),
    );
  }
}

32. Adaptive Sidebar

class AdaptiveSidebar extends StatelessWidget {
  const AdaptiveSidebar({super.key});

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth < 700) {
          return const SizedBox.shrink();
        }

        return Container(
          width: 240,
          padding: const EdgeInsets.all(16),
          child: const Column(
            children: [
              ListTile(
                leading: Icon(Icons.home),
                title: Text('Home'),
              ),
              ListTile(
                leading: Icon(Icons.person),
                title: Text('Profile'),
              ),
              ListTile(
                leading: Icon(Icons.settings),
                title: Text('Settings'),
              ),
            ],
          ),
        );
      },
    );
  }
}

33. Avoid Hard-Coded Device Checks

A common mistake is writing code such as:

if (Platform.isAndroid) {
  // Assume mobile layout
}

This is not a reliable way to determine the available layout space. An Android tablet, foldable, desktop window or multi-window configuration can have very different dimensions.

Instead, base layout decisions on available window size:

final width = MediaQuery.sizeOf(context).width;

if (width < 600) {
  return const CompactLayout();
}

return const ExpandedLayout();

34. Avoid Orientation-Only Layout Decisions

Do not assume that portrait always means mobile and landscape always means tablet or desktop.

A phone can be in landscape mode, while a tablet or desktop window can be narrow. Resizable windows and multi-window environments make orientation an unreliable replacement for measuring available space.

Use MediaQuery.sizeOf or LayoutBuilder when the layout decision depends on available space.


35. Avoid Full-Width Content on Desktop

Large screens provide a lot of horizontal space, but that does not mean every widget should expand to the full width.

For example, avoid:

Container(
  width: double.infinity,
  child: Text(
    'A very long paragraph...',
  ),
)

Instead, constrain the readable area:

Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      maxWidth: 800,
    ),
    child: const Text(
      'A readable content area on large screens.',
    ),
  ),
)

36. Preserving State During Screen Changes

When the screen changes size or orientation, widgets can be rebuilt. Important state such as scroll position should be preserved whenever possible.

PageStorageKey Example

ListView.builder(
  key: const PageStorageKey('product-list'),
  itemCount: 100,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text('Product $index'),
    );
  },
)

Flutter's adaptive-design guidance recommends preserving application and list state when window size, orientation or folding state changes.


37. Designing for Foldable Devices

Foldable devices introduce additional layout possibilities. A folded device may behave like a narrow screen, while an unfolded device may provide enough space for multiple panels.

For example:

LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth >= 800) {
      return const Row(
        children: [
          Expanded(child: MenuPanel()),
          Expanded(child: DetailsPanel()),
        ],
      );
    }

    return const MenuPanel();
  },
)

The important principle is to respond to the available application window rather than assuming a particular physical device configuration.


38. Complete Adaptive Dashboard Example

class AdaptiveDashboard extends StatelessWidget {
  const AdaptiveDashboard({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Dashboard'),
      ),
      body: LayoutBuilder(
        builder: (context, constraints) {
          final width = constraints.maxWidth;

          if (width < 600) {
            return const MobileDashboard();
          }

          if (width < 1000) {
            return const TabletDashboard();
          }

          return const DesktopDashboard();
        },
      ),
    );
  }
}

class MobileDashboard extends StatelessWidget {
  const MobileDashboard({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: const [
        DashboardCard(title: 'Users', value: '1,250'),
        DashboardCard(title: 'Orders', value: '540'),
        DashboardCard(title: 'Revenue', value: '₹85,000'),
      ],
    );
  }
}

class TabletDashboard extends StatelessWidget {
  const TabletDashboard({super.key});

  @override
  Widget build(BuildContext context) {
    return GridView.count(
      padding: const EdgeInsets.all(20),
      crossAxisCount: 2,
      crossAxisSpacing: 16,
      mainAxisSpacing: 16,
      children: const [
        DashboardCard(title: 'Users', value: '1,250'),
        DashboardCard(title: 'Orders', value: '540'),
        DashboardCard(title: 'Revenue', value: '₹85,000'),
        DashboardCard(title: 'Pending', value: '25'),
      ],
    );
  }
}

class DesktopDashboard extends StatelessWidget {
  const DesktopDashboard({super.key});

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        const SizedBox(
          width: 240,
          child: Center(
            child: Text('Sidebar'),
          ),
        ),
        const VerticalDivider(width: 1),
        Expanded(
          child: GridView.count(
            padding: const EdgeInsets.all(24),
            crossAxisCount: 4,
            crossAxisSpacing: 20,
            mainAxisSpacing: 20,
            children: const [
              DashboardCard(title: 'Users', value: '1,250'),
              DashboardCard(title: 'Orders', value: '540'),
              DashboardCard(title: 'Revenue', value: '₹85,000'),
              DashboardCard(title: 'Pending', value: '25'),
            ],
          ),
        ),
      ],
    );
  }
}

class DashboardCard extends StatelessWidget {
  final String title;
  final String value;

  const DashboardCard({
    super.key,
    required this.title,
    required this.value,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(title),
            const SizedBox(height: 10),
            Text(
              value,
              style: const TextStyle(
                fontSize: 26,
                fontWeight: FontWeight.bold,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

39. Recommended Project Structure

lib/
├── main.dart
├── models/
│   └── product.dart
├── data/
│   └── product_data.dart
├── screens/
│   ├── home/
│   │   ├── mobile_home.dart
│   │   ├── tablet_home.dart
│   │   └── desktop_home.dart
│   └── dashboard/
│       └── dashboard.dart
├── widgets/
│   ├── responsive_layout.dart
│   ├── adaptive_navigation.dart
│   ├── product_card.dart
│   └── dashboard_card.dart
└── utils/
    └── breakpoints.dart

Separating reusable widgets from screen-specific layouts makes responsive applications easier to maintain.


40. Creating a Reusable Responsive Layout Widget

class ResponsiveLayout extends StatelessWidget {
  final Widget mobile;
  final Widget tablet;
  final Widget desktop;

  const ResponsiveLayout({
    super.key,
    required this.mobile,
    required this.tablet,
    required this.desktop,
  });

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth < 600) {
          return mobile;
        }

        if (constraints.maxWidth < 1000) {
          return tablet;
        }

        return desktop;
      },
    );
  }
}

Usage

ResponsiveLayout(
  mobile: const MobileHome(),
  tablet: const TabletHome(),
  desktop: const DesktopHome(),
)

This pattern keeps responsive branching in one reusable component.


41. Common Widgets Used for Multi-Screen Interfaces

Widget Purpose
MediaQuery Access application window and accessibility information.
LayoutBuilder Build based on parent constraints.
Expanded Use remaining available space.
Flexible Allow flexible sizing inside Row or Column.
Wrap Move children to additional lines when required.
GridView Create responsive grid-based interfaces.
ConstrainedBox Set maximum or minimum dimensions.
SafeArea Protect content from system UI and display cutouts.
NavigationBar Compact navigation pattern.
NavigationRail Navigation pattern suitable for larger spaces.
SingleChildScrollView Prevent content from being clipped when it exceeds available space.

42. Common Mistakes

Mistake 1: Using Fixed Width Everywhere

Container(
  width: 500,
  child: TextField(),
)

This may overflow or look uncomfortable on small screens.

Better Approach

Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      maxWidth: 500,
    ),
    child: const TextField(),
  ),
)

Mistake 2: Checking Device Type

Do not automatically assume Android means phone or Windows means desktop layout.

Mistake 3: Using Orientation as the Main Breakpoint

Available width is usually more useful than simply checking portrait or landscape.

Mistake 4: Making Desktop UI Too Wide

Use maximum widths and grids to keep content readable.

Mistake 5: Ignoring Keyboard and Mouse

Large-screen users may interact using a mouse, trackpad and keyboard.

Mistake 6: Ignoring Text Scaling

Always test your interface with larger accessibility text sizes.


43. Best Practices

  • Design for available space rather than a specific device name.
  • Use MediaQuery.sizeOf for application-window measurements.
  • Use LayoutBuilder for local component constraints.
  • Choose breakpoints based on content requirements.
  • Use Expanded and Flexible instead of unnecessary fixed widths.
  • Use ConstrainedBox or similar constraints to prevent excessively wide content.
  • Use GridView for large-screen content collections.
  • Use adaptive navigation such as NavigationBar and NavigationRail.
  • Use SafeArea when content needs protection from system UI.
  • Support mouse, keyboard and touch interactions where appropriate.
  • Preserve important application state when the window changes size.
  • Break large widgets into smaller reusable components.
  • Test with different widths, heights and orientations.
  • Avoid unnecessary orientation locks.

44. Testing Multiple Screen Sizes

Responsive interfaces should be tested at multiple sizes.

Test on Mobile

  • Small portrait screen
  • Large phone
  • Landscape phone

Test on Tablet

  • Portrait tablet
  • Landscape tablet
  • Split-screen mode

Test on Desktop

  • Small desktop window
  • Medium desktop window
  • Wide desktop monitor
  • Resized browser window

Check For

  • Overflow errors
  • Text clipping
  • Incorrect navigation
  • Unusable buttons
  • Excessive empty space
  • Unreadable text
  • Broken images
  • Incorrect scrolling
  • Lost state

45. Practical Example: E-Commerce Application

Consider an e-commerce application.

Mobile

  • Top app bar.
  • Search field.
  • Two-column or compact product grid.
  • Bottom navigation.

Tablet

  • Navigation rail.
  • More product columns.
  • Larger product cards.
  • Additional filters.

Desktop

  • Sidebar categories.
  • Large product grid.
  • Persistent filters.
  • Shopping cart summary.
  • Additional product information.

The same product data and business logic can be shared while the presentation changes according to available space.


46. Practical Example: Learning Application

Mobile

Show courses in a vertical list.

Tablet

Show course categories and course content side-by-side.

Desktop

Show sidebar navigation, course list, video/content area and additional resources simultaneously.

LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth < 600) {
      return const CourseList();
    }

    return Row(
      children: [
        const SizedBox(
          width: 280,
          child: CourseCategories(),
        ),
        const VerticalDivider(width: 1),
        const Expanded(
          child: CourseContent(),
        ),
      ],
    );
  },
)

47. Design Workflow

  1. Identify the important content and actions.
  2. Design the smallest practical screen first.
  3. Make the mobile layout touch-friendly.
  4. Identify where additional space becomes useful.
  5. Introduce additional columns or panels for larger windows.
  6. Change navigation when necessary.
  7. Limit maximum content width on large screens.
  8. Test intermediate window sizes.
  9. Test accessibility and text scaling.
  10. Test mouse and keyboard interaction.
  11. Preserve application state during resizing.

48. Quick Revision Table

Concept Key Idea
Responsive UI Fits UI into available space.
Adaptive UI Chooses an appropriate UI for available space.
MediaQuery.sizeOf Gets application window size.
LayoutBuilder Gets local parent constraints.
Breakpoint Width at which layout behavior changes.
Expanded Uses remaining available space.
Flexible Allows flexible sizing.
GridView Useful for responsive collections.
ConstrainedBox Limits content dimensions.
SafeArea Protects content from system UI.
NavigationBar Useful for compact navigation.
NavigationRail Useful for larger layouts.

49. Interview Questions

Q1. What is responsive UI?

Responsive UI is an interface that adjusts its layout and dimensions according to the available space.

Q2. What is adaptive UI?

Adaptive UI chooses an appropriate interface structure or interaction model for the available space or environment.

Q3. What is the difference between MediaQuery and LayoutBuilder?

MediaQuery.sizeOf measures the application's window, while LayoutBuilder provides the constraints available to a specific widget from its parent.

Q4. Why should developers avoid hard-coded device types for layouts?

Physical device type does not always correspond to the available application window. Tablets can use split-screen mode, desktops can have small windows, and foldables can change their available layout space.

Q5. Why use ConstrainedBox?

It can prevent content from becoming excessively wide or narrow.

Q6. Why is GridView useful on large screens?

It can use available horizontal space while maintaining reasonable item widths.

Q7. Why use SafeArea?

It prevents content from being obscured by system UI, notches, camera cutouts and rounded display areas.

Q8. Should every device have a completely different UI?

No. Shared components and business logic can be reused while only the presentation and navigation structure change when necessary.


50. Practice Exercises

  1. Create a login screen that works on mobile, tablet and desktop.
  2. Create a responsive dashboard with different card layouts.
  3. Create a product grid that changes naturally with available width.
  4. Create an adaptive navigation system using NavigationBar and NavigationRail.
  5. Create a master-detail customer management interface.
  6. Create a responsive registration form with a maximum width.
  7. Create an adaptive e-commerce home page.
  8. Create a responsive admin panel with sidebar navigation.
  9. Test your application by continuously resizing a desktop browser.
  10. Add keyboard and mouse support to a desktop-oriented widget.

51. Key Takeaways

  • Multiple-screen UI design is essential for modern Flutter applications.
  • Responsive design adjusts UI to available space.
  • Adaptive design changes the UI structure when necessary.
  • Use MediaQuery.sizeOf to understand application window dimensions.
  • Use LayoutBuilder for local layout constraints.
  • Choose breakpoints according to content requirements rather than blindly following device categories.
  • Use flexible widgets such as Expanded, Flexible and Wrap.
  • Use grids and maximum-width constraints to make better use of large screens.
  • Adapt navigation for different amounts of available space.
  • Support touch, mouse and keyboard input where appropriate.
  • Preserve application state when screen dimensions change.
  • Always test mobile, tablet, desktop, web and intermediate window sizes.

52. Official Flutter Resources


53. Learn Flutter with JustAcademy

For structured Flutter learning, practical projects and course guidance, explore the JustAcademy Flutter training resources:

JustAcademy Flutter Training Course

Register for Flutter Course Demo


Conclusion

Designing interfaces for multiple screen sizes in Flutter is mainly about understanding available space and selecting a layout that remains usable at that size. A strong implementation combines responsive widgets, adaptive navigation, flexible layouts, appropriate constraints, readable content widths and support for different input methods.

Instead of building one rigid interface for a specific device, design reusable components and let the application respond to its current window size. By combining MediaQuery.sizeOf, LayoutBuilder, breakpoints, Expanded, Flexible, GridView, ConstrainedBox, SafeArea and adaptive navigation, you can create Flutter interfaces that work effectively across mobile, tablet, desktop, web and other screen configurations.

Previous Next
whatsapp