Search code examples
flutterdatagridsyncfusionflutter-getx

State manager is rebuilding widget continuously instead of on update, how to eliminate this? Flutter


So I'm having trouble sorting out how to dynamically add data to my DataGrid. I'm getting help from Syncfusion support who have been phenomenal so far. However I still haven't been able to get full and proper functionality.

I am using GetX GetBuilder to wrap the DataGrid. It receives data from the database and should be updating this data when the database is added to. However it is currently running continuously, or say 50X per second. This is obviously not good in many ways. However I currently have full functionality but it's not proper.

I threw a print statement into the GridSource constructor and it prints continuously. So something is wrong and I can't figure it out. I have tried moving the Builder around (out of the builder) as well as the dataSource declaration however this kills the function of the grid loading at all.

Here is the full demo project here

Can someone please point out what I'm doing wrong?

DataGrid screen:

import 'package:flutter/material.dart';
import 'package:getx_hive_demo_one/presentation/event_list_screen/add_event_button.dart';
import 'package:get/get.dart';
import 'package:getx_hive_demo_one/database/database.dart';
import 'package:getx_hive_demo_one/models/event.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart';

class EventListScreen extends StatelessWidget {
  final Database database = Get.put(Database());

  GridSource dataSource;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Event Dashboard'),
          backgroundColor: Colors.grey[850],
          actions: [AddEventButton(source: dataSource)],
        ),
        body: SafeArea(child: GetBuilder<Database>(
          builder: (database) {
            dataSource = GridSource(database.getEventList());
            return SfDataGrid(
              source: dataSource,
              columnWidthMode: ColumnWidthMode.fill,
              columns: <GridColumn>[
                GridTextColumn(
                    columnName: 'name',
                    label: Container(
                        color: Colors.grey[800],
                        padding: EdgeInsets.all(8.0),
                        alignment: Alignment.centerLeft,
                        child: Text(
                          'Event',
                          style: TextStyle(color: Colors.white),
                        ))),
                GridTextColumn(
                    columnName: 'start',
                    label: Container(
                        color: Colors.grey[800],
                        padding: EdgeInsets.all(8.0),
                        alignment: Alignment.centerLeft,
                        child: Text(
                          'Start Date',
                          style: TextStyle(color: Colors.white),
                        ))),
                GridTextColumn(
                    columnName: 'duration',
                    label: Container(
                        color: Colors.grey[800],
                        padding: EdgeInsets.all(8.0),
                        alignment: Alignment.centerLeft,
                        child: Text(
                          'Event Duration',
                          style: TextStyle(color: Colors.white),
                          overflow: TextOverflow.ellipsis,
                        ))),
                GridTextColumn(
                    columnName: 'invitees',
                    label: Container(
                        color: Colors.grey[800],
                        padding: EdgeInsets.all(8.0),
                        alignment: Alignment.centerLeft,
                        child: Text(
                          'Attendees',
                          style: TextStyle(color: Colors.white),
                        ))),
                GridTextColumn(
                    columnName: 'location',
                    label: Container(
                        color: Colors.grey[800],
                        padding: EdgeInsets.all(8.0),
                        alignment: Alignment.centerLeft,
                        child: Text(
                          'Location',
                          style: TextStyle(color: Colors.white),
                        ))),
              ],
            );
          },
        )));
  }
}

class GridSource extends DataGridSource {
  List<DataGridRow> _eventGridRow = [];
  List<Event> eventGrid = [];

  GridSource(this.eventGrid) {
    print('Run');
    buildDataGridRows();
  }

  void buildDataGridRows() {
    _eventGridRow = eventGrid
        .map<DataGridRow>(
          (event) => DataGridRow(
            cells: [
              DataGridCell<String>(columnName: 'name', value: event.eventName),
              DataGridCell<String>(columnName: 'start', value: event.eventStart.toString()),
              DataGridCell<String>(columnName: 'duration', value: event.eventDuration.toString()),
              DataGridCell<String>(columnName: 'invitees', value: event.numberInvitees.toString()),
              DataGridCell<String>(columnName: 'location', value: event.location),
            ],
          ),
        )
        .toList();
  }

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

  @override
  DataGridRowAdapter buildRow(DataGridRow row) {
    final int index = _eventGridRow.indexOf(row);
    Color getRowBackgroundColor() {
      if (index % 2 == 0) {
        return Colors.grey[100];
      }
      return Colors.transparent;
    }

    return DataGridRowAdapter(
      color: getRowBackgroundColor(),
      cells: [
        Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.all(8.0),
          child: Text(
            row.getCells()[0].value ?? '',
            overflow: TextOverflow.ellipsis,
          ),
        ),
        Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.all(8.0),
          child: Text(
            row.getCells()[1].value ?? '',
            overflow: TextOverflow.ellipsis,
          ),
        ),
        Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.all(8.0),
          child: Text(
            row.getCells()[2].value ?? '',
            overflow: TextOverflow.ellipsis,
          ),
        ),
        Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.all(8.0),
          child: Text(
            row.getCells()[3].value ?? ' ',
            overflow: TextOverflow.ellipsis,
          ),
        ),
        Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.all(8.0),
          child: Text(
            row.getCells()[4].value ?? '',
            overflow: TextOverflow.ellipsis,
          ),
        ),
      ],
    );
  }

  void updateDataGridSource() {
    notifyListeners();
  }
}

