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] DataGridSource was used after being disposed with SfDataPager #1803

Closed Trung15010802 closed 2 weeks ago

Trung15010802 commented 1 month ago

If I dispose a DataGridSource with SfDataPager (to avoid memory leak). I got this error:

══╡ EXCEPTION CAUGHT BY SCHEDULER LIBRARY ╞═════════════════════════════════════════════════════════
The following assertion was thrown during a scheduler callback:
A EmployeeDataSource was used after being disposed.
Once you have called dispose() on a EmployeeDataSource, it can no longer be used.

When the exception was thrown, this was the stack:
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 297:3       throw_
packages/flutter/src/foundation/change_notifier.dart 179:9                        <fn>
packages/flutter/src/foundation/change_notifier.dart 185:14                       debugAssertNotDisposed
packages/flutter/src/foundation/change_notifier.dart 412:27                       notifyListeners
packages/syncfusion_flutter_datagrid/src/datagrid_widget/sfdatagrid.dart 4843:11  notifyListeners
packages/syncfusion_flutter_datagrid/src/datagrid_widget/sfdatagrid.dart 4921:13  <fn>
packages/flutter/src/scheduler/binding.dart 1386:7                                [_invokeFrameCallback]
packages/flutter/src/scheduler/binding.dart 1322:11                               handleDrawFrame
packages/flutter/src/scheduler/binding.dart 1169:5                                [_handleDrawFrame]
lib/_engine/engine/platform_dispatcher.dart 1346:5                                invoke
lib/_engine/engine/platform_dispatcher.dart 260:5                                 invokeOnDrawFrame
lib/_engine/engine/initialization.dart 185:36                                     <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 550:37  _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 555:39  dcall

This is my code:


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

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

class _MyHomePageState extends ConsumerState<MyHomePage> {
  int _currentIndex = 0;

  @override
  Widget build(BuildContext context) {
    final List<Widget> list = [const TableWithPaper(), const Placeholder()];

    return DefaultTabController(
      length: 2,
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: const Text('Home page'),
          bottom: TabBar(
            onTap: (value) {
              setState(() {
                _currentIndex = value;
              });
            },
            tabs: const [
              Tab(
                text: '1',
              ),
              Tab(
                text: '2',
              ),
            ],
          ),
        ),
        body: list[_currentIndex],
      ),
    );
  }
}

/// The home page of the application which hosts the datagrid.
class TableWithPaper extends StatefulWidget {
  /// Creates the home page.
  const TableWithPaper({Key? key}) : super(key: key);

  @override
  TableWithPaperState createState() => TableWithPaperState();
}

class TableWithPaperState extends State<TableWithPaper> {
  List<Employee> employees = <Employee>[];
  late EmployeeDataSource employeeDataSource;

  @override
  void initState() {
    super.initState();
    employees = getEmployeeData();
    employeeDataSource = EmployeeDataSource(employeeData: employees);
  }

  @override
  void dispose() {
    employeeDataSource.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Syncfusion Flutter DataGrid'),
      ),
      body: Column(
        children: [
          Expanded(
            child: SfDataGrid(
              source: employeeDataSource,
              columnWidthMode: ColumnWidthMode.fill,
              rowsPerPage: 10,
              columns: <GridColumn>[
                GridColumn(
                    columnName: 'id',
                    label: Container(
                        padding: EdgeInsets.all(16.0),
                        alignment: Alignment.center,
                        child: Text(
                          'ID',
                        ))),
                GridColumn(
                    columnName: 'name',
                    label: Container(
                        padding: EdgeInsets.all(8.0),
                        alignment: Alignment.center,
                        child: Text('Name'))),
                GridColumn(
                    columnName: 'designation',
                    label: Container(
                        padding: EdgeInsets.all(8.0),
                        alignment: Alignment.center,
                        child: Text(
                          'Designation',
                          overflow: TextOverflow.ellipsis,
                        ))),
                GridColumn(
                    columnName: 'salary',
                    label: Container(
                        padding: EdgeInsets.all(8.0),
                        alignment: Alignment.center,
                        child: Text('Salary'))),
              ],
            ),
          ),
          SfDataPager(
            delegate: employeeDataSource,
            pageCount: employeeDataSource._employeeData.length / 10,
            direction: Axis.horizontal,
          )
        ],
      ),
    );
  }

  List<Employee> getEmployeeData() {
    return [
      Employee(10001, 'James', 'Project Lead', 20000),
      Employee(10002, 'Kathryn', 'Manager', 30000),
      Employee(10003, 'Lara', 'Developer', 15000),
      Employee(10004, 'Michael', 'Designer', 15000),
      Employee(10005, 'Martin', 'Developer', 15000),
      Employee(10006, 'Newberry', 'Developer', 15000),
      Employee(10007, 'Balnc', 'Developer', 15000),
      Employee(10008, 'Perry', 'Developer', 15000),
      Employee(10009, 'Gable', 'Developer', 15000),
      Employee(10010, 'Grimes', 'Developer', 15000)
    ];
  }
}

/// Custom business object class which contains properties to hold the detailed
/// information about the employee which will be rendered in datagrid.
class Employee {
  /// Creates the employee class with required details.
  Employee(this.id, this.name, this.designation, this.salary);

  /// Id of an employee.
  final int id;

  /// Name of an employee.
  final String name;

  /// Designation of an employee.
  final String designation;

  /// Salary of an employee.
  final int salary;
}

/// An object to set the employee collection data source to the datagrid. This
/// is used to map the employee data to the datagrid widget.
class EmployeeDataSource extends DataGridSource {
  /// Creates the employee data source class with required details.
  EmployeeDataSource({required List<Employee> employeeData}) {
    _employeeData = employeeData
        .map<DataGridRow>((e) => DataGridRow(cells: [
              DataGridCell<int>(columnName: 'id', value: e.id),
              DataGridCell<String>(columnName: 'name', value: e.name),
              DataGridCell<String>(
                  columnName: 'designation', value: e.designation),
              DataGridCell<int>(columnName: 'salary', value: e.salary),
            ]))
        .toList();
  }

  List<DataGridRow> _employeeData = [];

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

  @override
  DataGridRowAdapter buildRow(DataGridRow row) {
    return DataGridRowAdapter(
        cells: row.getCells().map<Widget>((e) {
      return Container(
        alignment: Alignment.center,
        padding: EdgeInsets.all(8.0),
        child: Text(e.value.toString()),
      );
    }).toList());
  }
}
abineshPalanisamy commented 1 month ago

Hi @Trung15010802

Thank you for providing the sample. Following our testing, we have confirmed that this is indeed a bug. A bug report has been logged in our feedback portal to address this issue. We are committed to resolving the reported issue and will incorporate the necessary changes in the upcoming weekly patch release, scheduled for rollout on April 30, 2024. We will let you know once released. We appreciate your patience and understanding until then.

Regards, Abinesh P

abineshPalanisamy commented 2 weeks ago

Hi @Trung15010802 ,

We are glad to inform you that the reported problem has been resolved on our end. Therefore, kindly update SfDataGrid to the latest version(25.1.42). If you have any further queries, please don't hesitate to reach out. We are more than happy to assist you.

Regards, Abinesh P