flutter_layout_grid

repository·main·Indexed 19 days ago

https://github.com/shyndman/flutter_layout_grid

A grid layout system for Flutter inspired by the CSS Grid Layout specification. It supports complex UI designs using fixed, flexible, and intrinsic content-sized tracks, named grid areas via ASCII-art strings, and automatic item placement. The library provides the LayoutGrid widget and placement tools like GridPlacement and NamedAreaGridPlacement to manage child positioning by index or area name.

Tokens
4.4K
Snippets
17
Records
20
Agent score
66%

What's inside flutter_layout_grid

  1. Use automatic child placement

    main

    The LayoutGrid can automatically place children when explicit positioning information is missing or partial. This behavior is controlled by the LayoutGrid.autoPlacement parameter and follows CSS Grid logic.

    • No placement info: If a child is not wrapped in GridPlacement or NamedAreaGridPlacement, it is treated as a 1x1 widget and placed in the first available vacant cell.
    • Partial placement info: If you provide some parameters (e.g., columnStart) but omit others (e.g., rowStart), the algorithm will search for the first vacant area that satisfies the provided constraints (e.g., finding the first available row in that specific column).
  2. Naming areas of the grid

    main

    You can slice the grid into rectangular regions called areas using an ASCII-art string provided to the areas parameter. This follows the format of CSS grid-template-areas but uses a multiline string.

    Important: If you provide an areas argument, you must ensure the number of elements in columnSizes and rowSizes matches the number of columns and rows defined in the ASCII art string.

    Example of a 2-column, 3-row grid with named areas:

    LayoutGrid(
      areas: '''
        header header
        nav    content
        footer footer
      '',
      columnSizes: [
        auto, // width for [nav, header, footer]
        1.fr, // width for [content, header, footer]
      ],
      rowSizes: [
        96.px, // height for [header]
        1.fr,  // height for [nav, content]
        72.px, // height for [footer]
      ],
      children: [
        // Children are assigned to areas using .inGridArea('name')
      ],
    )
    LayoutGrid(
      areas: '''
        header header
        nav    content
        footer footer
      '',
      columnSizes: [
        auto,
        1.fr,
      ],
      rowSizes: [
        96.px,
        1.fr,
        72.px,
      ],
      children: [
        // ...
      ],
    )
  3. Configure semantic ordering for accessibility

    main
    By default, Flutter exposes children to assistive technologies in their source order. If your visual grid layout changes the logical reading order (e.g., a child is visually at the top but appears last in the code), you must manually configure the semantic order. Use the Semantics widget's sortKey parameter to align the semantic tree with your visual layout.
  4. Configure Auto-placement packing strategies

    main

    When using the grid's auto-placement algorithm, you can choose between two packing strategies via the AutoPlacementPacking enum:

    • sparse: The algorithm only moves "forward" in the grid. It never backtracks to fill holes, ensuring items appear in the order they are provided, even if this leaves empty spaces.
    • dense: The algorithm attempts to fill holes earlier in the grid if smaller items appear later. This can result in items appearing out-of-order relative to their declaration if it helps fill gaps left by larger items.
    enum AutoPlacementPacking {
      sparse,
      dense,
    }
  5. How AutoPlacement works

    main

    The autoPlacement property controls how items that are not explicitly positioned are flowed into the grid. It uses the AutoPlacement class to define the direction and packing behavior:

    • AutoPlacement.rowSparse: Fills each row in turn, adding new rows as necessary.
    • AutoPlacement.rowDense: Fills each row in turn, attempting to fill holes earlier in the grid with smaller items (may cause items to appear out-of-order).
    • AutoPlacement.columnSparse: Fills each column in turn, adding new columns as necessary.
    • AutoPlacement.columnDense: Fills each column in turn, attempting to fill holes earlier in the grid (may cause items to appear out-of-order).
    // Example usage in LayoutGrid
    LayoutGrid(
      columnSizes: [Flexible(), Flexible()],
      rowSizes: [Flexible(), Flexible()],
      autoPlacement: AutoPlacement.rowDense,
      children: [...],
    )
  6. Basic usage of LayoutGrid

    main

    The LayoutGrid widget allows you to create complex layouts using track sizing for columns and rows, gaps between tracks, and child placement via named areas or index-based positioning. It uses terminology and concepts inspired by the CSS Grid Layout specification.

    import 'package:flutter_layout_grid/flutter_layout_grid.dart';
    
    class App extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Container(
          color: background,
          child: LayoutGrid(
            // ASCII-art named areas
            areas: '''
              header header  header
              nav    content aside
              nav    content .
              footer footer  footer
            ''',
            // Concise track sizing extension methods
            columnSizes: [152.px, 1.fr, 152.px],
            rowSizes: [
              112.px,
              auto,
              1.fr,
              64.px,
            ],
            // Column and row gaps!
            columnGap: 12,
            rowGap: 12,
            // Handy grid placement extension methods on Widget
            children: [
              Header().inGridArea('header'),
              Navigation().inGridArea('nav'),
              Content().inGridArea('content'),
              Aside().inGridArea('aside'),
              Footer().inGridArea('footer'),
            ],
          ),
        );
      }
    }
  7. Sizing columns and rows with track sizes

    main

    Grid track sizes are defined using LayoutGrid.columnSizes and LayoutGrid.rowSizes. You can use three types of track sizes:

    • Fixed: Occupies a specific number of pixels. Use FixedTrackSize(n), fixed(n), or n.px.
    • Flexible: Fills remaining space after the initial layout. Use FlexibleTrackSize(n), flexible(n), or n.fr.
    • Intrinsic Content: Sized to contain its items' contents and expands to fill available space after flexible tracks are calculated. Use IntrinsicContentTrackSize(), intrinsic(), or auto.

    Example of a 4x3 grid:

    LayoutGrid(
      columnSizes: [4.5.fr, 100.px, auto, 1.fr],
      rowSizes: [
        auto,
        100.px,
        1.fr,
      ],
    )
  8. Position child widgets in named areas

    main

    If you have defined areas in your LayoutGrid, you can place children into those specific named regions using the NamedAreaGridPlacement widget or the inGridArea extension method.

    Note: If a NamedAreaGridPlacement references an area name that does not exist in the grid's areas definition, the child will not be displayed. This behavior is useful for implementing responsive layouts where certain areas might disappear.

    LayoutGrid(
      areas: '''
        red red blue
        red red blue
         .   .  blue
      ''',
      columnSizes: [64.px, 64.px, 64.px],
      rowSizes: [
        64.px,
        64.px,
        64.px,
      ],
      children: [
        // Using NamedAreaGridPlacement constructor
        NamedAreaGridPlacement(
          areaName: 'red',
          child: Container(color: Colors.red),
        ),
        // Alternatively, using the extension method:
        Container(color: Colors.red).inGridArea('red'),
      ],
    )
  9. Position child widgets by row and column indexes

    main

    To place a widget in a specific location within a LayoutGrid, wrap it in a GridPlacement widget or use the withGridPlacement extension method. You can specify the starting position and the number of cells to span using columnStart, columnSpan, rowStart, and rowSpan.

    LayoutGrid(
      columnSizes: [1.fr, 1.fr, 1.fr, 1.fr],
      rowSizes: [
        1.fr,
        1.fr,
        1.fr,
      ],
      children: [
        GridPlacement(
          columnStart: 1,
          columnSpan: 3,
          rowStart: 0,
          rowSpan: 2,
          child: MyWidget(),
        ),
        // Alternatively, using the extension method:
        MyWidget().withGridPlacement(
          columnStart: 1,
          columnSpan: 3,
          rowStart: 0,
          rowSpan: 2,
        ),
      ],
    )
  10. Understand differences from CSS Grid Layout

    main

    While flutter_layout_grid implements many CSS Grid features, there are key differences to be aware of:

    Unsupported features:

    • Negative indexes: You cannot use negative values for row/column starts or ends to reference positions relative to the end of the axis.
    • Out-of-bounds placement: Placing an item outside the explicit grid defined by your template rows/columns will throw an error.
    • Advanced track sizing: minmax(), percentages, and aspect ratio track sizing are not currently supported.
    • Content-based sizing: Unlike CSS, flexible tracks in flutter_layout_grid do not account for the base size of their content (for performance reasons).

    Known limitations:

    • Flexible tracks whose flex factors sum to less than 1.
  11. Use GridPlacementExtensions for terse placement syntax

    main

    The package provides extension methods on Widget to allow for more concise placement code.

    • inGridArea(String areaName, {Key? key}): A shorthand for wrapping a widget in NamedAreaGridPlacement.
    • withGridPlacement({Key? key, int? columnStart, int columnSpan = 1, int? rowStart, int rowSpan = 1}): A shorthand for wrapping a widget in GridPlacement.
    // Using inGridArea
    MyWidget().inGridArea('sidebar');
    
    // Using withGridPlacement
    MyWidget().withGridPlacement(columnStart: 1, columnSpan: 2);