flutter_layout_grid 0.9.3

  • Readme
  • Changelog
  • Example
  • Installing
  • 87

Flutter Layout Grid #

Pub CircleCI

Example of Flutter Layout Grid

A grid-based layout system for Flutter, optimized for user interface design, inspired by CSS Grid Layout.

✨Featuring:✨

  • Fixed, flexible, and content-based row and column sizing
  • Precise control over placement of items if desired, including the ability to span rows, columns, and overlap items
  • A configurable automatic grid item placement algorithm, capable of sparse and dense packing across rows and columns
  • Right-to-left support, driven by ambient Directionality or configuation
  • Gutters!

Getting Started #

All the terminology used in this library is shared with the CSS Grid Layout spec. If you're unfamiliar, I recommend taking a look at MDN's glossary of grid terms.

For inclusion in your pubspec, see pub.dev.

Flutter v1.14.0+ support #

Flutter v1.14.0 includes a breaking change to ParentDataWidget, which is fixed in flutter_layout_grid: 0.10.0-dev.0.

Example #

This is the source for the sample you can see above.

const cellRed = Color(0xffc73232);
const cellMustard = Color(0xffd7aa22);
const cellGrey = Color(0xffcfd4e0);
const cellBlue = Color(0xff1553be);
const background = Color(0xff242830);

class Piet extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: background,
      child: LayoutGrid(
        columnGap: 12,
        rowGap: 12,
        templateColumnSizes: [
          FlexibleTrackSize(1),
          FlexibleTrackSize(3.5),
          FlexibleTrackSize(1.3),
          FlexibleTrackSize(1.3),
          FlexibleTrackSize(1.3),
        ],
        templateRowSizes: [
          FlexibleTrackSize(1),
          FlexibleTrackSize(0.3),
          FlexibleTrackSize(1.5),
          FlexibleTrackSize(1.2),
        ],
        children: [
          // Column 1
          _buildItemForColor(cellRed).withGridPlacement(
            columnStart: 0,
            rowStart: 0,
            rowSpan: 2,
          ),
          _buildItemForColor(cellMustard).withGridPlacement(
            columnStart: 0,
            rowStart: 2,
            rowSpan: 2,
          ),
          // Column 2
          _buildItemForColor(cellRed).withGridPlacement(
            columnStart: 1,
            rowStart: 0,
            rowSpan: 4,
          ),
          // Column 3
          _buildItemForColor(cellBlue).withGridPlacement(
            columnStart: 2,
            columnSpan: 3,
            rowStart: 0,
          ),
          _buildItemForColor(cellMustard).withGridPlacement(
            columnStart: 2,
            columnSpan: 3,
            rowStart: 1,
            rowSpan: 2,
          ),
          _buildItemForColor(cellGrey).withGridPlacement(
            columnStart: 2,
            rowStart: 3,
          ),
          // Column 4
          _buildItemForColor(cellBlue).withGridPlacement(
            columnStart: 3,
            rowStart: 3,
          ),
          // Column 5
          _buildItemForColor(cellMustard).withGridPlacement(
            columnStart: 4,
            rowStart: 3,
          ),
        ],
      ),
    );
  }

  Widget _buildItemForColor(Color c) => SizedBox.expand(
        child: DecoratedBox(decoration: BoxDecoration(color: c)),
      );
}

Usage #

Sizing of Columns and Rows #

There are currently three way to size tracks (rows or columns):

  • FlexibleSizeTrack — consumes remaining space after the initial layout has completed.
  • FixedSizeTrack — occupies a specific number of pixels on an axis
  • IntrinsicContentTrackSize — sized to contain its items' contents. Will also expand to fill available space, once FlexibleTrackSize tracks have been given the opportunity.

Technically, you could define your own, but I wouldn't because the API will be evolving.

Placing widgets in the LayoutGrid #

When an arbitrary widget is provided to LayoutGrid.children, it will be allotted a single cell and placed automatically according to the LayoutGrid.autoPlacement algorithm (described here).

