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

Issue

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),
                            ))),
                  ],
                );
              })),
    );
  }
}

Answered By – Balasubramani Sundaram

Answer Checked By – Robin (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *