TrinaGrid

repository·main·Indexed 41 days ago

https://github.com/doonfrs/trina_grid

A feature-rich data grid for Flutter designed for displaying, editing, and managing tabular data across web, desktop, and mobile platforms. As a successor to PlutoGrid, it includes an automatic migration tool and supports advanced features such as column freezing, resizing, pagination, custom cell renderers, and export options (CSV, JSON, PDF).

Tokens
116.7K
Snippets
306
Records
393
Agent score
47%

What's inside TrinaGrid

  1. Overview of TrinaGrid features

    main

    TrinaGrid is a comprehensive Flutter data grid with the following feature categories:

    Column Features

    • Types: Text, Number, Boolean, Selection (dropdown), Date, Time, DateTime, Currency, and Percentage.
    • Interaction: Freezing (left/right), Resizing (drag), Moving (drag and drop), Hiding, Sorting, and Filtering.
    • Layout: Column Groups, Column Footers (aggregates), and Viewport Visibility.
    • Customization: Column Renderers, Title Renderers, Menus, and Filter Icon customization.

    Row Features

    • Selection: Single or multiple row selection, and built-in checkbox selection.
    • Interaction: Row Moving (reorder), Row Groups, and Frozen Rows.
    • Appearance: Row Coloring and Row Wrappers.
    • Data Loading: Pagination (Client-side, Lazy/Server-side, and Infinite Scroll), and Asynchronous row addition.

    Cell Features

    • Interaction: Cell Selection (individual or ranges) and Cell Editing.
    • Customization: Cell Renderers, Edit Cell Renderers, and Cell Validation.
    • Data: Cell Value Change handling.

    Data Management & UI

    • Data: Copy & Paste, Export (CSV, JSON, PDF), and Lazy Loading.
    • UI: Themes (Light/Dark), Custom Styling, Enhanced Scrollbars (draggable/hover effects), Record Sidebar (searchable/editable panel), and RTL support.
    • Navigation: Keyboard navigation, Context Menus, and Dual Grid Mode.
  2. Performance considerations for variable row heights

    main

    Switching from uniform to variable row heights changes how the underlying list renders:

    • Uniform Heights: Uses ListView.builder with itemExtent, which is highly efficient for large datasets.
    • Variable Heights: Uses ListView.builder without itemExtent, requiring more complex scroll calculations and potentially reducing performance.

    Mitigation Strategies:

    • Set heights at creation time whenever possible.
    • Limit the number of variable-height rows.
    • For very large datasets, consider implementing lazy height calculation or keeping most rows at a uniform height.
  3. Understand Column Group Behavior (Movement, Hiding, Resizing)

    main

    Column Movement

    • If a column is moved outside its group, it is removed from that group.
    • If all columns are removed from a group, the group is automatically removed.
    • Moving columns between groups updates the group structure automatically.

    Column Hiding

    • If all columns in a group are hidden, the group is not displayed.
    • When a hidden column is shown again, it returns to its original group.

    Column Resizing

    • Resizing a column affects only that specific column.
    • The group width automatically adjusts to accommodate the total width of its constituent columns.
  4. How cell-level and column-level rendering work together

    main

    TrinaGrid supports two levels of rendering customization:

    1. Column-level rendering: Applied via TrinaColumn to all cells within that specific column.
    2. Cell-level rendering: Applied via the renderer property on a TrinaCell instance to customize a specific individual cell.

    Precedence Rule: When both are defined, the cell-level renderer takes precedence over the column-level renderer.

    // Column-level renderer (applies to all cells in the column)
    TrinaColumn(
      title: 'Status',
      field: 'status',
      type: TrinaColumnType.text(),
      renderer: (rendererContext) {
        return Text(rendererContext.cell.value.toString());
      },
    )
    
    // Cell-level renderer (applies only to this specific cell)
    TrinaCell(
      value: 'Completed',
      renderer: (rendererContext) {
        return Container(color: Colors.green, child: Text(rendererContext.cell.value.toString()));
      },
    )
  5. Configure Column Resize Modes

    main

    TrinaGrid supports three resizing behaviors via the TrinaResizeMode enum. You can set this in the TrinaGridConfiguration using the columnSize property.

    • TrinaResizeMode.none: Disables column resizing. Users cannot drag column edges.
    • TrinaResizeMode.normal: (Default) Resizing a column only changes its own width. Other columns remain unchanged, which may increase the total grid width.
    • TrinaResizeMode.pushAndPull: Resizing a column causes neighboring columns to adjust their widths to maintain the overall grid width. Expanding one column shrinks others, and vice versa.
    TrinaGrid(
      columns: columns,
      rows: rows,
      configuration: const TrinaGridConfiguration(
        columnSize: TrinaGridColumnSizeConfig(
          resizeMode: TrinaResizeMode.pushAndPull,
        ),
      ),
    )
  6. Use TrinaGridStateManager to control the grid

    main

    The TrinaGridStateManager provides methods to control the grid programmatically (e.g., for sorting, selection, or row manipulation). You should capture the instance from the onLoaded event of the TrinaGrid widget.

    late TrinaGridStateManager stateManager;
    
    // In your TrinaGrid widget:
    onLoaded: (TrinaGridOnLoadedEvent event) {
      stateManager = event.stateManager;
    },
  7. Control loading indicator coverage with TrinaGridLoadingLevel

    main

    You can specify how much of the grid is covered by the loading indicator using the TrinaGridLoadingLevel enum. This allows you to target specific areas of the grid during asynchronous operations.

    • TrinaGridLoadingLevel.grid: Covers the entire grid area (default).
    • TrinaGridLoadingLevel.body: Covers only the body area of the grid.
    • TrinaGridLoadingLevel.row: Covers only the row area of the grid.
  8. Handle server-side sorting and filtering in Lazy Pagination

    main

    When using lazy pagination, the TrinaLazyPaginationRequest object provides the current state of sorting and filtering, allowing you to apply these to your server-side queries.

    Sorting: Check request.sortColumn. If it is not null and the sort is not isNone, apply the sort to your API call.

    Filtering: Use request.filterRows to apply filters. You can use FilterHelper to convert these rows into more usable formats:

    • FilterHelper.convertRowsToMap(request.filterRows)
    • FilterHelper.convertRowsToFilter(request.filterRows, stateManager.refColumns)

    Note: When sorting or filtering changes, the pagination automatically resets to page 1 to maintain data consistency.

    // Sorting
    if (request.sortColumn != null && !request.sortColumn!.sort.isNone) {
      // Apply server-side sorting based on sortColumn
    }
    
    // Filtering
    if (request.filterRows.isNotEmpty) {
      final filterMap = FilterHelper.convertRowsToMap(request.filterRows);
      // or
      final filter = FilterHelper.convertRowsToFilter(
        request.filterRows,
        stateManager.refColumns,
      );
    }
  9. How context menus interact with other TrinaGrid features

    main

    Context menus in TrinaGrid are integrated with several core column management features. Depending on your configuration, the context menu provides direct access to:

    • Column Freezing: Options to freeze or unfreeze columns.
    • Column Resizing: If both enableContextMenu and enableDropToResize are enabled, the context menu icon can be used as a handle to drag and resize columns.
    • Column Filtering: Access to filtering options (requires enableFilterMenuItem to be true).
    • Column Visibility: Options to hide columns or manage overall column visibility.
  10. Styling TrinaGrid with different theme approaches

    main

    TrinaGrid provides three primary ways to apply styling, which can be combined using the copyWith method:

    1. Built-in Palettes: Use TrinaGridConfiguration() for a light theme or TrinaGridConfiguration.dark() for a dark theme. These use fixed colors that do not change with your app's theme.
    2. Material Theme Integration: Use TrinaGridConfiguration.fromTheme(context) to derive grid colors from your application's ColorScheme. This automatically handles light/dark mode based on ColorScheme.brightness.
    3. Manual Styling: Define specific values for the fields in TrinaGridStyleConfig manually.

    Important: When using fromTheme(context), you must call it inside the build method. It cannot be const because it reads the ambient theme, and calling it in initState will prevent the grid from updating when the theme changes.

    // 1. Built-in palettes
    configuration: const TrinaGridConfiguration.dark(),
    
    // 2. Material theme integration
    configuration: TrinaGridConfiguration.fromTheme(context),
    
    // 3. Manual styling
    configuration: TrinaGridConfiguration(style: TrinaGridStyleConfig(rowHeight: 50)),
  11. Alternative: Use hidden columns for data storage

    main

    Instead of using TrinaRow.data, you can store metadata in columns that are not visible to the user by setting the hide property to true in TrinaColumn.

    Pros/Cons:

    • Hidden Columns: Data is stored within the standard row.cells structure and is part of the grid's cell model. Accessible via row.cells['column_id'].
    • TrinaRow.data: Cleaner for complex objects or metadata that doesn't map to a specific grid column.
    TrinaColumn(
      title: 'Original Name',
      field: 'original_name',
      type: TrinaColumnType.text(),
      hide: true,  // Column won't be rendered
    )
  12. Column freezing limitations and safeguards

    main

    TrinaGrid includes built-in logic to ensure the grid remains usable when freezing columns:

    1. Space Preservation: The grid ensures there is always enough space for non-frozen (scrollable) columns to be visible.
    2. Automatic Unfreezing: If the width of the middle (non-frozen) columns becomes too narrow due to frozen columns taking up space, the frozen columns will be automatically released (unfrozen).
    3. Validation: The limitToggleFrozenColumn method is used internally to check if there is sufficient space before allowing a new column to be frozen.