LayoutGrid(
  templateColumnSizes = [/*...*/];
  templateRowSizes = [/*...*/];
  children: [
    MyWidget(), // Will occupy a 1x1 in the first vacant cell
  ],
)

Precise control over placement of an item is provided via the GridPlacement widget. You can think of GridPlacement as the Positioned equivalent for LayoutGrid — it controls the where a widget is placed, and the cells it occupies.

LayoutGrid(
  templateColumnSizes = [/*...*/];
  templateRowSizes = [/*...*/];
  children: [
    GridPlacement(
      // All parameters optional
      columnStart: 1,
      columnSpan: 3,
      rowStart: 5,
      rowSpan: 2,
      child: MyWidget(),
    ),
  ],
)

Alternatively, GridPlacements can be created using the withGridPlacement extension method on Widget. Using this method, the example above becomes:

LayoutGrid(
  templateColumnSizes = [/*...*/];
  templateRowSizes = [/*...*/];
  children: [
    MyWidget().withGridPlacement(
      // All parameters optional
      columnStart: 1,
      columnSpan: 3,
      rowStart: 5,
      rowSpan: 2,
    ),
  ],
)

All of the GridPlacement's constructor parameters are optional. It defaults to a 1x1 grid item that will be placed automatically by the grid. Specifying positioning or spanning information (via columnStart/columnSpan/rowStart/rowSpan parameters) will feed additional constraints into its algorithm.

A definitely-placed item (meaning columnStart and rowStart have both been provided), will always be placed precisely, even if it overlaps other definitely-placed items. Automatically-placed items will flow around those that have been placed definitely.

Accessibility and Placement

Take note that the meaning you convey visually through placement may not be clear when presented by assitive technologies, as Flutter defaults to exposing information in source order.

In situations where your semantic (visual) ordering differs from ordering in the source, the ordering can be configured via the Semantics widget's sortKey parameter.

Differences from CSS Grid Layout #

Things in CSS Grid Layout that are not supported:

  • Negative row/column starts/ends. In CSS, these values refer to positions relative to the end of a grid's axis.
  • Any cells outside of the explicit grid. If an item is placed outside of the area defined by your template rows/columns, we will throw an error. Support for automatic addition of rows and columns to accommodate out of bound items is being considered.
  • minmax(), percentages, aspect ratios track sizing
  • Named template areas, although they're coming

Differences:

  • In flutter_layout_grid, flexible tracks do not account for their content's base sizes as they do in CSS. It's expensive to measure, and I opted for speed.
  • Flexible tracks whose flex factors sum to < 1

Why not Slivers? #

This library is not Sliver-based. I'd considered it, but my use cases required the content-based sizing of rows and columns, and I didn't want to figure out the UI challenges associated with resizing tracks during scroll. I might be interested in taking those on at some point.

Roadmap #

  • [ ] Tests!
  • [ ] Named template areas, for friendlier item placement
  • [ ] Improved track sizing, including minimum/maximums and aspect ratios
  • [ ] The ability to specify row and column gaps at specific line locations via a delegate
  • [ ] Implicit grid support (automatic growth along an axis as children are added)
  • [ ] Performance improvements, as soon as I can get this profiler running(!!!)

[0.9.3] #

  • Correct Flutter dependency in pubspec

[0.9.2] #

  • Make AutoPlacement class a little more enum-like, by adding a toString() that resembles Dart enums and a static .values field

[0.9.1] #

  • Mention the prerelease version supporting Flutter v1.14.0+ in the README

[0.9.0] #

  • Reverted support for Flutter v1.14.0+, because it won't be stable for awhile. Flutter v1.14.0+ support is published as 0.10.0-dev.0

[0.8.0] #

  • Added support for Flutter v1.14.0+

[0.7.0] #

  • Add extension method support for grid item placement — Widget.withGridPlacement

[0.6.3] #

  • Fix broken badge links in README

[0.6.2] #

  • Fix several bugs in the examples
  • Add intrinsic-size computing functions. I don't know if they're right yet, but it's a start.

[0.6.1] #

  • Size grid minimally if an infinite constraint is provided

