Column drag and drop in Flutter Data Grid

18 Nov 201818 minutes to read

The SfDataGrid enables columns to be dragged and dropped by setting the SfDataGrid.allowColumnsDragging property to true and returning true from the SfDataGrid.onColumnDragging callback. During the column-dragging process, a drag feedback widget is displayed. By utilizing the SfDataGrid.onColumnDragging callback, you can handle drag and drop operations to reorder columns dynamically.

The DataGrid provides the rearranged index of the dragged column, indicating its new position after being dropped. Inside the onColumnDragging callback, you can utilize this index to reorder the columns to the desired position. This allows you to handle the column reordering directly within the callback at the sample level.

Prerequisites

The following code example demonstrates the basic setup required for column drag and drop functionality:

import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart';

class Employee {
  Employee(this.id, this.name, this.designation, this.salary);
  final int id;
  final String name;
  final String designation;
  final int salary;

  dynamic operator [](String key) {
    switch (key) {
      case 'id':
        return id;
      case 'name':
        return name;
      case 'designation':
        return designation;
      case 'salary':
        return salary;
      default:
        return '';
    }
  }
}

Enable column drag and drop

The following code example shows how to enable column drag and drop functionality:

List<Employee> employees = <Employee>[];
  late List<GridColumn> columns;
  late EmployeeDataSource employeeDataSource;

  @override
  void initState() {
    super.initState();

    columns = getColumns;
    employees = getEmployeeData();
    employeeDataSource = EmployeeDataSource(
      employees: employees,
      columns: columns,
    );
  }

  List<GridColumn> get getColumns {
    return <GridColumn>[
      GridColumn(
        columnName: 'id',
        label: Container(
          padding: const EdgeInsets.all(16.0),
          alignment: Alignment.center,
          child: const Text('ID'),
        ),
      ),
      GridColumn(
        columnName: 'name',
        label: Container(
          padding: const EdgeInsets.all(8.0),
          alignment: Alignment.center,
          child: const Text('Name'),
        ),
      ),
      GridColumn(
        columnName: 'designation',
        label: Container(
          padding: const EdgeInsets.all(8.0),
          alignment: Alignment.center,
          child: const Text('Designation', overflow: TextOverflow.ellipsis),
        ),
      ),
      GridColumn(
        columnName: 'salary',
        label: Container(
          padding: const EdgeInsets.all(8.0),
          alignment: Alignment.center,
          child: const Text('Salary'),
        ),
      ),
    ];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Syncfusion Flutter Data Grid')),
      body: SfDataGrid(
        source: employeeDataSource,
        columnWidthMode: ColumnWidthMode.fill,
        allowColumnsDragging: true,
        columns: columns,
        onColumnDragging: (DataGridColumnDragDetails details) {
          if (details.action == DataGridColumnDragAction.dropped &&
              details.to != null) {
            final GridColumn rearrangeColumn = columns[details.from];
            columns.removeAt(details.from);
            columns.insert(details.to!, rearrangeColumn);
            employeeDataSource.buildDataGridRows();
            employeeDataSource.refreshDataGrid();
          }
          return true;
        },
      ),
    );
  }

class EmployeeDataSource extends DataGridSource {
  EmployeeDataSource({
    required List<Employee> employees,
    required this.columns,
  }) {
    _employees = employees;
    buildDataGridRows();
  }

  late List<Employee> _employees;
  late List<GridColumn> columns;
  late List<DataGridRow> dataGridRows;

  void buildDataGridRows() {
    dataGridRows = _employees.map<DataGridRow>((employee) {
      return DataGridRow(
        cells: columns.map<DataGridCell>((column) {
          return DataGridCell(
            columnName: column.columnName,
            value: employee[column.columnName],
          );
        }).toList(),
      );
    }).toList();
  }

  @override
  List<DataGridRow> get rows => dataGridRows;

  @override
  DataGridRowAdapter? buildRow(DataGridRow row) {
    return DataGridRowAdapter(
      cells: row.getCells().map<Widget>((dataGridCell) {
        return Container(
          alignment: Alignment.center,
          padding: const EdgeInsets.symmetric(horizontal: 8.0),
          child: Text(dataGridCell.value.toString()),
        );
      }).toList(),
    );
  }

  void refreshDataGrid() {
    notifyListeners();
  }
}

Flutter Data Grid shows a checkbox filter in web platform

Note:

  • To reorder the columns in the DataGrid, create an instance variable to hold the columns and then assign that instance to the SfDataGrid.columns property instead of directly assigning a list literal. This allows you to reorder the collection within the callback, maintaining the desired column order.
  • After reordering columns, rebuild the rows based on the updated columns collection by calling buildDataGridRows() and notifyListeners(). This is necessary because the column index may change after reordering. By rebuilding the rows, you ensure that the row data aligns correctly with the reordered columns.
  • Download the complete sample application from GitHub.

