A grid-based layout system for Flutter, inspired by CSS Grid Layout

Overview

Flutter Layout Grid

Pub Github test

A powerful grid layout system for Flutter, optimized for complex user interface design.

Piet painting recreated using Flutter Layout Grid   Periodic table rendered using Flutter Layout Grid   Scrabble board rendered using Flutter Layout Grid

Click images to see their code


Featuring:

  • 📐 Fixed, flexible, and content-sized rows and columns (docs)
  • 👇 Precise control over placement of items, including the ability to span rows, columns, and overlap items (docs)
  • 💬 Named grid areas for descriptive positioning of children (docs)
  • 🦾 A configurable automatic grid item placement algorithm, capable of sparse and dense packing across rows and columns (docs)
  • 🔚 Right-to-left support, driven by ambient Directionality or configuration
  • Accessibility considerations (this is your responsibility as a frontend developer, so please read docs and learn related technologies)
  • 🩳 Extension methods and helper functions for descriptive, and short, layout code
  • 🐛 Debugging aids, including widget property listings in DevTools, Debug Painting, and visual indication of child overflow

Inspired by (and largely based on), the excellent CSS Grid Layout spec.

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.

We also maintain a couple of specialized releases:

Null-safe version

dependencies:
  flutter_layout_grid: ^1.0.0

Flutter pre-v1.14.0 version

dependencies:
  flutter_layout_grid: ^0.9.0

Example

Visual:

Desktop app layout rendered using Flutter Layout Grid

Code:

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'),
        ],
      ),
    );
  }
}

This example is available at example/app_layout.dart.

For a similar example that includes responsive behavior, check out example/responsive_app_layout.dart.

Sizing of Columns and Rows

The sizes of the gridʼs columns and rows are set using LayoutGrid.columnSizes and LayoutGrid.rowSizes.

Hereʼs what a 4⨉3 grid might look like (4 columns, 3 rows):

LayoutGrid(
  columnSizes: [4.5.fr, 100.px, auto, 1.fr],
  rowSizes: [
    auto,
    100.px,
    1.fr,
  ],
)

Each element of columnSizes and rowSizes represents the function used to size a column or row (collectively known as "track sizes").

There are currently three way to size rows and columns:

Class Name Description Usage
FixedTrackSize Occupies a specific number of pixels on an axis FixedTrackSize(64)
fixed(64)
64.px
FlexibleSizeTrack Fills remaining space after the initial layout has completed FlexibleTrackSize(1.5)
flexible(1.5)
1.5.fr
IntrinsicContentTrackSize Sized to contain its itemsʼ contents. Will also expand to fill available space, once FlexibleTrackSize tracks have been given the opportunity. IntrinsicContentTrackSize()
intrinsic()
auto

Technically, you can also define your own, but probably shouldnʼt as the API will likely be evolving as I tackle (#25) (minmax() support).

Naming areas of the grid

A gridʼs rows and columns can be sliced into areas — rectangular regions containing one or more grid cells. These areas can be named (optionally), and used to place gridʼs children. The areas are named using an ASCII-art string provided to the areas parameter.

LayoutGrid(
  areas: '''
    header header
    nav    content
    footer footer
  ''',
  // ...
)

Note: We use the same format as CSSʼs grid-template-areas, except we use a multiline string.

If an areas argument has been provided to a grid, you must specify the same number of sizes using columnSizes and rowSizes elements. For the example above:

LayoutGrid(
  areas: '''
    header header
    nav    content
    footer footer
  ''',
  // 2 columns, 3 rows, just like the areas string
  columnSizes: [
    auto, // contributes width to [nav, header, footer]
    1.fr, // contributes width to [content, header, footer]
  ],
  rowSizes: [
    96.px, // contributes height to [header]
    1.fr,  // contributes height to [nav, content]
    72.px, // contributes height to [footer]
  ],
  children: [
    // ...
  ],
)

Grid children can be assigned to named areas using the NamedAreaGridPlacement widget. For more information, see assigning the child to a named area.

Positioning child widgets in the LayoutGrid

Once you have a grid, you have to tell its children which rows and columns they should occupy. There are three ways of doing this:

Child placement by row and column indexes

A gridʼs child can be instructed to occupy a specific set of columns and rows by using the GridPlacement widget.

For example, letʼs say you had a 4⨉3 grid, and you wanted a widget to be positioned from column 1–4 and row 0–2:

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, an extension method on Widget is available
    MyWidget().withGridPlacement(
      columnStart: 1,
      columnSpan: 3,
      rowStart: 0,
      rowSpan: 2,
    ),
  ],
)

