Designing Interfaces for Multiple Screen Sizes in Flutter
Designing interfaces for multiple screen sizes means creating Flutter applications that remain usable, readable, and visually organized across phones, tablets, desktops, web browsers, foldable devices, and resizable application windows. A good multi-screen interface does not simply stretch the same mobile UI onto a larger display. Instead, it responds to the amount of space available and can change layout structure, navigation, spacing, content density, and interaction patterns when necessary.
Flutter describes responsive design as fitting the UI into available space and adaptive design as making the UI usable in that space. In practical applications, both approaches are commonly used together. Flutter Adaptive and Responsive Design Documentation
1. What Does Designing for Multiple Screen Sizes Mean?
Flutter applications can run in environments with very different dimensions. A phone may provide only a few hundred logical pixels of width, while a desktop or browser window may provide considerably more.
The interface should therefore be able to respond to the available space.
Typical Screen Categories
- Small screens: Phones and narrow application windows.
- Medium screens: Tablets and medium-sized browser windows.
- Large screens: Desktop monitors, large tablets, and wide browser windows.
- Resizable screens: Desktop and web applications whose windows can continuously change size.
2. Why Multiple Screen Sizes Matter
A fixed layout can work correctly on one device and fail on another. For example, a horizontal row containing several buttons may fit on a desktop but overflow on a phone.
Designing for multiple screen sizes helps you:
- Prevent horizontal overflow.
- Improve readability.
- Use available screen space effectively.
- Support portrait and landscape configurations.
- Provide appropriate navigation patterns.
- Improve touch usability.
- Support mouse and keyboard interaction on larger devices.
- Handle resizable windows.
- Support tablets, desktops, web, and foldable devices.
- Create a consistent experience across platforms.
3. Responsive Design vs Adaptive Design
| Responsive Design | Adaptive Design |
|---|
| Fits UI elements into available space. | Changes UI structure when necessary. |
| Adjusts width, spacing, wrapping, and sizing. | Can change navigation and overall layout. |
| Example: cards change from four columns to two columns. | Example: bottom navigation changes to a navigation rail. |
| Focuses on fitting content. | Focuses on usability in the available space. |
Flutter recommends combining responsive and adaptive techniques rather than treating them as completely separate concepts. Flutter Responsive and Adaptive Design
4. Mobile-First Thinking
Mobile-first design starts with the constraints of a small screen and then progressively enhances the interface for larger screens.
Mobile Layout
+----------------------+
| App Bar |
+----------------------+
| |
| Content |
| |
| Content |
| |
+----------------------+
| Home | Search | User |
+----------------------+
Large Layout
+------------------------------------------------+
| Sidebar | Main Content |
| | |
| Home | Cards Charts Details |
| Users | |
| Orders | Table |
| Settings| |
+------------------------------------------------+
The goal is not necessarily to make the desktop version look identical to the mobile version. The larger screen can support additional functionality and information simultaneously.
5. Start With Available Space
A common mistake is to determine layout based only on whether a device is called a phone, tablet, or desktop. The actual application window may be smaller or larger than the physical display.
For example:
- A tablet can run an application in split-screen mode.
- A desktop application can be resized to a narrow window.
- A browser can be resized to many different widths.
- A foldable device can change the available layout area.
Flutter recommends basing layout decisions on available window or widget space rather than hardware categories. Flutter Adaptive Design Best Practices
6. Understanding Logical Pixels
Flutter layouts use logical pixels rather than directly designing against physical pixels. Logical pixels help UI elements maintain a reasonably consistent visual size across displays with different pixel densities.
Container(
width: 200,
height: 100,
child: const Text('Flutter'),
)
The width value represents logical layout space, not a fixed number of physical display pixels.
7. Using MediaQuery
MediaQuery.sizeOf(context) can be used when your decision depends on the size of the application's current window.
final size = MediaQuery.sizeOf(context);
print(size.width);
print(size.height);
Basic Example
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
final width = MediaQuery.sizeOf(context).width;
if (width < 600) {
return const MobileLayout();
}
return const LargeLayout();
}
}
MediaQuery.sizeOf returns the size of the application's window in logical pixels. Flutter recommends using the more specific sizeOf API when only the size is needed. Flutter MediaQuery and Adaptive Layout Guidance
8. Using LayoutBuilder
LayoutBuilder is useful when a widget should adapt to the amount of space 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
Adaptive Example
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return const MobileLayout();
}
return const DesktopLayout();
},
)
Flutter's adaptive tutorial demonstrates using LayoutBuilder and a 600 logical-pixel example breakpoint to switch between small and large layouts. Flutter LayoutBuilder and Adaptive Layout Tutorial
9. MediaQuery vs LayoutBuilder
| Feature | MediaQuery.sizeOf | LayoutBuilder |
|---|
| Measures | Application window | Parent-provided constraints |
| Returns | Size | BoxConstraints |
| Scope | Whole application window | Specific location in widget tree |
| Useful for | Global navigation/layout decisions | Local widget adaptation |
| Example | Mobile vs desktop navigation | Card columns inside a dashboard |
Flutter's general adaptive approach recommends choosing between these tools according to whether you need the whole application window size or the constraints available to a particular widget. Flutter General Adaptive Approach
10. Breakpoints
A breakpoint is a point at which the interface changes its layout structure.
Example Breakpoints
const double smallBreakpoint = 600;
const double mediumBreakpoint = 900;
One possible layout strategy is:
- Below 600: Small/mobile layout.
- 600 to below 900: Medium/tablet layout.
- 900 and above: Large/desktop layout.
These values are examples, not universal device definitions. Breakpoints should be chosen based on where your content needs more space or where the structure needs to change.
11. Three-Layout Strategy
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return const MobileLayout();
}
if (constraints.maxWidth < 900) {
return const TabletLayout();
}
return const DesktopLayout();
},
)
This approach is easy to understand and works well when mobile, tablet, and desktop layouts have meaningfully different structures.
12. Designing a Mobile Layout
Mobile screens normally benefit from vertically arranged content.
Scaffold(
appBar: AppBar(
title: const Text('Products'),
),
body: ListView(
padding: const EdgeInsets.all(16),
children: const [
ProductCard(),
ProductCard(),
ProductCard(),
],
),
bottomNavigationBar: NavigationBar(
selectedIndex: 0,
destinations: [
NavigationDestination(
icon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.shopping_cart),
label: 'Cart',
),
NavigationDestination(
icon: Icon(Icons.person),
label: 'Profile',
),
],
),
)
Mobile Design Principles
- Use simple navigation.
- Prefer vertical content flow.
- Use readable text sizes.
- Provide comfortable touch targets.
- Avoid excessive horizontal content.
- Use scrolling for long content.
- Keep important actions easy to reach.
13. Designing a Tablet Layout
Tablets can support more content simultaneously.
Row(
children: [
SizedBox(
width: 240,
child: NavigationPanel(),
),
const VerticalDivider(width: 1),
Expanded(
child: MainContent(),
),
],
)
Other tablet patterns include:
- Two-column forms.
- Two-column product grids.
- Navigation rail.
- List-detail layouts.
- Larger cards.
- Expanded dashboard sections.
14. Designing a Desktop Layout
Desktop applications can use large horizontal space for navigation, content, details, tables, and toolbars.
Row(
children: [
SizedBox(
width: 250,
child: Sidebar(),
),
const VerticalDivider(width: 1),
Expanded(
child: MainContent(),
),
],
)
Desktop Design Principles
- Use side navigation where appropriate.
- Support mouse interaction.
- Support keyboard interaction.
- Use hover feedback where useful.
- Provide keyboard shortcuts for productivity features.
- Use tables and multi-column layouts when appropriate.
- Do not stretch every component across the entire screen.
15. Adaptive Navigation
Navigation is one of the most important components to adapt.
Mobile NavigationBar
NavigationBar(
selectedIndex: 0,
destinations: const [
NavigationDestination(
icon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.search),
label: 'Search',
),
NavigationDestination(
icon: Icon(Icons.person),
label: 'Profile',
),
],
)
Large Screen NavigationRail
NavigationRail(
selectedIndex: 0,
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.search),
label: Text('Search'),
),
NavigationRailDestination(
icon: Icon(Icons.person),
label: Text('Profile'),
),
],
)
Flutter's adaptive guidance gives navigation as an example of a component that may switch between a bottom navigation bar on smaller windows and a navigation rail on larger windows. Flutter Adaptive Navigation Guidance
16. Complete Adaptive Navigation Example
class AdaptiveNavigationPage extends StatelessWidget {
const AdaptiveNavigationPage({super.key});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final isLarge = constraints.maxWidth >= 600;
if (isLarge) {
return Scaffold(
body: Row(
children: [
NavigationRail(
selectedIndex: 0,
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.settings),
label: Text('Settings'),
),
],
),
const Expanded(
child: MainContent(),
),
],
),
);
}
return Scaffold(
body: const MainContent(),
bottomNavigationBar: NavigationBar(
selectedIndex: 0,
destinations: const [
NavigationDestination(
icon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.settings),
label: 'Settings',
),
],
),
);
},
);
}
}
17. Designing Flexible Rows
Hard-coded widths can cause problems on small screens. Expanded and Flexible allow widgets to use available space more effectively.
Expanded Example
Row(
children: [
Expanded(
child: Container(
height: 100,
child: const Center(
child: Text('Card 1'),
),
),
),
const SizedBox(width: 16),
Expanded(
child: Container(
height: 100,
child: const Center(
child: Text('Card 2'),
),
),
),
],
)
18. Responsive Row to Column
A common pattern is to use a row when enough width is available and a column when width is limited.
LayoutBuilder(
builder: (context, constraints) {
final isWide = constraints.maxWidth >= 700;
if (isWide) {
return Row(
children: [
Expanded(child: ProfileCard()),
const SizedBox(width: 16),
Expanded(child: DetailsCard()),
],
);
}
return Column(
children: [
ProfileCard(),
const SizedBox(height: 16),
DetailsCard(),
],
);
},
)
19. Responsive Grid
Large screens often have enough space to show several cards in one row, while small screens may need one or two cards per row.
LayoutBuilder(
builder: (context, constraints) {
int columns;
if (constraints.maxWidth < 600) {
columns = 1;
} else if (constraints.maxWidth < 1000) {
columns = 2;
} else {
columns = 4;
}
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: columns,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 1.2,
),
itemCount: 12,
itemBuilder: (context, index) {
return Card(
child: Center(
child: Text('Item ${index + 1}'),
),
);
},
);
},
)
20. Grid Based on Maximum Item Width
For large screens, it is often better to define a maximum desired item width rather than hard-code the number of columns based on device type.
GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 280,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 1.2,
),
itemCount: 20,
itemBuilder: (context, index) {
return Card(
child: Center(
child: Text('Product ${index + 1}'),
),
);
},
)
Flutter's large-screen guidance specifically recommends using window size and maximum item widths instead of hard-coding a column count based on whether the device is a tablet or another hardware category. Flutter Large Screen Layout Guidance
21. Preventing Excessive Desktop Width
Large displays can create a problem when text fields, articles, forms, or cards become excessively wide.
Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 900,
),
child: Padding(
padding: const EdgeInsets.all(24),
child: MainContent(),
),
),
)
This technique keeps content within a controlled maximum width while still allowing it to shrink on smaller screens.
22. Responsive Forms
Mobile Form
Column(
children: [
TextField(
decoration: const InputDecoration(
labelText: 'First Name',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
decoration: const InputDecoration(
labelText: 'Last Name',
border: OutlineInputBorder(),
),
),
],
)
Desktop Form
Row(
children: [
Expanded(
child: TextField(
decoration: const InputDecoration(
labelText: 'First Name',
border: OutlineInputBorder(),
),
),
),
const SizedBox(width: 16),
Expanded(
child: TextField(
decoration: const InputDecoration(
labelText: 'Last Name',
border: OutlineInputBorder(),
),
),
),
],
)
Adaptive Form
LayoutBuilder(
builder: (context, constraints) {
final isWide = constraints.maxWidth >= 700;
return isWide
? Row(
children: [
Expanded(child: FirstNameField()),
const SizedBox(width: 16),
Expanded(child: LastNameField()),
],
)
: Column(
children: [
FirstNameField(),
const SizedBox(height: 16),
LastNameField(),
],
);
},
)
23. Responsive Login Interface
Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 450,
),
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
children: [
const FlutterLogo(size: 80),
const SizedBox(height: 24),
const Text(
'Login',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 24),
TextField(
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
obscureText: true,
decoration: const InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {},
child: const Text('Login'),
),
),
],
),
),
),
)
24. Responsive Images
Images should adapt to their available width while maintaining an appropriate aspect ratio.
AspectRatio(
aspectRatio: 16 / 9,
child: Image.asset(
'assets/images/banner.jpg',
width: double.infinity,
fit: BoxFit.cover,
),
)
Using Flexible Width
ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 700,
),
child: Image.asset(
'assets/images/product.jpg',
fit: BoxFit.contain,
),
)
25. Responsive Text
Text should remain readable without relying on arbitrary scaling based only on device type.
Text(
'Flutter Responsive UI',
style: Theme.of(context).textTheme.headlineMedium,
)
Using theme text styles helps maintain consistency. Also consider user accessibility settings such as text scaling rather than assuming every user has the default text size.
MediaQuery also provides accessibility-related information such as text scaling and high-contrast settings. Flutter SafeArea and MediaQuery
26. Responsive Spacing
LayoutBuilder(
builder: (context, constraints) {
final spacing = constraints.maxWidth < 600
? 16.0
: 32.0;
return Padding(
padding: EdgeInsets.all(spacing),
child: const Text('Responsive content'),
);
},
)
Spacing should support hierarchy without becoming unnecessarily large on small screens or excessively cramped on large screens.
27. Using Wrap
Wrap is useful when a group of widgets should automatically move to another line when there is not enough horizontal space.
Wrap(
spacing: 12,
runSpacing: 12,
children: [
Chip(label: Text('Flutter')),
Chip(label: Text('Dart')),
Chip(label: Text('Firebase')),
Chip(label: Text('UI')),
Chip(label: Text('API')),
],
)
Useful Applications
- Tags.
- Filters.
- Category buttons.
- Action buttons.
- Search filters.
- Toolbar actions.
28. Responsive Dashboard
A dashboard can use different layouts depending on available width.
Mobile
Column(
children: [
StatsCard(),
ChartCard(),
RecentOrders(),
],
)
Tablet
Column(
children: [
Row(
children: [
Expanded(child: StatsCard()),
Expanded(child: StatsCard()),
],
),
ChartCard(),
RecentOrders(),
],
)
Desktop
Column(
children: [
Row(
children: [
Expanded(child: StatsCard()),
Expanded(child: StatsCard()),
Expanded(child: StatsCard()),
Expanded(child: StatsCard()),
],
),
Row(
children: [
Expanded(child: ChartCard()),
Expanded(child: RecentOrders()),
],
),
],
)
29. Responsive List-Detail Interface
A list-detail layout is a common pattern for email applications, contact applications, messaging applications, and administration systems.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth >= 700) {
return Row(
children: [
SizedBox(
width: 300,
child: ContactList(),
),
const VerticalDivider(width: 1),
const Expanded(
child: ContactDetails(),
),
],
);
}
return ContactList();
},
)
Flutter's adaptive tutorial uses a similar concept, displaying a sidebar and details together on larger layouts and navigation-based screens on smaller layouts. Flutter Adaptive List-Detail Tutorial
30. Responsive Tables
Large tables can be difficult to display on mobile screens. Instead of forcing a wide table into a narrow viewport, consider changing the representation.
Desktop Table
DataTable(
columns: const [
DataColumn(label: Text('Name')),
DataColumn(label: Text('Email')),
DataColumn(label: Text('Status')),
DataColumn(label: Text('Role')),
],
rows: const [
DataRow(
cells: [
DataCell(Text('John')),
DataCell(Text('[email protected]')),
DataCell(Text('Active')),
DataCell(Text('Admin')),
],
),
],
)
Mobile Alternative
ListView(
children: const [
Card(
child: ListTile(
title: Text('John'),
subtitle: Text('[email protected]'),
trailing: Text('Active'),
),
),
],
)
The idea is to preserve the information while changing its presentation when a full table no longer fits comfortably.
31. Responsive Dialogs
Dialog(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 500,
),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Confirmation',
style: TextStyle(fontSize: 22),
),
const SizedBox(height: 16),
const Text(
'Are you sure you want to continue?',
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {},
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () {},
child: const Text('Confirm'),
),
],
),
],
),
),
),
)
32. SafeArea
SafeArea protects content from system UI such as status bars, display cutouts, and rounded display edges.
Scaffold(
body: SafeArea(
child: YourResponsiveContent(),
),
)
Flutter recommends using SafeArea where content could otherwise be obscured by physical display features or operating-system UI. Flutter SafeArea Documentation
33. Avoid Orientation-Only Layout Decisions
Orientation alone does not always tell you how much space an application has.
For example:
- A phone can be in landscape but still have limited width.
- A tablet can be in portrait and have substantial width.
- A desktop window can be resized to a narrow shape.
Flutter's adaptive best-practice guidance recommends using available window size through MediaQuery.sizeOf or constraints through LayoutBuilder rather than relying on orientation alone for major layout decisions. Flutter Adaptive Best Practices
34. Avoid Device-Type Checks
Avoid code such as:
if (isPhone) {
return PhoneLayout();
} else if (isTablet) {
return TabletLayout();
} else {
return DesktopLayout();
}
Instead, base the decision on available space:
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return const SmallLayout();
}
if (constraints.maxWidth < 900) {
return const MediumLayout();
}
return const LargeLayout();
},
)
This approach handles resizable windows and multi-window scenarios more reliably because the layout reacts to the actual rendering space. Flutter Device-Type and Window-Size Guidance
35. Designing for Foldable and Multi-Window Devices
Modern applications can run in split-screen or multi-window environments. Foldable devices can also change the available display area when the device changes configuration.
Therefore:
- Do not assume the application always occupies the entire physical display.
- Do not assume portrait mode means a narrow application.
- Do not assume a tablet always has enough width for a desktop-style layout.
- Use the current application window size when deciding layout.
- Consider display features such as hinges and folds when they affect the UI.
Flutter's adaptive documentation specifically discusses large screens, foldables, window sizing, and display dimensions. Flutter Large Screens and Foldables
36. Designing for Different Input Devices
Screen size is only one part of multi-device design. The input method also changes.
| Device | Common Input | Design Considerations |
|---|
| Phone | Touch | Large touch targets and simple gestures. |
| Tablet | Touch, keyboard | Touch-friendly controls with additional layout space. |
| Desktop | Mouse, keyboard, trackpad | Hover, shortcuts, precise pointer interaction. |
| Web | Mouse, keyboard, touch | Responsive resizing and multiple input methods. |
Flutter's adaptive best practices recommend designing around the strengths of each form factor and supporting different input devices where appropriate. Flutter Input and Form-Factor Guidance
37. Hover Support
Hover feedback can be useful on desktop and web interfaces.
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () {},
child: const Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Text('Hoverable Card'),
),
),
),
)
38. Keyboard Support
Desktop applications can provide keyboard shortcuts for frequently used actions.
Shortcuts(
shortcuts: const {
SingleActivator(
LogicalKeyboardKey.control,
key: LogicalKeyboardKey.keyS,
): SaveIntent(),
},
child: Actions(
actions: {
SaveIntent: CallbackAction<SaveIntent>(
onInvoke: (intent) {
// Save operation.
return null;
},
),
},
child: const Focus(
autofocus: true,
child: Text('Press Ctrl+S to save'),
),
),
)
39. Reusable Responsive Widget
When many screens use the same breakpoints, create a reusable 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 < 900) {
return tablet;
}
return desktop;
},
);
}
}
Usage
ResponsiveLayout(
mobile: const MobileHome(),
tablet: const TabletHome(),
desktop: const DesktopHome(),
)
40. Sharing Data Across Layouts
The data and business logic should generally remain independent from the screen-specific presentation.
class Product {
final String name;
final double price;
const Product({
required this.name,
required this.price,
});
}
const products = [
Product(
name: 'Laptop',
price: 75000,
),
Product(
name: 'Phone',
price: 45000,
),
];
The same data can be displayed differently:
Mobile:
ListView
Tablet:
GridView
Desktop:
GridView + Sidebar + Details
41. Recommended Project Structure
lib/
├── main.dart
├── models/
│ └── product.dart
├── services/
│ └── product_service.dart
├── widgets/
│ ├── product_card.dart
│ ├── responsive_layout.dart
│ └── navigation_panel.dart
├── screens/
│ ├── mobile/
│ │ └── mobile_home.dart
│ ├── tablet/
│ │ └── tablet_home.dart
│ └── desktop/
│ └── desktop_home.dart
└── theme/
└── app_theme.dart
This is one possible structure. Do not create separate platform-specific files for every widget unless the presentation genuinely needs to differ. Reusable components should remain shared whenever practical.
42. Do Not Duplicate Business Logic
Avoid implementing the same API, validation, database, and business logic separately for every screen size.
Shared Data
|
Business Logic
|
+-------------+-------------+
| | |
Mobile Tablet Desktop
UI UI UI
This architecture makes maintenance easier because the presentation can change while the underlying data and logic remain shared.
43. Complete Multiple-Screen-Size Example
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Multi Screen UI',
theme: ThemeData(
colorSchemeSeed: Colors.blue,
useMaterial3: true,
),
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return const MobileHome();
}
if (constraints.maxWidth < 900) {
return const TabletHome();
}
return const DesktopHome();
},
),
),
);
}
}
class MobileHome extends StatelessWidget {
const MobileHome({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Mobile'),
),
body: ListView(
padding: const EdgeInsets.all(16),
children: const [
InfoCard(title: 'Users', value: '1,250'),
InfoCard(title: 'Orders', value: '850'),
InfoCard(title: 'Revenue', value: '₹75,000'),
],
),
);
}
}
class TabletHome extends StatelessWidget {
const TabletHome({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Tablet'),
),
body: GridView.count(
padding: const EdgeInsets.all(24),
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
children: const [
InfoCard(title: 'Users', value: '1,250'),
InfoCard(title: 'Orders', value: '850'),
InfoCard(title: 'Revenue', value: '₹75,000'),
InfoCard(title: 'Pending', value: '42'),
],
),
);
}
}
class DesktopHome extends StatelessWidget {
const DesktopHome({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
SizedBox(
width: 240,
child: Container(
padding: const EdgeInsets.all(20),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'My App',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 30),
Text('Dashboard'),
SizedBox(height: 20),
Text('Users'),
SizedBox(height: 20),
Text('Orders'),
SizedBox(height: 20),
Text('Settings'),
],
),
),
),
const VerticalDivider(width: 1),
Expanded(
child: GridView.count(
padding: const EdgeInsets.all(32),
crossAxisCount: 4,
crossAxisSpacing: 20,
mainAxisSpacing: 20,
children: const [
InfoCard(title: 'Users', value: '1,250'),
InfoCard(title: 'Orders', value: '850'),
InfoCard(title: 'Revenue', value: '₹75,000'),
InfoCard(title: 'Pending', value: '42'),
],
),
),
],
),
);
}
}
class InfoCard extends StatelessWidget {
final String title;
final String value;
const InfoCard({
super.key,
required this.title,
required this.value,
});
@override
Widget build(BuildContext context) {
return Card(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
title,
style: const TextStyle(fontSize: 18),
),
const SizedBox(height: 8),
Text(
value,
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
}
44. Common Mistakes
Mistake 1: Fixed Width Everywhere
Container(
width: 1000,
child: Content(),
)
This can overflow when the available width is smaller.
Mistake 2: Hard-Coded Device Types
if (isTablet) {
return TabletUI();
}
Use available window size instead.
Mistake 3: Too Many Breakpoints
Every breakpoint increases layout complexity. Add a breakpoint when the UI structure genuinely needs to change.
Mistake 4: Stretching Everything on Desktop
Large displays require thoughtful maximum widths and content density.
Mistake 5: Ignoring Accessibility
Text scaling, contrast, and input accessibility can affect layout.
Mistake 6: Ignoring Keyboard and Mouse
Desktop and web interfaces should support pointer and keyboard interaction where appropriate.
45. Best Practices for Multiple Screen Sizes
- Design around available space.
- Use
MediaQuery.sizeOf for whole-window decisions.
- Use
LayoutBuilder for local constraints.
- Use content-based breakpoints.
- Keep breakpoints simple.
- Use flexible widgets instead of unnecessary fixed widths.
- Use
Expanded and Flexible.
- Use
Wrap for automatically flowing content.
- Use
GridView for suitable large-screen content.
- Use maximum widths for forms and reading content.
- Use
SafeArea where appropriate.
- Support different input devices.
- Keep data and business logic shared.
- Break large widgets into smaller reusable widgets.
- Test continuously at different window sizes.
- Avoid unnecessary orientation locks.
Flutter's official adaptive-design best practices emphasize breaking large widgets into smaller components, designing for the strengths of different form factors, avoiding unnecessary orientation locks, avoiding hardware-type checks, and supporting different input devices. Flutter Adaptive Design Best Practices
46. Testing Multiple Screen Sizes
Mobile Testing
- Small portrait phone.
- Large portrait phone.
- Landscape phone.
- Different text scaling settings.
- Keyboard-open state.
- Display cutouts and safe areas.
Tablet Testing
- Portrait tablet.
- Landscape tablet.
- Split-screen configuration.
- Different tablet widths.
- Navigation rail.
Desktop Testing
- Small desktop window.
- Medium desktop window.
- Large monitor.
- Mouse interaction.
- Keyboard navigation.
- Hover behavior.
Web Testing
flutter run -d chrome
Resize the browser window continuously and observe whether the layout transitions correctly. Flutter's adaptive tutorial explicitly demonstrates resizing the browser window to test adaptive behavior. Flutter Adaptive Layout Tutorial
47. Practical Project: Multi-Screen E-Commerce UI
Create an e-commerce application with:
- Product categories.
- Product cards.
- Search.
- Filters.
- Shopping cart.
- Product details.
- Checkout form.
- User profile.
Mobile
AppBar
|
Search
|
Categories
|
Product List
|
Bottom Navigation
Tablet
NavigationRail
|
Categories | Product Grid
|
+-- Product Details
Desktop
Sidebar | Header/Search
|
+-- Categories
|
+-- Product Grid | Filters
|
+-- Details Panel
|
+-- Cart/Checkout
48. Practical Project: Responsive Admin Panel
A responsive admin panel can adapt as follows:
| Component | Mobile | Tablet | Desktop |
|---|
| Navigation | NavigationBar/Drawer | NavigationRail | Sidebar |
| Stats | One column | Two columns | Four columns |
| Orders | Cards | Condensed table | Full table |
| Filters | Bottom sheet | Inline/filter panel | Persistent filter panel |
| Charts | Stacked | Two-column | Multi-panel |
49. Practical Workflow
- Identify the content and functionality of the screen.
- Design the basic mobile experience.
- Identify which components benefit from additional width.
- Determine where the layout should change.
- Create meaningful breakpoints.
- Measure the available space.
- Build flexible components.
- Change navigation when appropriate.
- Limit excessive content width on large screens.
- Support relevant input methods.
- Test different window sizes.
- Fix overflow and accessibility problems.
- Refactor repeated layout logic into reusable widgets.
50. Quick Revision
| Concept | Purpose |
|---|
| Responsive UI | Fits UI elements into available space. |
| Adaptive UI | Changes UI structure when needed. |
| MediaQuery.sizeOf | Gets application window size. |
| LayoutBuilder | Gets parent-provided constraints. |
| Breakpoint | Defines when layout structure changes. |
| Expanded | Uses remaining available space. |
| Flexible | Allows children to flex within available space. |
| Wrap | Moves children onto additional lines. |
| GridView | Creates two-dimensional layouts. |
| ConstrainedBox | Controls maximum and minimum dimensions. |
| SafeArea | Protects content from system UI and display cutouts. |
| NavigationBar | Useful for compact navigation layouts. |
| NavigationRail | Useful for wider layouts. |
| Sidebar | Useful for large-screen navigation. |
51. Practice Exercises
- Create a Flutter page that changes from a Column to a Row at a selected breakpoint.
- Create a product list that changes into a responsive grid.
- Create a mobile NavigationBar and desktop NavigationRail.
- Create a list-detail interface for tablet and desktop.
- Create a responsive registration form.
- Create an adaptive dashboard with statistics cards.
- Create a responsive admin table that becomes cards on mobile.
- Create a desktop sidebar that disappears on narrow layouts.
- Add hover support for desktop users.
- Add a keyboard shortcut for an important desktop action.
- Test the application by resizing a Chrome window.
- Test the same application in portrait and landscape configurations.
52. Key Takeaways
- Flutter applications can support many different screen sizes from a shared codebase.
- Do not assume that device type always tells you the available application space.
- Responsive design helps content fit the available space.
- Adaptive design can change the structure of the interface.
- Use
MediaQuery.sizeOf for application-window-level decisions.
- Use
LayoutBuilder for local widget constraints.
- Use content-based breakpoints instead of hardware-specific checks.
- Mobile layouts often use vertical lists and compact navigation.
- Tablet layouts can use split views, grids, and navigation rails.
- Desktop layouts can use sidebars, tables, toolbars, and multiple panels.
- Use
Expanded, Flexible, Wrap, and GridView for flexible layouts.
- Use
ConstrainedBox to avoid excessively wide desktop content.
- Use
SafeArea to protect content from system UI and display cutouts.
- Support mouse and keyboard interaction where appropriate.
- Keep data and business logic shared between layouts.
- Test applications at many window sizes instead of testing only one device.
53. Official Flutter Resources
54. Learn Flutter with JustAcademy
For structured Flutter training and practical application development, visit the JustAcademy Flutter Training Course.
You can also register for a Flutter course demo through the JustAcademy Flutter Course Demo Registration page.
Conclusion
Designing interfaces for multiple screen sizes is an essential Flutter skill for creating applications that work well across phones, tablets, desktops, web browsers, foldables, and resizable windows. The most important principle is to design around the space actually available to the application instead of relying only on device names. Use MediaQuery.sizeOf for application-window decisions and LayoutBuilder for local layout constraints. Combine these tools with flexible widgets such as Expanded, Flexible, Wrap, GridView, ConstrainedBox, and SafeArea. By sharing data and business logic while adapting navigation and presentation where appropriate, you can create maintainable Flutter interfaces that remain usable and organized across many screen sizes.