[0.6.0] #

  • Supply grid items with loose constraints, not tight

[0.5.3] #

  • README tweak

[0.5.2] #

  • Add a license (MIT)

[0.5.1] #

  • Dependency version fix

[0.5.0] #

  • First version. See the README.

example/flutter_layout_grid.dart

import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_layout_grid/flutter_layout_grid.dart';

// Sets a platform override for desktop to avoid exceptions. See
// https://flutter.dev/desktop#target-platform-override for more info.
void _enablePlatformOverrideForDesktop() {
  if (!kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux)) {
    debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
  }
}

void main() {
  _enablePlatformOverrideForDesktop();
  runApp(PietApp());
}

class PietApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Layout Grid Desktop Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        body: Container(
          color: Colors.white,
          child: Center(
            child: Piet(),
          ),
        ),
      ),
    );
  }
}

const cellRed = Color(0xffc73232);
const cellMustard = Color(0xffd7aa22);
const cellGrey = Color(0xffcfd4e0);
const cellBlue = Color(0xff1553be);
const background = Color(0xff242830);

class Piet extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: background,
      child: LayoutGrid(
        columnGap: 12,
        rowGap: 12,
        templateColumnSizes: [
          FlexibleTrackSize(1),
          FlexibleTrackSize(3.5),
          FlexibleTrackSize(1.3),
          FlexibleTrackSize(1.3),
          FlexibleTrackSize(1.3),
        ],
        templateRowSizes: [
          FlexibleTrackSize(1),
          FlexibleTrackSize(0.3),
          FlexibleTrackSize(1.5),
          FlexibleTrackSize(1.2),
        ],
        children: [
          // Column 1
          _buildItemForColor(cellRed).withGridPlacement(
            columnStart: 0,
            rowStart: 0,
            rowSpan: 2,
          ),
          _buildItemForColor(cellMustard).withGridPlacement(
            columnStart: 0,
            rowStart: 2,
            rowSpan: 2,
          ),
          // Column 2
          _buildItemForColor(cellRed).withGridPlacement(
            columnStart: 1,
            rowStart: 0,
            rowSpan: 4,
          ),
          // Column 3
          _buildItemForColor(cellBlue).withGridPlacement(
            columnStart: 2,
            columnSpan: 3,
            rowStart: 0,
          ),
          _buildItemForColor(cellMustard).withGridPlacement(
            columnStart: 2,
            columnSpan: 3,
            rowStart: 1,
            rowSpan: 2,
          ),
          _buildItemForColor(cellGrey).withGridPlacement(
            columnStart: 2,
            rowStart: 3,
          ),
          // Column 4
          _buildItemForColor(cellBlue).withGridPlacement(
            columnStart: 3,
            rowStart: 3,
          ),
          // Column 5
          _buildItemForColor(cellMustard).withGridPlacement(
            columnStart: 4,
            rowStart: 3,
          ),
        ],
      ),
    );
  }

  Widget _buildItemForColor(Color c) => SizedBox.expand(
        child: DecoratedBox(decoration: BoxDecoration(color: c)),
      );
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  flutter_layout_grid: ^0.9.3

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:flutter_layout_grid/flutter_layout_grid.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
75
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
87
Learn more about scoring.

We analyzed this package on Mar 27, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.7.1
  • pana: 0.13.6
  • Flutter: 1.12.13+hotfix.8

Health suggestions

Fix lib/src/foundation/placement.dart. (-0.50 points)

Analysis of lib/src/foundation/placement.dart reported 1 hint:

line 51 col 24: Test type arguments in operator ==(Object other).

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.6.0 <3.0.0
collection ^1.0.0 1.14.11 1.14.12
flutter 0.0.0
meta ^1.0.0 1.1.8
quiver ^2.0.0 2.1.3
Transitive dependencies
matcher 0.12.6
path 1.6.4
sky_engine 0.0.99
stack_trace 1.9.3
typed_data 1.1.6
vector_math 2.0.8
Dev dependencies
flutter_test