Flutter Adaptive Grid Layout
A Flutter package for building declarative, responsive, and adaptive grid layouts inspired by CSS Grid Layout. Simplify complex UI adaptation across different screen sizes with intuitive ASCII-art-like templates.
π Features
flutter_adaptive_grid_layout aims to streamline the development of adaptive user interfaces by providing a powerful and declarative way to define your layouts.
- Declarative Templates: Define your grid structure using an intuitive ASCII-art-like string similar to CSS Grid's
grid-template-areas. - Breakpoint-Based Adaptation: Easily define different layouts for
compact,medium,expanded, andlargescreen sizes, reacting toMediaQuerychanges. - Flexible & Fixed Sizing: Control row and column dimensions with
FlexSize.flex()for proportional scaling orFlexSize.fixed()for precise pixel sizes.FlexSize.content()allows for content-driven sizing (though with currentStack-based implementation, it behaves as flexible filler). - Named Regions: Assign meaningful names to grid areas and map your Flutter widgets directly to these regions.
- Simplified Responsive Design: Reduce complex conditional
buildlogic, leading to cleaner and more maintainable UI code.
π Getting Started
Installation
Add flutter_adaptive_grid_layout to your pubspec.yaml file:
YAML
dependencies:
flutter:
sdk: flutter
flutter_adaptive_grid_layout: ^0.0.1 # Use the latest version from pub.flutter-io.cn
Then, run flutter pub get to fetch the package.
Basic Usage
Wrap your adaptive layout with AdaptiveLayoutSizeProvider and use AdaptiveGridLayout to define your responsive structure.
Dart
import 'package:flutter/material.dart';
import 'package:flutter_adaptive_grid_layout/flutter_adaptive_grid_layout.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
// Determine the current screen size based on breakpoints
final currentLayoutSize = AdaptiveBreakpoints.materialDesign.fromWidth(
MediaQuery.of(context).size.width,
);
return Scaffold(
appBar: AppBar(
title: const Text('Adaptive Dashboard (Resize Window)'),
),
body: AdaptiveLayoutSizeProvider(
size: currentLayoutSize,
breakpoints: AdaptiveBreakpoints.materialDesign,
child: AdaptiveGridLayout(
breakpoints: AdaptiveBreakpoints.materialDesign,
layoutTemplates: {
// Define your layout for compact screens (e.g., mobile portrait)
AdaptiveLayoutSize.compact: AdaptiveGridTemplate(
size: AdaptiveLayoutSize.compact,
template: [
"header",
"content",
"sidebar", // Sidebar stacks below content
"footer",
],
columnSizes: [FlexSize.flex(1)], // Single column
rowSizes: [FlexSize.content(), FlexSize.flex(1), FlexSize.content(), FlexSize.content()],
),
// Define your layout for medium screens (e.g., tablets, mobile landscape)
AdaptiveLayoutSize.medium: AdaptiveGridTemplate(
size: AdaptiveLayoutSize.medium,
template: [
"header header",
"sidebar content",
"footer footer",
],
columnSizes: [FlexSize.fixed(200), FlexSize.flex(1)], // Fixed 200px sidebar, rest for content
rowSizes: [FlexSize.content(), FlexSize.flex(1), FlexSize.content()],
),
// Define your layout for expanded screens (e.g., small desktops)
AdaptiveLayoutSize.expanded: AdaptiveGridTemplate(
size: AdaptiveLayoutSize.expanded,
template: [
"header header header",
"nav content details",
"footer footer footer",
],
columnSizes: [FlexSize.fixed(150), FlexSize.flex(2), FlexSize.flex(1)], // Nav fixed, content gets 2x flex, details 1x
rowSizes: [FlexSize.content(), FlexSize.flex(1), FlexSize.content()],
),
// Define your layout for large screens (e.g., large desktops)
AdaptiveLayoutSize.large: AdaptiveGridTemplate(
size: AdaptiveLayoutSize.large,
template: [
"header header header header",
"nav content details ads",
"footer footer footer footer",
],
columnSizes: [FlexSize.fixed(150), FlexSize.flex(2), FlexSize.flex(1), FlexSize.fixed(100)], // Even more columns
rowSizes: [FlexSize.content(), FlexSize.flex(1), FlexSize.content()],
),
},
// Provide your actual widgets for each named region
children: {
'header': Container(
color: Colors.red[100],
padding: const EdgeInsets.all(8.0),
child: const Center(child: Text('App Header', style: TextStyle(fontSize: 20))),
),
'sidebar': Container(
color: Colors.green[100],
padding: const EdgeInsets.all(8.0),
child: const Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Sidebar Nav'),
ListTile(title: Text('Item 1')),
ListTile(title: Text('Item 2')),
],
),
),
'nav': Container(
color: Colors.purple[100],
padding: const EdgeInsets.all(8.0),
child: const Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Main Nav'),
ListTile(title: Text('Dashboard')),
ListTile(title: Text('Settings')),
],
),
),
'content': Container(
color: Colors.blue[100],
padding: const EdgeInsets.all(8.0),
child: const Center(child: Text('Main Content Area\n(Try resizing the window!)', textAlign: TextAlign.center, style: TextStyle(fontSize: 18))),
),
'details': Container(
color: Colors.orange[100],
padding: const EdgeInsets.all(8.0),
child: const Center(child: Text('Details Panel')),
),
'ads': Container(
color: Colors.yellow[100],
padding: const EdgeInsets.all(8.0),
child: const Center(child: Text('Advertisements')),
),
'footer': Container(
color: Colors.grey[300],
padding: const EdgeInsets.all(8.0),
child: const Center(child: Text('App Footer')),
),
},
),
),
);
}
}
Understanding the Template (AdaptiveGridTemplate)
The template property in AdaptiveGridTemplate is a List<String>, where each string represents a row. Words in the string represent named areas, and . represents an empty cell. Repeating a name spans that area across multiple columns.
Example template:
Dart
[
"header header",
"sidebar content",
"footer footer",
]
This defines a 3-row, 2-column grid where:
- The
headerspans the first row across both columns. sidebaris in(1,0)andcontentin(1,1).footerspans the third row across both columns.
Sizing Rows and Columns (FlexSize)
columnSizes and rowSizes define how the grid tracks (rows/columns) are sized:
FlexSize.flex(factor): Takes a proportional amount of available space.factordefaults to 1.0. (e.g.,FlexSize.flex(2)takes twice the space ofFlexSize.flex(1)).FlexSize.fixed(pixels): Takes a fixed size in logical pixels. (e.g.,FlexSize.fixed(200)for a 200px wide sidebar).FlexSize.content(): Aims to size based on the intrinsic content. (Current implementation is a basic approximation using available flexible space; for precise content-based sizing, a customRenderObjectWidgetwould be ideal).FlexSize.auto(): Similar toFlexSize.content(), often implying minimal space or filling remaining space.
πΌοΈ Screenshots
See flutter_adaptive_grid_layout in action across different screen sizes:
Compact Layout (e.g., Mobile Portrait)
On smaller screens, the sidebar moves below the main content, optimizing for vertical space.

Medium Layout (e.g., Tablet / Desktop Narrow)
This layout provides a sidebar on the left and main content on the right, suitable for moderate screen widths.

Expanded Layout (e.g., Small Laptops)
This layout provides a sidebar on the left and main content on the centre and details on the right.

Large Layout (e.g., Desktop Wide)
For expansive screens, additional panels (like details and ads) appear, utilizing the extra horizontal space.

βοΈ Advanced Usage & Notes
- Custom Breakpoints: You can define your own
AdaptiveBreakpointsinstance instead of usingAdaptiveBreakpoints.materialDesignif your app requires different size thresholds. - Performance: For extremely complex grids or highly dynamic content, consider that the current
StackandPositionedbased implementation is a basic approximation. For true CSS Grid-like intrinsic sizing and advanced performance, a customRenderObjectWidgetorCustomMultiChildLayoutwould be a more robust (and complex) solution.
π€ Contributing
Contributions are welcome! If you find a bug or have a feature request, please open an issue or submit a pull request on GitHub.
π License
This package is licensed under the MIT License. See the LICENSE file for more details.