syncfusion / flutter-widgets

Syncfusion Flutter widgets libraries include high quality UI widgets and file-format packages to help you create rich, high-quality applications for iOS, Android, and web from a single code base.
1.44k stars 672 forks source link

[syncfusion_flutter_datagrid] How to make sense of row index in buildGroupCaptionCellWidget? #1838

Open divan opened 2 weeks ago

divan commented 2 weeks ago

Hi,

I'm trying to use the new wonderful row grouping feature, and I use custom group caption widget using buildGroupCaptionCellWidget in my data source class. I want to display some of the common properties of the grouped rows, so need access to the actual data object of grouped rows.

 @override
  Widget? buildGroupCaptionCellWidget(
      RowColumnIndex rowColumnIndex, String summaryValue) {
    final idx = rowColumnIndex.rowIndex;
    print('> rowIndex is $idx');
    T? item = myData.someHowGetActualItem(idx);

    return Container(
        padding: EdgeInsets.symmetric(horizontal: 12, vertical: 15),
        child: MyWidget(item),
    );
  }

My understanding is that I can rely on rowIndex to find the actual row and the object it represents. On my test data I have 3 items (assuming they being grouped by propery 'Group').

This displayed correctly by data grid as:

Caption for Group 1

Caption for Group 2

But, rowIndex values seems to be misaligned with underlying data and based on the actual number of widgets/rows displayed.:

rowIndex is 0 rowIndex is 4

So my question is, how to translate rowIndex to the actual row? Unless I'm missing something obvious, isn't it the whole point of rowColumnIndex to help identify the data, so the proper caption header widget can be built?

Thanks in advance for replies!

abineshPalanisamy commented 2 weeks ago

Hi @divan ,

Based on the provided details, the rowColumnIndex obtained from DataGridSource.buildGroupCaptionCellWidget represents solely the row and column index of the caption summary row. Whenever the caption summary row is expanded or collapsed, the rowColumnIndex changes according to the visible rows. This rowColumnIndex is derived from the internal grouped collection.

When using this rowColumnIndex to access the actual data object within grouped rows, we kindly request additional clarification on the expected behavior, along with clear examples and the necessary scenarios from your end. This will significantly aid us in further investigating and promptly providing an appropriate solution.

Regards, Abinesh P

divan commented 2 weeks ago

@abineshPalanisamy thank you for prompt reply! Here is a quick example. I put some explanation in the buildGroupCaptionCellWidget function comments. My real app case is a bit different (I need to display custom widget for custom type of the common group field), but this example is showing the point.

Currently this code throws an exception, because rowIndex indeed points to 4th internal grouped row, and data has only 3 items. What I'm looking for is to the way to get to single/any/all objects from actual data for the given group.

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'DataGrid Group Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

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

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late EmployeeDataSource _employeeDataSource;

  List<Employee> _employees = <Employee>[];

  @override
  void initState() {
    super.initState();
    _employees = getEmployeeData();
    _employeeDataSource = EmployeeDataSource(employees: _employees);
    _employeeDataSource
        .addColumnGroup(ColumnGroup(name: 'designation', sortGroupRows: true));
  }

  List<Employee> getEmployeeData() {
    return [
      Employee(10003, 'Lara', 'Designer', 15000),
      Employee(10004, 'Michael', 'Designer', 15000),
      Employee(10001, 'James', 'Project Lead', 20000),
    ];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SfDataGrid(
        source: _employeeDataSource,
        columnWidthMode: ColumnWidthMode.fill,
        columns: [
          GridColumn(
              columnName: 'id',
              label: Container(
                  padding: const EdgeInsets.symmetric(horizontal: 16.0),
                  alignment: Alignment.centerRight,
                  child: const Text(
                    'ID',
                    overflow: TextOverflow.ellipsis,
                  ))),
          GridColumn(
              columnName: 'name',
              label: Container(
                  padding: const EdgeInsets.symmetric(horizontal: 16.0),
                  alignment: Alignment.centerLeft,
                  child: const Text(
                    'Name',
                    overflow: TextOverflow.ellipsis,
                  ))),
          GridColumn(
              columnName: 'designation',
              label: Container(
                  padding: const EdgeInsets.symmetric(horizontal: 16.0),
                  alignment: Alignment.centerLeft,
                  child: const Text(
                    'Designation',
                    overflow: TextOverflow.ellipsis,
                  ))),
          GridColumn(
              columnName: 'salary',
              label: Container(
                  padding: const EdgeInsets.symmetric(horizontal: 16.0),
                  alignment: Alignment.centerRight,
                  child: const Text(
                    'Salary',
                    overflow: TextOverflow.ellipsis,
                  ))),
        ],
      ),
    );
  }
}

class EmployeeDataSource extends DataGridSource {
  List<Employee> employees = [];
  EmployeeDataSource({required this.employees}) {
    dataGridRows = employees
        .map<DataGridRow>((dataGridRow) => DataGridRow(cells: [
              DataGridCell<int>(columnName: 'id', value: dataGridRow.id),
              DataGridCell<String>(columnName: 'name', value: dataGridRow.name),
              DataGridCell<String>(
                  columnName: 'designation', value: dataGridRow.designation),
              DataGridCell<int>(
                  columnName: 'salary', value: dataGridRow.salary),
            ]))
        .toList();
  }

  List<DataGridRow> dataGridRows = [];

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

  @override
  DataGridRowAdapter? buildRow(DataGridRow row) {
    return DataGridRowAdapter(
        cells: row.getCells().map<Widget>((dataGridCell) {
      return Container(
          alignment: (dataGridCell.columnName == 'id' ||
                  dataGridCell.columnName == 'salary')
              ? Alignment.centerRight
              : Alignment.centerLeft,
          padding: const EdgeInsets.symmetric(horizontal: 16.0),
          child: Text(
            dataGridCell.value.toString(),
            overflow: TextOverflow.ellipsis,
          ));
    }).toList());
  }

  @override
  Widget? buildGroupCaptionCellWidget(
      RowColumnIndex rowColumnIndex, String summaryValue) {
    // get the Employee object of the first person for this group
    // to find a salary
    final idx = rowColumnIndex.rowIndex;
    print('> rowIndex is $idx');
    Employee employee = employees[idx];
    String text = 'Salary of the first person is ${employee.salary}';

    return Container(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 15),
        child: Text(
          text,
        ));
  }
}

Output is:

flutter: > rowIndex is 4
Another exception was thrown: RangeError (index): Invalid value: Not in inclusive range 0..2: 4
divan commented 2 weeks ago

I guess one of the solution might be to use {{Key}} as a summary, and then manually search my underlying data by this value. What I don't like about this approach is the need to convert to/from String, as `summaryString' is a string only...

abineshPalanisamy commented 2 weeks ago

Hi @divan ,

As of now, We have already considered your request to support for accessing the grouped rows in the DataGridSource.buildGroupCaptionCellWidget function as a feature. We will implement this feature in any of our upcoming releases. At the planning stage for every release cycle, we review all open features and identify features for implementation based on specific parameters including product vision, technological feasibility, and customer interest. We appreciate your patience and understanding until then. You can follow up with the below feedback for further details,

Feedback link: 50278

Regards, Abinesh P