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