Pull to Refresh in Flutter DataGrid (SfDataGrid)
8 Jul 202624 minutes to read
The SfDataGrid provides support to add more data at runtime by using the pull-to-refresh feature. You can enable the pull-to-refresh option by setting the SfDataGrid.allowPullToRefresh property to true and overriding the DataGridSource.handleRefresh method to add new data to the data source at runtime, then notify the data grid about the changes.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart';
class Employee {
Employee(this.id, this.name, this.designation, this.salary);
final int id;
final String name;
final String designation;
final int salary;
}
class PullToRefreshDemoState extends State<PullToRefreshDemo> {
List<Employee> _employees = <Employee>[];
late EmployeeDataSource _employeeDataSource;
@override
void initState() {
super.initState();
_employees = getEmployeeData();
_employeeDataSource = EmployeeDataSource(employeeData: _employees);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SfDataGrid(
allowPullToRefresh: true,
source: _employeeDataSource,
columns: <GridColumn>[
GridColumn(
columnName: 'id',
label: Container(
padding: EdgeInsets.symmetric(horizontal: 16.0),
alignment: Alignment.centerRight,
child: Text('ID', overflow: TextOverflow.ellipsis),
),
),
GridColumn(
columnName: 'name',
label: Container(
padding: EdgeInsets.symmetric(horizontal: 16.0),
alignment: Alignment.centerLeft,
child: Text('Name', overflow: TextOverflow.ellipsis),
),
),
GridColumn(
columnName: 'designation',
label: Container(
padding: EdgeInsets.symmetric(horizontal: 16.0),
alignment: Alignment.centerLeft,
child: Text('Designation', overflow: TextOverflow.ellipsis),
),
),
GridColumn(
columnName: 'salary',
label: Container(
padding: EdgeInsets.symmetric(horizontal: 16.0),
alignment: Alignment.centerRight,
child: Text('Salary', overflow: TextOverflow.ellipsis),
),
),
],
),
);
}
}
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),
];
}
class EmployeeDataSource extends DataGridSource {
late List<Employee> _employees = [];
EmployeeDataSource({required List<Employee> employeeData}) {
_employees = employeeData;
buildDataGridRows();
}
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: EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
dataGridCell.value.toString(),
overflow: TextOverflow.ellipsis,
),
);
}).toList(),
);
}
@override
Future<void> handleRefresh() async {
// Simulate network delay for fetching new data from server
await Future.delayed(Duration(seconds: 2));
_addMoreRows(_employees, 15);
buildDataGridRows();
notifyListeners();
}
void buildDataGridRows() {
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();
}
void _addMoreRows(List<Employee> employees, int count) {
final Random random = Random();
final startIndex = employees.isNotEmpty ? employees.length : 0;
final endIndex = startIndex + count;
for (int i = startIndex; i < endIndex; i++) {
employees.add(
Employee(
1000 + i,
_names[random.nextInt(_names.length)],
_designation[random.nextInt(_designation.length)],
10000 + random.nextInt(10000),
),
);
}
}
final List<String> _names = <String>[
'Welli',
'Blonp',
'Folko',
'Furip',
'Folig',
'Picco',
'Frans',
'Warth',
'Linod',
'Simop',
'Merep',
'Riscu',
'Seves',
'Vaffe',
'Alfki',
];
final List<String> _designation = <String>[
'Project Lead',
'Developer',
'Manager',
'Designer',
'System Analyst',
'CEO',
];
}Reference: Download the demo application from GitHub.