onColumnDragging callback

The SfDataGrid.onColumnDragging callback is triggered during column drag operations. This callback provides the following properties in the DataGridColumnDragDetails:

  • from: The index of the currently dragging column. Use this to identify which column is being moved.
  • to: The index where the column will be dropped. This is null during drag updates and populated when the column is dropped.
  • action: Indicates the drag action as a DataGridColumnDragAction enum. The update action fires while dragging, and dropped fires when the column is released.
  • offset: The current offset of the dragging column during the drag operation. Use this to calculate the drag position if needed.

Cancel the column dropping for a specific column

You can cancel the column dropping at a specific target column by returning false from the SfDataGrid.onColumnDragging callback. This allows you to prevent columns from being dropped at certain positions or restrict specific columns from being dropped.

In the following example, columns cannot be dropped at index 2 (the third column position):

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Syncfusion Flutter Data Grid')),
      body: SfDataGrid(
        source: employeeDataSource,
        allowColumnsDragging: true,
        columns: columns,
        onColumnDragging: (DataGridColumnDragDetails details) {
          // Prevent dropping at column index 2
          if (details.action == DataGridColumnDragAction.update &&
              details.to == 2) {
            return false;
          }
          // Handle the drop action
          if (details.action == DataGridColumnDragAction.dropped &&
              details.to != null &&
              details.from >= 0 &&
              details.from < columns.length) {
            final GridColumn rearrangeColumn = columns[details.from];
            columns.removeAt(details.from);
            columns.insert(details.to!, rearrangeColumn);
            employeeDataSource.buildDataGridRows();
            employeeDataSource.notifyListeners();
          }
          return true;
        },
      ),
    );
  }

Changing the feedback widget

The DataGrid allows you to customize the feedback widget displayed during column dragging by using the SfDataGrid.columnDragFeedbackBuilder builder. This builder is called during drag operations and allows you to return a custom widget that represents the dragged column.

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Syncfusion Flutter Data Grid')),
      body: SfDataGrid(
        source: employeeDataSource,
        allowColumnsDragging: true,
        columns: columns,
        columnDragFeedbackBuilder: (BuildContext context, GridColumn column) {
          return Container(
            height: 50,
            width: column.actualWidth,
            color: Colors.teal[400],
            child: const Center(
              child: DefaultTextStyle(
                style: TextStyle(
                  fontSize: 14,
                  color: Colors.yellow,
                  fontWeight: FontWeight.bold,
                ),
                child: Text('Drag View'),
              ),
            ),
          );
        },
        onColumnDragging: (DataGridColumnDragDetails details) {
          if (details.action == DataGridColumnDragAction.dropped &&
              details.to != null &&
              details.from >= 0 &&
              details.from < columns.length) {
            final GridColumn rearrangeColumn = columns[details.from];
            columns.removeAt(details.from);
            columns.insert(details.to!, rearrangeColumn);
            employeeDataSource.buildDataGridRows();
            employeeDataSource.notifyListeners();
          }
          return true;
        },
      ),
    );
  }

Flutter Data Grid shows a checkbox filter in web platform

Drag indicator customization

The color and thickness of the drag indicator displayed during column dragging can be customized using the SfDataGridThemeData.columnDragIndicatorColor and SfDataGridThemeData.columnDragIndicatorStrokeWidth properties. These properties are available in the syncfusion_flutter_core package through the SfDataGridTheme widget.

Note:

  • The SfDataGridThemeData and SfDataGridTheme classes are available in the syncfusion_flutter_core package.
  • Import the theme package: import 'package:syncfusion_flutter_core/theme.dart';

The following code demonstrates how to customize the drag indicator appearance:

import 'package:syncfusion_flutter_core/theme.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Syncfusion Flutter Data Grid')),
      body: SfDataGridTheme(
        data: SfDataGridThemeData(
          columnDragIndicatorColor: Colors.pink,
          columnDragIndicatorStrokeWidth: 3,
        ),
        child: SfDataGrid(
          source: employeeDataSource,
          allowColumnsDragging: true,
          columns: columns,
          onColumnDragging: (DataGridColumnDragDetails details) {
            if (details.action == DataGridColumnDragAction.dropped &&
                details.to != null &&
                details.from >= 0 &&
                details.from < columns.length) {
              final GridColumn rearrangeColumn = columns[details.from];
              columns.removeAt(details.from);
              columns.insert(details.to!, rearrangeColumn);
              employeeDataSource.buildDataGridRows();
              employeeDataSource.notifyListeners();
            }
            return true;
          },
        ),
      ),
    );
  }

Flutter Data Grid shows a checkbox filter in web platform