GridPlacement also has a super power — all of its parameters are optional. If, for example, you do not specify a rowStart, the automatic placement algorithm will attempt to place the child in the first vacant spot that it can find.

Child placement in named areas

If your grid has named areas defined, you can place children in those areas using the NamedAreaGridPlacement widget. For example:

LayoutGrid(
  areas: '''
    red red blue
    red red blue
     .   .  blue
  ''',
  // Note that the number of columns and rows matches the grid above (3x3)
  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, an extension method on Widget is available
    Container(color: Colors.red).inGridArea('red'),
  ],
)

NOTE: If a NamedAreaGridPlacement references a named area that doesnʼt exist, it will not be displayed in the grid. This can be helpful when switching between responsive layouts.

Automatic child placement

Grid children can be placed into rows and columns automatically based on partial or non-existent placement information.

The algorithm responsible for automatic placement has several modes, selected through the LayoutGrid.autoPlacement parameter. The behavior of these modes are identical to those supported by CSSʼs grid, described described here.

When no placement information is provided

If a child is provided to the grid without being wrapped in a GridPlacement or NamedAreaGridPlacement, it will be allotted a single cell (1⨉1), and placed into the first vacant cell in the grid.

When partial placement information is provided

All of the GridPlacement widgetʼs parameters are optional. By specifying additional positioning or spanning information with columnStart/columnSpan/rowStart/rowSpan parameters, more constraints are fed into the placement algorithm.

For example, if columnStart is provided, but not rowStart, the placement algorithm will walk across the gridʼs rows until it finds a vacant area, in the specified column, that accommodates the child.

Read more about CSSʼs grid placement algorithm

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.

For an example of this in practice, see example/semantic_ordering.dart.