Continuous print statement:

flutter: Run
flutter: Run
flutter: Run
flutter: Run
flutter: Run
flutter: Run
flutter: Run
flutter: Run
flutter: Run
flutter: Run
flutter: Run
flutter: Run
flutter: Run

Solution

  • Issue is cause due to creating the GridSource every time and also inside buildDataRow your are calling the getEventList and refreshing too, so its looping. Please replace the DataBase & DataGrid with below provide code snippet

    DataBase

    import 'package:flutter/foundation.dart';
    
    ///
    ///
    /// HIVE DATABASE MODEL USING ENCRYPTION AND CRUD ACTIONS
    
    import 'package:flutter_secure_storage/flutter_secure_storage.dart';
    import 'package:getx_hive_demo_one/models/event.dart';
    import 'package:get/get.dart';
    import 'package:hive/hive.dart';
    import 'dart:convert';
    import 'package:syncfusion_flutter_datagrid/datagrid.dart';
    import 'package:flutter/material.dart';
    
    class Database extends GetxController {
      GridSource dataGridSource;
    
      Database() {
        dataGridSource = GridSource(this);
      }
    
      // Hive box name
      String _boxName = 'database';
    
      // Initialize our list of contacts
      List<Event> eventList = [];
    
      // Holds our active contact
      Event _activeEvent;
    
      ///
      ///
      /// Encrypted Box
      /// Create & open encrypted box
      /// Return encrypted box for other methods to have box
      Future<Box<Event>> encryptedBox() async {
        final FlutterSecureStorage secureStorage = const FlutterSecureStorage();
        var containsEncryptionKey = await secureStorage.containsKey(key: 'key');
        if (!containsEncryptionKey) {
          var key = Hive.generateSecureKey();
          await secureStorage.write(key: 'key', value: base64UrlEncode(key));
        }
        var encryptionKey = base64Url.decode(await secureStorage.read(key: 'key'));
        var box = await Hive.openBox<Event>(_boxName,
            encryptionCipher: HiveAesCipher(encryptionKey));
        return box;
      }
    
      ///
      ///
      /// Create Event
      /// Saves data to hive box, updates repository & notify listeners
      void addEvent(Event newEvent) async {
        var box = await Hive.openBox<Event>(_boxName);
        await box.add(newEvent);
        eventList = box.values.toList();
        refresh();
      }
    
      ///
      ///
      /// Read Event
      /// Sends events in database to list repository
      void getEvents() async {
        var box = await Hive.openBox<Event>(_boxName);
        eventList = box.values.toList();
        refresh();
      }
    
      ///
      ///
      /// Read Event
      /// Returns event list
      List<Event> getEventList() {
        getEvents();
        return eventList;
      }
    
      ///
      ///
      /// Read Event
      /// Gets contact by index from repository
      Event getEvent(index) {
        return eventList[index];
      }
    
      ///
      ///
      /// Read Event
      /// Gets length of event repository
      int get eventCount {
        return eventList.length;
      }
    
      ///
      ///
      /// Read Event
      /// Sets active event to notify listeners for
      void setActiveEvent(key) async {
        var box = await Hive.openBox<Event>(_boxName);
        _activeEvent = box.get(key);
        refresh();
      }
    
      ///
      ///
      /// Read Event
      /// Get active event
      Event getActiveEvent() {
        return _activeEvent;
      }
    
      ///
      ///
      /// Update Event
      /// Updates event info with new data
      void editEvent({Event event, int eventKey}) async {
        var box = await Hive.openBox<Event>(_boxName);
        await box.put(eventKey, event);
        eventList = box.values.toList();
        _activeEvent = box.get(eventKey);
        refresh();
      }
    
      void deleteAll() async{
        var box = await Hive.openBox<Event>(_boxName);
        box.deleteFromDisk();
        refresh();
      }
    
      ///
      ///
      /// Delete Event
      /// Deletes event from box and updates list
      void deleteEvent(key) async {
        var box = await Hive.openBox<Event>(_boxName);
        await box.delete(key);
        eventList = box.values.toList();
        print('Deleted event at:' + key.toString());
        refresh();
      }
    
      /// Non-encrypted box
      /// var box = await Hive.openBox<Event>(_boxName);
    }
    
    class GridSource extends DataGridSource {
    
    
    List<DataGridRow> _eventGridRow = [];
      final Database database;
      List eventList = [];
    
      GridSource(this.database) {
        this.database.addListener(handleListChanges);
      }
    
      void handleListChanges() {
        if (!listEquals(eventList, this.database.eventList)) {
          eventList = this.database.eventList;
          buildDataGridRows();
          notifyListeners();
        }
      }
    
      void buildDataGridRows() {
        _eventGridRow = eventList
            .map<DataGridRow>(
              (event) => DataGridRow(
                cells: [
                  DataGridCell<String>(columnName: 'name', value: event.eventName),
                  DataGridCell<String>(
                      columnName: 'start', value: event.eventStart.toString()),
                  DataGridCell<String>(
                      columnName: 'duration',
                      value: event.eventDuration.toString()),
                  DataGridCell<String>(
                      columnName: 'invitees',
                      value: event.numberInvitees.toString()),
                  DataGridCell<String>(
                      columnName: 'location', value: event.location),
                ],
              ),
            )
            .toList();
      }
    
      @override
      List<DataGridRow> get rows => _eventGridRow;
    
      @override
      DataGridRowAdapter buildRow(DataGridRow row) {
        final int index = _eventGridRow.indexOf(row);
        Color getRowBackgroundColor() {
          if (index % 2 == 0) {
            return Colors.grey[100];
          }
          return Colors.transparent;
        }
    
        return DataGridRowAdapter(
          color: getRowBackgroundColor(),
          cells: [
            Container(
              alignment: Alignment.centerLeft,
              padding: EdgeInsets.all(8.0),
              child: Text(
                row.getCells()[0].value,
                overflow: TextOverflow.ellipsis,
              ),
            ),
            Container(
              alignment: Alignment.centerLeft,
              padding: EdgeInsets.all(8.0),
              child: Text(
                row.getCells()[1].value,
                overflow: TextOverflow.ellipsis,
              ),
            ),
            Container(
              alignment: Alignment.centerLeft,
              padding: EdgeInsets.all(8.0),
              child: Text(
                row.getCells()[2].value,
                overflow: TextOverflow.ellipsis,
              ),
            ),
            Container(
              alignment: Alignment.centerLeft,
              padding: EdgeInsets.all(8.0),
              child: Text(
                row.getCells()[3].value,
                overflow: TextOverflow.ellipsis,
              ),
            ),
            Container(
              alignment: Alignment.centerLeft,
              padding: EdgeInsets.all(8.0),
              child: Text(
                row.getCells()[4].value,
                overflow: TextOverflow.ellipsis,
              ),
            ),
          ],
        );
      }
    }
    

    DataGridPage

    import 'package:flutter/material.dart';
    import 'package:get/get.dart';
    import 'package:getx_hive_demo_one/database/database.dart';
    import 'package:syncfusion_flutter_datagrid/datagrid.dart';
    
    class DataGrid extends StatelessWidget {
      final Database database = Get.put(Database());
    
    
      @override
      Widget build(BuildContext context) {
        return SafeArea(
          child: Scaffold(
              body: GetBuilder<Database>(
                  builder: (database) {
                    return SfDataGrid(
                      source: database.dataGridSource,
                      columnWidthMode: ColumnWidthMode.fill,
                      columns: <GridColumn>[
                        GridTextColumn(
                            columnName: 'name',
                            label: Container(
                                color: Colors.grey[800],
                                padding: EdgeInsets.all(8.0),
                                alignment: Alignment.centerLeft,
                                child: Text(
                                  'Event',
                                  style: TextStyle(color: Colors.white),
                                ))),
                        GridTextColumn(
                            columnName: 'start',
                            label: Container(
                                color: Colors.grey[800],
                                padding: EdgeInsets.all(8.0),
                                alignment: Alignment.centerLeft,
                                child: Text(
                                  'Start Date',
                                  style: TextStyle(color: Colors.white),
                                ))),
                        GridTextColumn(
                            columnName: 'duration',
                            label: Container(
                                color: Colors.grey[800],
                                padding: EdgeInsets.all(8.0),
                                alignment: Alignment.centerLeft,
                                child: Text(
                                  'Event Duration',
                                  style: TextStyle(color: Colors.white),
                                  overflow: TextOverflow.ellipsis,
                                ))),
                        GridTextColumn(
                            columnName: 'invitees',
                            label: Container(
                                color: Colors.grey[800],
                                padding: EdgeInsets.all(8.0),
                                alignment: Alignment.centerLeft,
                                child: Text(
                                  'Attendees',
                                  style: TextStyle(color: Colors.white),
                                ))),
                        GridTextColumn(
                            columnName: 'location',
                            label: Container(
                                color: Colors.grey[800],
                                padding: EdgeInsets.all(8.0),
                                alignment: Alignment.centerLeft,
                                child: Text(
                                  'Location',
                                  style: TextStyle(color: Colors.white),
                                ))),
                      ],
                    );
                  })),
        );
      }
    }