This document explains how the grid works on the Dart side. Also, it can be a development guide when you want to be a grid contributor. This document will be continuously updated, and any suggestions would be helpful.


Below you will find some quick definitions to help you read through the document.

Cache classes

Aim to reduce the time cost of getting data from the backend.


A Cell is one individual cell in a grid. You can see more in the Cell section.


A column is a theoretical representation of data, however, there is no Column class.


A Field represents the configuration of a column. You can see more in the Field section.


A Grid type is a simple representation of items placed in columns and rows. It is not a spreadsheet. You can see more in the Grid section


A Row represents a group of related data

Classes with a PB suffix are generated in protobuf format. You could check the Events document out if you are interested in how the protobuf classes are generated.


At its core, a Grid type is a simple representation of items placed in columns and rows. It is not a spreadsheet.

Another name for a column is Field. A column's configuration is defined in the Field class. It is important to note though that although a grid has the concept of columns, there are no actual Column classes.

A user can add a Row, and then define the data in each of the cells created for the Grid's Fields in that row.


When you open a grid, a GridBloc will be initialized. There are four cache classes, as shown in the diagram above.

  1. GridRowCache

    • Caches the grid's Rows in memory.

    • A Row contains many Cells, each Cellwill be cached in theGridCellCache`.

    • Allows to insert/delete/update Rows.

  2. GridCellCache

    • Caches each Cell by GridCellCacheKey in memory.

    • Allows to remove/insert Cells.



A Field represents a column's configuration. It will contain the column's name, id, width, type (as defined in FieldType), etc.


A Field has a FieldType. The FieldType defines the kind of data contained in a column. For example, a column may contain dates, numbers, text, multi-select list, etc.

Certain field types have user-defined options such as color, date format, number format, or a list of values for a multi-select list. These options are defined within a specialization of the FieldTypeOption class.

Each field has its TypeOptionBuilder in the backend that is used to parse the bytes into corresponding FieldTypeOption.



A FieldEditor is a widget that is used to edit the field's shared properties. Such as the name of the field, etc. It uses the FieldTypeOptionEditor to customize the UI for each field.


FieldEditorBloc uses a TypeOptionDataController to listen for changes to a Field or perform a rename operation. It will notify the widget to rebuild if its state has changed.


Defines how to update a Field's properties. Such as the name, the field, and the type option data.


Defines how to load a Field's type option data. For example, when we create a new Field, we use NewTypeOptionLoader. We use FieldTypeOptionLoader to load the existing Field's type option data.


FieldTypeOptionEditor is a widget that provides a custom UI for each Field. You can provide a custom UI by extending the TypeOptionWidgetBuilder As the image below shows, we have many TypeOptionWidgetBuilder implementations.

The widget returned by TypeOptionWidgetBuilder use TypeOptionWidgetContext as its data model. For example, DateTypeOptionWidget uses DateTypeOptionContext that extends the TypeOptionWidgetContext.

TypeOptionWidgetContext uses TypeOptionDataParser to parse the generic data, List, to specific data type. As the image below shows, each TypeOptionContext must have a corresponding TypeOptionDataParser.


A Row represents a group of related Cells.


RowService handles up the logic to interact with the backend. It allows creating, duplicating, deleting, and moving the row operations.


Caching the rows in memory to reduce the cost of getting data from the backend. (as defined in the Cache)


  • A Row has a list of Cells. It uses the GridCellBuilder to build the custom Cell according to the field type. Each cell should extend the GridCellWidget interface.


A Cell is one individual cell in a grid. The number of Cells in a Row is equal to the number of Fields in the Grid. We define the GridCellWidget that defines the shared behaviors. Such as CellAccessory, CellEditable, and CellShortcuts.

Let's look at the select GridSingleSelectCell and find out how it works. When a user clicks a cell, the SelectOptionCellEditor will show up.

  • SelectOptionCellEditor is a widget that defines the UI when editing the cell.

  • SelectOptionCellEditorBloc binds the UI and the data, the SelectOptionCellEditor will be rebuilt if the bloc state changes.

  • SelectOptionService handles up the logic for deleting, updating the select option with the backend.

  • GridSelectOptionCellController use GridCellController to implement the cell's operations.


  • Allows getting Read/Write cell data.

  • Listens to the cell date change.

  • Allows getting the corresponding field type option data that is parsed by the TypeOptionDataParser.

  • Listens to the field event and loads the cell data if needed. For example, the numbered cell should reload when the number format is changed.


Allow getting the cell data and then parsing into a specific type.


The implementation of GridCellDataParser will parse the List<int> into specific cell data. For example, the SelectOptionCellDataParser will parse the List into SelectOptionCellDataPB.


We can use CellDataPersistence that implements the GridCellDataPersistence to perform normal save operation.

Also, implement the GridCellDataPersistenceto provide custom data saving operation. Just like the DateCellDataPersistence does.


Handling the logic for reading and writing the cell data with the backend.

Last updated