Customizing the refresh indicator
SfDataGrid displays Flutter’s RefreshIndicator during pull-to-refresh actions. You can customize the refresh indicator by using the SfDataGrid.refreshIndicatorStrokeWidth and SfDataGrid.refreshIndicatorDisplacement properties.
To set the indicator’s color and background color, use the ColorScheme properties within ThemeData. The primary color in ColorScheme controls the indicator’s color.
Note: The deprecated
ThemeData.accentColorproperty has been replaced withColorScheme.primaryin Flutter 3.0+. Use ColorScheme for modern Flutter applications.
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart';
class CustomRefreshIndicatorDemoState
extends State<CustomRefreshIndicatorDemo> {
List<Employee> _employees = <Employee>[];
late EmployeeDataSource _employeeDataSource;
@override
void initState() {
super.initState();
_employees = getEmployeeData();
_employeeDataSource = EmployeeDataSource(employeeData: _employees);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Theme(
data: ThemeData(
brightness: Brightness.light,
canvasColor: Colors.lightBlue,
colorScheme: const ColorScheme.light(primary: Colors.white),
),
child: SfDataGrid(
allowPullToRefresh: true,
source: _employeeDataSource,
refreshIndicatorStrokeWidth: 3.0,
refreshIndicatorDisplacement: 60.0,
columns: <GridColumn>[
GridColumn(
columnName: 'id',
label: Container(
padding: EdgeInsets.symmetric(horizontal: 16.0),
alignment: Alignment.centerRight,
child: Text('ID', overflow: TextOverflow.ellipsis),
),
),
GridColumn(
columnName: 'name',
label: Container(
padding: EdgeInsets.symmetric(horizontal: 16.0),
alignment: Alignment.centerLeft,
child: Text('Name', overflow: TextOverflow.ellipsis),
),
),
GridColumn(
columnName: 'designation',
label: Container(
padding: EdgeInsets.symmetric(horizontal: 16.0),
alignment: Alignment.centerLeft,
child: Text('Designation', overflow: TextOverflow.ellipsis),
),
),
GridColumn(
columnName: 'salary',
label: Container(
padding: EdgeInsets.symmetric(horizontal: 16.0),
alignment: Alignment.centerRight,
child: Text('Salary', overflow: TextOverflow.ellipsis),
),
),
],
),
),
);
}
}Reference: Download the demo application from GitHub.

Programmatic Pull to Refresh
You can trigger data refresh programmatically using the SfDataGridState.refresh() method. By default, the refresh indicator is displayed during the refresh operation. To refresh data without showing the refresh indicator, pass false to the showRefreshIndicator parameter.
The refresh() method calls the DataGridSource.handleRefresh() method, which is a Future<void> that returns when the refresh operation completes. If an exception occurs during handleRefresh(), the data grid will handle it gracefully and the refresh state will be reset.
Note: Use a
GlobalKey<SfDataGridState>to access the refresh method from outside the SfDataGrid widget.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart';
EmployeeDataSource _employeeDataSource = EmployeeDataSource();
List<Employee> _employees = <Employee>[];
final GlobalKey<SfDataGridState> key = GlobalKey<SfDataGridState>();
class PullToRefreshSampleState extends State<PullToRefreshSample> {
@override
void initState() {
super.initState();
_employees = getEmployeeData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SfDataGrid(
key: key,
allowPullToRefresh: true,
source: _employeeDataSource,
columns: <GridColumn>[
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),
),
),
],
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.refresh),
onPressed: () {
key.currentState!.refresh();
},
),
);
}
}
class EmployeeDataSource extends DataGridSource {
EmployeeDataSource() {
buildDataGridRows();
}
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
Future<void> handleRefresh() async {
await Future.delayed(const Duration(seconds: 5));
_addMoreRows(_employees, 15);
buildDataGridRows();
notifyListeners();
}
void buildDataGridRows() {
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();
}
void _addMoreRows(List<Employee> employees, int count) {
final Random random = Random();
final startIndex = employees.isNotEmpty ? employees.length : 0,
endIndex = startIndex + count;
for (int i = startIndex; i < endIndex; i++) {
employees.add(
Employee(
1000 + i,
_names[random.nextInt(_names.length - 1)],
_designation[random.nextInt(_designation.length - 1)],
10000 + random.nextInt(10000),
),
);
}
}
final List<String> _names = <String>[
'Welli',
'Blonp',
'Folko',
'Furip',
'Folig',
'Picco',
'Frans',
'Warth',
'Linod',
'Simop',
'Merep',
'Riscu',
'Seves',
'Vaffe',
'Alfki',
];
final List<String> _designation = <String>[
'Project Lead',
'Developer',
'Manager',
'Designer',
'System Analyst',
'CEO',
];
}Reference: Download the demo application from GitHub.