Automatic semantic ordering is currently being explored in #50.

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. Handy, but weʼre not there yet. (#5)
  • 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. (#7)
  • minmax(), percentages, aspect ratios track sizing (#25)

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

Roadmap

  • Tests! (we now have a decent suite going)
  • 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(!!!)
Comments
  • Intrinsic height not updated on resizing children

    Intrinsic height not updated on resizing children

    When using IntrinsicContentTrackSizes the intrinsic height of a row is calculated based on its children, but seemingly only once. When a child resizes, the intrinsic height is not updated I believe. This is especially onbious when using image widgets inside such rows:

            SingleChildScrollView(
              child: Container(
                color: Colors.yellow,
                child: LayoutGrid(
                  gridFit: GridFit.loose,
                  templateColumnSizes: [
                    FlexibleTrackSize(1),
                  ],
                  templateRowSizes: [
                    IntrinsicContentTrackSize(),
                  ],
                  children: [
                    Image.network(
                      'https://images.vrt.be/height40/2018/04/26/2644a887-4938-11e8-abcc-02b7b76bf47f.png',
                      // height: 40,
                    ).withGridPlacement(columnStart: 0, rowStart: 0),
                  ],
                ),
              ),
            )
    

    Screenshot 2020-12-01 at 09 58 21

    The height of this row is incorrect when the image is shown. Adding a pre-specified size (height) to the image will cause the row to size correctly, but it is not always viable to known the size upfront.

    Screenshot 2020-12-01 at 09 58 50

    Another way to see this problem is by replacing the SingleChildScrollView in the above example to be an IntrinsicHeight. The row will be 0px high, as the height is not updated.

    Any solution or workaround is greatly appreciated! Or even hint on where to fix this in code, as I have tried but don't understand yet when a recalculation should and can happen.

    cf https://github.com/Sub6Resources/flutter_html/pull/456

    bug 
    opened by erickok 21
  • Not suited well for unlimited scrolling?

    Not suited well for unlimited scrolling?

    I'm just evaluating this library and missing some features:

    • Item Builder for efficient & performant "unlimited"/huge lists
    • Ability to add/remove views/widgets (with animations?)

    Is it a general difficult task on flutter or why is there almost no library supporting unlimited scrolling?

    opened by heyarny 17
  • Resizable elements?

    Resizable elements?

    Hi,

    I'm pretty new to flutter and was wondering if there was any support/it might be on a roadmap to permit resizable elements? Im trying to create kind of a drag and drop system where the user can shrink the item and then move it around, with the rest of the grid repacking to accommodate. Is this doable?

    question 
    opened by polyrain 16
  • Add WeightedContentTrackSize

    Add WeightedContentTrackSize

    It would be useful to have a WeightedContentTrackSize (or whatever we end up calling it), which would size a track based on the weight it's contents have over the total intrinsic size of all columns.

    This is how an HTML table would size itself (given no other constraints). This would solve Sub6Resources/flutter_html#491.

    opened by erickok 12
  • Null safety release (now published as 1.0.0-nullsafety.6)

    Null safety release (now published as 1.0.0-nullsafety.6)

    Hey,

    As more and more packages are getting compatible with null safety, would it be possible for you to make a null safety release ? I need it personally because my app uses flutter_math which relies on quiver: ^3.0.0-nullsafety and it also use your package which relies on quiver ^2.0.0.

    Thanks !

    opened by Skyost 9
  • Layout with uneven columns and centring for a row

    Layout with uneven columns and centring for a row

    Hi, thanks for your work on this so far.

    I am wondering if it is possible to achieve something like this using any of the placement strategies.

    image

    So far, I cannot seem to find documentation for that. I am interested in the top row being centred.

    Thanks.

    opened by ominibyte 8
  • Improper intrinsic track sizing for multi-line Text

    Improper intrinsic track sizing for multi-line Text

    This widget is extremely powerful and very handy, thank you!

    Imagine you have a vertical CustomScrollView with a SliverFillRemaining sliver (hasScrollBody=false) that contains a container with a minimum height set to 300px that contains a LayoutGrid with a single column and 2 rows:

    • A flex row that expands to whatever free space is remaining
    • A row that contains a Text that is long enough to wrap across multiple lines

    Yes, this is a complicated layout! Essentially, it's a simplified example of an application that has a lot of vertically expanding content that is intrinsically sized, and then needs one row in a grid that is normally sized to fit the rest of the viewport, but if that content is longer than the height of the viewport it will then be able to scroll to see everything.

    The bug in this situation is that in the first pass Flutter will call computeMaxIntrinsicHeight on the RenderLayoutGrid renderbox (because it's in a context where the cross-axis (horizontal) is fixed and it wants to know how high the widget can go for a given width), and flutter passes the fixed cross-axis width. The code is currently specifying a constraint of a minimum width for the given width passed to _computeIntrinsicSize:

      @override
      double computeMaxIntrinsicHeight(double width) =>
          _computeIntrinsicSize(BoxConstraints(minWidth: width)).maxTracksHeight;
    

    _computeIntrinsicSize then calls computeGridSize passing the constraints, but leaving the child constraints set to null. Then the child constraints land up getting inflated to infinity.

    This is a problem because when it's computing the row track height, even with a grid fit of passthrough, the childConstraints has a min width of the defined amount but a max width of infinity. The max width of infinity gets translated into a crossAxisSize=0 when minIntrinsicSize is finally called. And because crossAxisSize=0, then the Text widget computes a height of like 3000 pixels (exact number depends on how much text is passed) because the Text RenderBox is asked what it's intrinsic height would be with a zero width, and because Text will wrap it creates one line of text per word making it very big. This leads to a very large max intrinsic height of thousands of pixels. Then flutter calls the layout itself, and now it passes the constraint with the height set to 3000+ pixels. The width is now set in this second computation, and the computeGridSize call this time only needs a few lines of text (for example). This leaves the flex row expanding to be thousands of pixels in size unnecessarily.

    Here is the debug track layout print output with some extra stuff added showing the problem (in this case there is a single column with 3 rows, row 0 and row 2 are intrinsically sized, and row 2 has the multi-line text widget):

    Debug Log Showing Problem
    DEBUG: ============= computeMaxIntrinsicHeight(579) =========
    DEBUG: ------------------------- computeGridSize(gridFit=GridFit.passthrough, gridConstraints=BoxConstraints(579.0<=w<=Infinity, 0.0<=h<=Infinity), childConstraints=null)
    DEBUG: ------------------------- computeGridSize updated childConstraints=BoxConstraints(579.0<=w<=Infinity, 0.0<=h<=Infinity)
    DEBUG COLUMN tracks with a maximum free space of 0, isAxisUpperBound=false
    min-max: 0.0->0.0 (same)
    free space: 0
    DEBUG ROW tracks with a maximum free space of 0, isAxisUpperBound=false
    Resolving intrinsic row heights [tracks [0,2]] with constraints BoxConstraints(579.0<=w<=Infinity, 0.0<=h<=Infinity)
    DEBUG: measuring minIntrinsicSize for 1 items...
    DEBUG: measuring min intrinsic item size on axis Axis.vertical with crossAxisSize=0
      min size of track 0 = 56
      distributing 56 across track 0 on min
      max size of track 0 = 56
    DEBUG: measuring minIntrinsicSize for 1 items...
    DEBUG: measuring min intrinsic item size on axis Axis.vertical with crossAxisSize=0
      min size of track 2 = 3902
      distributing 3902 across track 2 on min
      max size of track 2 = 3902
      update track 0 = 56.0
      update track 2 = 3902.0
    min-max: 3958.0->3958.0 (same)
    free space: -3958
    DEBUG ================= Starting grid layout for constraints BoxConstraints(w=579.0, h=3958.0), child constraints BoxConstraints(w=579.0, h=3958.0)
    DEBUG: ------------------------- computeGridSize(gridFit=GridFit.passthrough, gridConstraints=BoxConstraints(w=579.0, h=3958.0), childConstraints=null)
    DEBUG: ------------------------- computeGridSize updated childConstraints=BoxConstraints(w=579.0, h=3958.0)
    DEBUG COLUMN tracks with a maximum free space of 579, isAxisUpperBound=true
    min-max: 0.0->0.0 (same)
    free space: 579
    DEBUG ROW tracks with a maximum free space of 3958, isAxisUpperBound=true
    Resolving intrinsic row heights [tracks [0,2]] with constraints BoxConstraints(w=579.0, h=3958.0)
    DEBUG: measuring minIntrinsicSize for 1 items...
    DEBUG: measuring min intrinsic item size on axis Axis.vertical with crossAxisSize=579
      min size of track 0 = 56
      distributing 56 across track 0 on min
      max size of track 0 = 56
    DEBUG: measuring minIntrinsicSize for 1 items...
    DEBUG: measuring min intrinsic item size on axis Axis.vertical with crossAxisSize=579
      min size of track 2 = 94
      distributing 94 across track 2 on min
      max size of track 2 = 94
      update track 0 = 56.0
      update track 2 = 94.0
    min-max: 150.0->150.0 (same)
    free space: 3808
    Determined track sizes:
      column 0: 579
      row 0: 56
      row 1: 3808
      row 2: 94
    Finished track sizing
    

    The solution is pretty simple, when computing the max intrinsic width or max intrinsic height, instead of setting a minHeight or minWidth constraint, set a maxHeight or maxWidth constraint:

      @override
      double computeMaxIntrinsicWidth(double height) =>
          _computeIntrinsicSize(BoxConstraints(maxHeight: height)).maxTracksWidth;
    
      @override
      double computeMaxIntrinsicHeight(double width) =>
          _computeIntrinsicSize(BoxConstraints(maxWidth: width)).maxTracksHeight;
    

    This logic follows by reading the doc comments where it talks about width-in-height-out layout algorithms (e.g., which is what the Text widget is with line wrapping).

    With this fix in place, the layout works exactly as desired, with no extra space in the flex widget. Here is the debug layout log with the fix in place:

    Debug Log With Fix In Place
    DEBUG: ============= computeMaxIntrinsicHeight(579) =========
    DEBUG: ------------------------- computeGridSize(gridFit=GridFit.passthrough, gridConstraints=BoxConstraints(0.0<=w<=579.0, 0.0<=h<=Infinity), childConstraints=null)
    DEBUG: ------------------------- computeGridSize updated childConstraints=BoxConstraints(0.0<=w<=579.0, 0.0<=h<=Infinity)
    DEBUG COLUMN tracks with a maximum free space of 579, isAxisUpperBound=true
    min-max: 0.0->0.0 (same)
    free space: 579
    DEBUG ROW tracks with a maximum free space of 0, isAxisUpperBound=false
    Resolving intrinsic row heights [tracks [0,2]] with constraints BoxConstraints(0.0<=w<=579.0, 0.0<=h<=Infinity)
    DEBUG: measuring minIntrinsicSize for 1 items...
    DEBUG: measuring min intrinsic item size on axis Axis.vertical with crossAxisSize=579
      min size of track 0 = 56
      distributing 56 across track 0 on min
      max size of track 0 = 56
    DEBUG: measuring minIntrinsicSize for 1 items...
    DEBUG: measuring min intrinsic item size on axis Axis.vertical with crossAxisSize=579
      min size of track 2 = 94
      distributing 94 across track 2 on min
      max size of track 2 = 94
      update track 0 = 56.0
      update track 2 = 94.0
    min-max: 150.0->150.0 (same)
    free space: -150
    DEBUG ================= Starting grid layout for constraints BoxConstraints(w=579.0, h=469.0), child constraints BoxConstraints(w=579.0, h=469.0)
    DEBUG: ------------------------- computeGridSize(gridFit=GridFit.passthrough, gridConstraints=BoxConstraints(w=579.0, h=469.0), childConstraints=null)
    DEBUG: ------------------------- computeGridSize updated childConstraints=BoxConstraints(w=579.0, h=469.0)
    DEBUG COLUMN tracks with a maximum free space of 579, isAxisUpperBound=true
    min-max: 0.0->0.0 (same)
    free space: 579
    DEBUG ROW tracks with a maximum free space of 469, isAxisUpperBound=true
    Resolving intrinsic row heights [tracks [0,2]] with constraints BoxConstraints(w=579.0, h=469.0)
    DEBUG: measuring minIntrinsicSize for 1 items...
    DEBUG: measuring min intrinsic item size on axis Axis.vertical with crossAxisSize=579
      min size of track 0 = 56
      distributing 56 across track 0 on min
      max size of track 0 = 56
    DEBUG: measuring minIntrinsicSize for 1 items...
    DEBUG: measuring min intrinsic item size on axis Axis.vertical with crossAxisSize=579
      min size of track 2 = 94
      distributing 94 across track 2 on min
      max size of track 2 = 94
      update track 0 = 56.0
      update track 2 = 94.0
    min-max: 150.0->150.0 (same)
    free space: 319
    Determined track sizes:
      column 0: 579
      row 0: 56
      row 1: 319
      row 2: 94
    Finished track sizing
    

    I'm pretty sure the changes to computeMaxIntrinsicWidth and computeMaxIntrinsicHeight are correct in all cases, but you could also choose to only use the new behavior based on a new flag to the LayoutGrid.

    bug 
    opened by klondikedragon 7
  • Fix invalid area size when gap is negative

    Fix invalid area size when gap is negative

    I'm trying to make a table-like layout in Flutter so I have to use negative gap to accommodate outside and inside borders. The code looks like this:

            Stack(
              children: <Widget>[
                LayoutGrid(
                  columnGap: -borderWidth,
                  rowGap: -borderWidth,
                  gridFit: GridFit.passthrough,
                  templateColumnSizes: [/* ... */],
                  templateRowSizes: [/* IntrinsicContentTrackSize()... */],
                  children: [
                    Container(
                      child: cell,
                      decoration: BoxDecoration(border: Border.all(width: borderWidth)
                    ),
                    /* ... */
                  ],
                ),
                Positioned.fill(
                  child: Container(decoration: BoxDecoration(border: Border.all(width: borderWidth))
                ),
              ],
            )
    

    This PR tries to fix the invalid area size when the rows are IntrinsicContentTrackSizes. I have also included a golden image so it's easier to imagine.

    opened by daohoangson 7
  • Support widget tests

    Support widget tests

    I wanted to have a widget test for your example, but it does not work. I copied LayoutGridExample from the example. Note: LayoutGridExample is wrapped in a MaterialApp Widget because it needed a TextDirection to work in the widget test.

      testWidgets("test LayoutGridExample", (WidgetTester tester) async {
              await tester.pumpWidget(MaterialApp(
              home: Scaffold(body: LayoutGridExample()),
        ));
      });` 
    
    The result was:
    ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
    The following NoSuchMethodError was thrown during performLayout():
    The getter 'minWidth' was called on null.
    Receiver: null
    Tried calling: minWidth
    
    The relevant error-causing widget was:
      LayoutGrid
    When the exception was thrown, this was the stack:
    #0      Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
    #1      RenderLayoutGrid.computeMinIntrinsicWidth (package:flutter_layout_grid/src/rendering/layout_grid.dart:193:33)
    #2      RenderBox._computeIntrinsicDimension (package:flutter/src/rendering/box.dart:1299:20)
    #3      RenderBox.getMinIntrinsicWidth (package:flutter/src/rendering/box.dart:1341:12)
    #4      RenderBox.debugAssertDoesMeetConstraints.<anonymous closure>.testIntrinsic (package:flutter/src/rendering/box.dart:1986:41)
    #5      RenderBox.debugAssertDoesMeetConstraints.<anonymous closure>.testIntrinsicsForValues (package:flutter/src/rendering/box.dart:1997:43)
    #6      RenderBox.debugAssertDoesMeetConstraints.<anonymous closure> (package:flutter/src/rendering/box.dart:2004:32)
    #7      RenderBox.debugAssertDoesMeetConstraints (package:flutter/src/rendering/box.dart:2028:6)
    #8      RenderBox.size=.<anonymous closure> (package:flutter/src/rendering/box.dart:1745:7)
    #9      RenderBox.size= (package:flutter/src/rendering/box.dart:1747:6)
    #10     RenderLayoutGrid.performLayout (package:flutter_layout_grid/src/rendering/layout_grid.dart:257:9)
    #11     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #12     RenderPadding.performLayout (package:flutter/src/rendering/shifted_box.dart:206:11)
    #13     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #14     RenderConstrainedBox.performLayout (package:flutter/src/rendering/proxy_box.dart:259:13)
    #15     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #16     MultiChildLayoutDelegate.layoutChild (package:flutter/src/rendering/custom_layout.dart:163:11)
    #17     _ScaffoldLayout.performLayout (package:flutter/src/material/scaffold.dart:477:7)
    #18     MultiChildLayoutDelegate._callPerformLayout (package:flutter/src/rendering/custom_layout.dart:232:7)
    #19     RenderCustomMultiChildLayoutBox.performLayout (package:flutter/src/rendering/custom_layout.dart:391:14)
    #20     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #21     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #22     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #23     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #24     _RenderCustomClip.performLayout (package:flutter/src/rendering/proxy_box.dart:1232:11)
    #25     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #26     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #27     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #28     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #29     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #30     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #31     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #32     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #33     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #34     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #35     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #36     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #37     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #38     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #39     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #40     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #41     RenderOffstage.performLayout (package:flutter/src/rendering/proxy_box.dart:3168:13)
    #42     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #43     RenderStack.performLayout (package:flutter/src/rendering/stack.dart:505:15)
    #44     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #45     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #46     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #47     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #48     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #49     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #50     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #51     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #52     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #53     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #54     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #55     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #56     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #57     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:105:13)
    #58     RenderObject.layout (package:flutter/src/rendering/object.dart:1724:7)
    #59     RenderView.performLayout (package:flutter/src/rendering/view.dart:167:13)
    #60     RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:1584:7)
    #61     PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:844:18)
    #62     AutomatedTestWidgetsFlutterBinding.drawFrame (package:flutter_test/src/binding.dart:977:23)
    #63     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:283:5)
    #64     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1102:15)
    #65     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1041:9)
    #66     AutomatedTestWidgetsFlutterBinding.pump.<anonymous closure> (package:flutter_test/src/binding.dart:872:9)
    #69     TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:69:41)
    #70     AutomatedTestWidgetsFlutterBinding.pump (package:flutter_test/src/binding.dart:859:27)
    #71     WidgetTester.pumpWidget.<anonymous closure> (package:flutter_test/src/widget_tester.dart:323:22)
    #74     TestAsyncUtils.guard (package:flutter_test/src/test_async_utils.dart:69:41)
    #75     WidgetTester.pumpWidget (package:flutter_test/src/widget_tester.dart:320:27)
    #76     main.<anonymous closure> 
    #79     testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:124:25)
    #81     testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:122:9)
    #82     TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:696:19)
    #96     AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:1066:17)
    #98     AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:1054:35)
    (elided 35 frames from class _FakeAsync, package dart:async, package dart:async-patch, and package stack_trace)
    
    The following RenderObject was being processed when the exception was fired: RenderLayoutGrid#09ebd NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE:
      creator: LayoutGrid ← Padding ← SizedBox.expand ← LayoutGridExample ← _BodyBuilder ← MediaQuery ←
        LayoutId-[<_ScaffoldSlot.body>] ← CustomMultiChildLayout ← AnimatedBuilder ← DefaultTextStyle ←
        AnimatedDefaultTextStyle ← _InkFeatures-[GlobalKey#fa485 ink renderer] ← ⋯
      parentData: offset=Offset(64.0, 0.0) (can use size)
      constraints: BoxConstraints(w=736.0, h=600.0)
      size: Size(736.0, 600.0)
    This RenderObject had the following descendants (showing up to depth 5):
        child 1: RenderDecoratedBox#c35ca NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
          child: RenderPadding#70ded NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
            child: RenderLimitedBox#fcc45 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
              child: RenderConstrainedBox#52cb1 NEEDS-LAYOUT NEEDS-PAINT
        child 2: RenderDecoratedBox#fa04b NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
          child: RenderPadding#2166b NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
            child: RenderLimitedBox#201b5 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
              child: RenderConstrainedBox#72513 NEEDS-LAYOUT NEEDS-PAINT
        child 3: RenderDecoratedBox#c144d NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
          child: RenderPadding#6bf68 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
            child: RenderLimitedBox#a7c7d NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
              child: RenderConstrainedBox#1cc60 NEEDS-LAYOUT NEEDS-PAINT
        child 4: RenderDecoratedBox#4c85b NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
          child: RenderPadding#f23f0 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
            child: RenderLimitedBox#fc6db NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
              child: RenderConstrainedBox#f6952 NEEDS-LAYOUT NEEDS-PAINT
        child 5: RenderDecoratedBox#db41d NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
          child: RenderPadding#343cb NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
            child: RenderLimitedBox#8bbf9 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
              child: RenderConstrainedBox#f95e3 NEEDS-LAYOUT NEEDS-PAINT
        child 6: RenderDecoratedBox#2f451 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
          child: RenderPadding#ea346 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
            child: RenderLimitedBox#49d0c NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
              child: RenderConstrainedBox#74d25 NEEDS-LAYOUT NEEDS-PAINT
        child 7: RenderDecoratedBox#eb805 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
          child: RenderPadding#3e3bd NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
            child: RenderLimitedBox#2e62e NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
              child: RenderConstrainedBox#7d4d6 NEEDS-LAYOUT NEEDS-PAINT
    ════════════════════════════════════════════════════════════════════════════════════════════════════
    

    This may be related to another issue

    bug 
    opened by TobiasSuhrborg 7
  • What is the equivalent of childAspectRatio in LayoutGrid?

    What is the equivalent of childAspectRatio in LayoutGrid?

    Since GridView doesn't support some advanced features like varied spans, I take flutter_layout_grid as a perfect candidate of it.

    But it seems necessary to pass a definite rowSize(i.e. defined with .px) to LayoutGrid, otherwise the height of the grid is always zero. What I expect is a parameter like childAspectRatio in GridView to set the h/w ratio of every child in the grid. I've tried to wrap the child with AspectRatio widget, but it doesn't work.

    Here is a minimal example:

    table = LayoutGrid(
      columnSizes: List.filled(cols, 1.fr),
      rowSizes: List.filled(rows, auto),
      children: _buildTable(cols, rows),
    );
    
    List<Widget> _buildTable(int cols, int rows) {
      List<Widget> result = List.generate(
          cols * rows,
          (index) => AspectRatio(
            aspectRatio: 9.0/16.0,
            child: Container(
                  margin: EdgeInsets.all(2),
                  padding: EdgeInsets.all(2),
                  decoration: BoxDecoration(
                      color: Theme.of(context).hintColor.withOpacity(0.14),
                      borderRadius: BorderRadius.circular(4)),
                ).withGridPlacement(
                    rowStart: index ~/ cols, columnStart: index % cols),
          ));
      return result;
    }
    

    It's rendered as a zero-height widget. How could I fix it?

    opened by w568w 5
  • Row gaps in nested grid cause overflow on parent grid

    Row gaps in nested grid cause overflow on parent grid

    In this example: https://gist.github.com/dsyrstad/ecc79ed05f25a462476ea414812158f2 I have two nested grids which use auto-sized rows and row gaps. The inner grid overflows the outer grid's cell by the exact amount of the row gap:

    image

    It overflows by exactly the InnerGrid rowGap (10) * 2 (two gaps). Notice that when InnerGrid is not nested (bottom of screenshot), it does not overflow.

    If I change the InnerGrid rowGap to zero, it no longer overflows:

    image

    Note that the rowGap on OuterGrid is simply there so you can see Row 3 overflowing into the gap.

    I'm using flutter_layout_grid 2.0.0 and Flutter 2.10.5. The problem is reproducible on all platforms that I've tried (Linux desktop, web, and Android).

    opened by dsyrstad 4
  • Draggable gaps ?

    Draggable gaps ?

    Hi,

    I am wondering if the layout can / could support draggable gaps where one gap (a separation between 2 content panes) is draggable to adjust the size / ratio of those 2 panes.

    Something like this: CleanShot 2022-05-16 at 18 36 27

    At the same time, maybe this whole thing and a state manager to save the updated layout should be build on top of this project rather than integrated with it...

    opened by tlvenn 0
  • Improve feature list to clarify named areas and directionality

    Improve feature list to clarify named areas and directionality

    The third bullet about named areas isn't clear unless you're familiar with grid concepts. The fifth point is also unclear if you aren't familiar with Flutter.

    documentation 
    opened by hilaryhacksel 3
  • Improve accessibility through automatic semantic ordering

    Improve accessibility through automatic semantic ordering

    The README has a description of how to get a screenreader to announce grid items in a sensible order, independently of how they appear visually. This is roughly equivalent to how CSS Grid Layout works (ie, it doesn't do much, but you can direct behavior yourself).

    It occurred to me that we can do a much better job than this, since we may be able to automatically introduce SemanticsNodes to mirror placement. For example, introducing a SemanticsSortKey subclass that orders by column first, then row (and considers directionality?).

    I'm not exactly sure how this would work yet. Things that would need figuring out:

    • How are semantics nodes introduced without using Semantics widgets?
    • Should the grid introduce a SemanticsNode, so that children are kept explicit (explicitChildNodes = true)
      • Is there another way to keep a child's sortKey from merging with its parent?
      • What is the implication of this, if the nodes would merge in other circumstances, like with Text widgets?
    • How should the automatic child SemanticsNodes work?
      • We would need for placement to have taken place, in order to resolve to explicit column/row placement for the sort key.
      • How are they introduced?
    • How does directionality fit in?
    • Should this be default behavior?
      • If so, how do you turn it off? And vice versa?
    enhancement 
    opened by shyndman 0
Owner
Felt
Felt
Advanced & beautiful animations inspired by animate_do and Animate.css, every animation is a widget that contains default but customizable values 💙

animate_it An animation package inspired by animate_do and Animate.css. Since the original animate_do is not being maintained, I have decided to creat

Luke Moody 3 Oct 1, 2022
Grid-View-App - Grid View App For Flutter

grid_view_app practice purpose flutter application Getting Started This project

Md Tarequl Islam 4 Jun 9, 2022
An Event-based system, highly inspired by NodeJS's Event Emitter

An Event-based system, highly inspired by NodeJS's Event Emitter. This implementation uses generic types to allow for multiple data types, while still being intuitive.

JUST A SNIPER ツ 5 Dec 1, 2022
Create a Grid Layout of IoT (Internet of Things) devices in a particular house.

Create a Grid Layout of IoT (Internet of Things) devices in a particular house. Keep it simple to just 4-6 devices. Each device will have an icon on its own. When you press the icon, toggle the image and toggle the text underneath between on and off.

null 0 Dec 30, 2021
Flutter After Layout - Brings functionality to execute code after the first layout of a widget has been performed

Flutter After Layout - Brings functionality to execute code after the first layout of a widget has been performed, i.e. after the first frame has been displayed. Maintainer: @slightfoot

Flutter Community 432 Jan 3, 2023
Application developed in Flutter with inspired layout in the Tinder

flutter_tinder_template This is an template implementation of the Tinder App with Flutter. How to Run the Project Ensure that you have the flutter ins

Gildson 41 Sep 21, 2021
A Flutter widget for rendering HTML and CSS as Flutter widgets

flutter_html A Flutter widget for rendering HTML and CSS as Flutter widgets. Screenshot 1 Screenshot 2 Screenshot 3 Table of Contents: Installing Curr

Vishal Raj 1 Dec 15, 2021
C2F can convert css style to Flutter code online.

C2F is an interesting little project. I hope to find a way to convert css styles to flutter styles. I believe many web developers will like it. https:

Anonymous Namespace 232 Dec 29, 2022
📐 It's a set of common utility strategies to work with responsive styles with Flutter and CSS in JS

@displaykit/responsive_styles You don't need to be worried just because you have to support multiple screens ?? ?? ?? ?? . It's a set of common utilit

DisplayKit Tech 42 Dec 16, 2022
Apply values per media breakpoints. Breakpoints are similar to the breakpoints used in bootstrap css framework.

Apply values per media breakpoints. Breakpoints are similar to the breakpoints used in bootstrap css framework.

Glenford Williams 4 Mar 26, 2021
Flutter UI library based on IBM's Carbon Design System 💎

Flutter Carbon ‌Carbon is IBM’s open-source design system for products and experiences. With the IBM Design Language as its foundation, the system con

Nour El-Din Shobier 89 Jan 5, 2023
CartToGo is an IoT-based smart system that is connected to an iOS mobile application

CartToGo is an IoT-based smart system that is connected to an iOS mobile application, used by supermarket shoppers to reduce their waiting time in the queue lines and that’s done by enabling them to scan each product’s barcode to display its price and total price on the LCD display as well as the iOS mobile application.

CartToGo 5 Dec 15, 2022
Build a system that recommends jobs based on resume.

Job Findr Problem Statement: Build a system that recommends jobs based on resume. Problem Description: The problem statement suggests to build a syste

null 2 Jul 8, 2022
Sticky Grid View For Flutter

Features Listing images in gridview wrapped listview gives bad performance (disp

null 1 Nov 14, 2022
Gol grid - a cellular automata flutter widget

GolGrid Conway's Game of Life in a flutter widget. A GolGrid is a rendering of a GridWorld controlled by a Thumper. Example The example contains two a

Jeff Regan 4 Mar 25, 2022
Flutter package for displaying grid view of daily task like Github-Contributions.

flutter_annual_task flutter_annual_task Flutter package for displaying grid view of daily task like Github-Contributions. Example Usage Make sure to c

HuanSuh 24 Sep 21, 2022
Flutter widget that arrange buttons in a grid.

flutter_grid_button Flutter widget that arrange buttons in a grid. It is useful for making a number pad, calculator, and so on. Getting Started To use

zuvola 11 Jan 10, 2022
UTS Mobile Programming membuat Grid view

uts_181021450006 A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started

null 1 Mar 30, 2022
A grid that supports both dragging and tapping to select its items

drag_select_grid_view A grid that supports both dragging and tapping to select its items. Basic usage DragSelectGridView constructor is very similar t

Hugo Passos 121 Dec 11, 2022