Hello,
I'm trying to use the exportToPdf from the SfDataGrid per this article here: https://help.syncfusion.com/flutter/datagrid/export-to-pdf
Printing functionality only really needs to work with Web (although with iOS and Android is good to have).
The Version I'm using is here:
cupertino_icons: ^1.0.2
firebase_core: ^1.21.1
firebase_auth: ^3.7.0
cloud_firestore: ^3.4.6
syncfusion_flutter_calendar: ^20.2.40
provider: ^6.0.3
syncfusion_flutter_datagrid: ^20.2.50
syncfusion_flutter_datagrid_export: ^20.3.49-beta
The Click event I'm using has the following code:
I have a global key:
final GlobalKeysfDataGridKey = GlobalKey ();
And use this with an ElevatedButton.Icon as outlined below:
ElevatedButton.icon(
onPressed: () async {
print('Start Printing');
PdfDocument document = sfDataGridKey.currentState!.exportToPdfDocument();
final Listbytes = document.saveSync();
document.dispose();
print('Done Printing');
},
icon: const Icon(Icons.print),
label: const Text('Print'),
),
SfDataGrid(
key: sfDataGridKey,
controller: _dataGridController,
selectionMode: SelectionMode.single,
source: _pendingEventDataSource = PendingEventDataSource(pendingEvents),
columnWidthMode: ColumnWidthMode.fill,
columns: [
The Error I'm seeing below:
Start Printing
Error: Unexpected null value.
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 266:49 throw_
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 528:63 nullCheck
packages/syncfusion_flutter_datagrid_export/src/export_to_pdf.dart 583:75 getCellValue
packages/syncfusion_flutter_datagrid_export/src/export_to_pdf.dart 394:32 exportRow
packages/syncfusion_flutter_datagrid_export/src/export_to_pdf.dart 381:7 exportRows
packages/syncfusion_flutter_datagrid_export/src/export_to_pdf.dart 483:5 exportToPdfGrid
packages/syncfusion_flutter_datagrid_export/src/export_to_pdf.dart 415:29 exportToPdfDocument
packages/syncfusion_flutter_datagrid_export/src/export_to_pdf.dart 156:21 DataGridPdfExportExtensions.exportToPdfDocument
packages/prod_scheduler_acc/widgets/data_grid_printing.dart 128:82
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 84:54 runBody
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 123:5 _async
packages/prod_scheduler_acc/widgets/data_grid_printing.dart 126:40
packages/flutter/src/material/ink_well.dart 1072:21 handleTap
packages/flutter/src/gestures/recognizer.dart 253:24 invokeCallback
packages/flutter/src/gestures/tap.dart 627:11 handleTapUp
packages/flutter/src/gestures/tap.dart 306:5 [_checkUp]
packages/flutter/src/gestures/tap.dart 239:7 handlePrimaryPointer
packages/flutter/src/gestures/recognizer.dart 615:9 handleEvent
packages/flutter/src/gestures/pointer_router.dart 98:12 [_dispatch]
packages/flutter/src/gestures/pointer_router.dart 143:9
dart-sdk/lib/_internal/js_dev_runtime/private/linked_hash_map.dart 21:13 forEach
packages/flutter/src/gestures/pointer_router.dart 141:17 [_dispatchEventToRoutes]
packages/flutter/src/gestures/pointer_router.dart 127:7 route
packages/flutter/src/gestures/binding.dart 460:19 handleEvent
packages/flutter/src/gestures/binding.dart 440:14 dispatchEvent
packages/flutter/src/rendering/binding.dart 337:11 dispatchEvent
packages/flutter/src/gestures/binding.dart 395:7 [_handlePointerEventImmediately]
packages/flutter/src/gestures/binding.dart 357:5 handlePointerEvent
packages/flutter/src/gestures/binding.dart 314:7 [_flushPointerEventQueue]
packages/flutter/src/gestures/binding.dart 295:7 [_handlePointerDataPacket]
lib/_engine/engine/platform_dispatcher.dart 1183:13 invoke1
lib/_engine/engine/platform_dispatcher.dart 244:5 invokeOnPointerDataPacket
lib/_engine/engine/pointer_binding.dart 147:39 [_onPointerData]
lib/_engine/engine/pointer_binding.dart 653:20
lib/_engine/engine/pointer_binding.dart 594:14
lib/_engine/engine/pointer_binding.dart 288:16 loggedHandler
lib/_engine/engine/pointer_binding.dart 179:80
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 334:14 _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 339:39 dcall
Is there a working version of the export to PDF for the web?
If I use a non-async function call, I get this error:
======== Exception caught by gesture ===============================================================
The following TypeErrorImpl was thrown while handling a gesture:
Unexpected null value.
When the exception was thrown, this was the stack:
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 266:49 throw_
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 528:63 nullCheck
packages/syncfusion_flutter_datagrid_export/src/export_to_pdf.dart 583:75 getCellValue
packages/syncfusion_flutter_datagrid_export/src/export_to_pdf.dart 394:32 exportRow
packages/syncfusion_flutter_datagrid_export/src/export_to_pdf.dart 381:7 exportRows
packages/syncfusion_flutter_datagrid_export/src/export_to_pdf.dart 483:5 exportToPdfGrid
packages/syncfusion_flutter_datagrid_export/src/export_to_pdf.dart 415:29 exportToPdfDocument
packages/syncfusion_flutter_datagrid_export/src/export_to_pdf.dart 156:21 DataGridPdfExportExtensions.exportToPdfDocument
packages/prod_scheduler_acc/widgets/data_grid_printing.dart 128:82 <fn>
packages/flutter/src/material/ink_well.dart 1072:21 handleTap
packages/flutter/src/gestures/recognizer.dart 253:24 invokeCallback
packages/flutter/src/gestures/tap.dart 627:11 handleTapUp
packages/flutter/src/gestures/tap.dart 306:5 [_checkUp]
packages/flutter/src/gestures/tap.dart 239:7 handlePrimaryPointer
packages/flutter/src/gestures/recognizer.dart 615:9 handleEvent
packages/flutter/src/gestures/pointer_router.dart 98:12 [_dispatch]
packages/flutter/src/gestures/pointer_router.dart 143:9 <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/linked_hash_map.dart 21:13 forEach
packages/flutter/src/gestures/pointer_router.dart 141:17 [_dispatchEventToRoutes]
packages/flutter/src/gestures/pointer_router.dart 127:7 route
packages/flutter/src/gestures/binding.dart 460:19 handleEvent
packages/flutter/src/gestures/binding.dart 440:14 dispatchEvent
packages/flutter/src/rendering/binding.dart 337:11 dispatchEvent
packages/flutter/src/gestures/binding.dart 395:7 [_handlePointerEventImmediately]
packages/flutter/src/gestures/binding.dart 357:5 handlePointerEvent
packages/flutter/src/gestures/binding.dart 314:7 [_flushPointerEventQueue]
packages/flutter/src/gestures/binding.dart 295:7 [_handlePointerDataPacket]
lib/_engine/engine/platform_dispatcher.dart 1183:13 invoke1
lib/_engine/engine/platform_dispatcher.dart 244:5 invokeOnPointerDataPacket
lib/_engine/engine/pointer_binding.dart 147:39 [_onPointerData]
lib/_engine/engine/pointer_binding.dart 653:20 <fn>
lib/_engine/engine/pointer_binding.dart 594:14 <fn>
lib/_engine/engine/pointer_binding.dart 288:16 loggedHandler
lib/_engine/engine/pointer_binding.dart 179:80 <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 334:14 _checkAndCall
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 339:39 dcall
Handler: "onTap"
Recognizer: TapGestureRecognizer#3ba07
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(329.5, 119.5)
finalLocalPosition: Offset(62.5, 6.5)
button: 1
sent tap down
====================================================================================================
Hi Martin,
Based on the provided information, we are unable to reproduce the reported issue. We have tested the following sample and it’s working fine on our end. We suspect that you’re setting different names for the column in the buildDataGridRow and GridColumn.columnName. We map the values based on the column name which is set in the buildDataGridRow and GridColumn.columnName. So, you need to set the same name in both places. That's the behavior of DataGrid. We have prepared a simple sample for that. Please check the following code snippet and sample.
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('SfDatagrid Demo')), body: Column(children: [ const Padding(padding: EdgeInsets.only(top: 30)), SizedBox( child: ElevatedButton.icon( onPressed: () async { final PdfDocument document = _key.currentState!.exportToPdfDocument();
final List<int> bytes = document.saveSync(); await helper.saveAndLaunchFile(bytes, 'DataGrid.pdf'); document.dispose(); }, icon: const Icon(Icons.print), label: const Text('Export DataGrid to PDF'), )), Expanded( child: Card( margin: const EdgeInsets.all(30), child: SfDataGrid( source: _employeeDataSource, key: _key, columns: getColumns, columnWidthMode: ColumnWidthMode.fill), )) ])); } }
List<GridColumn> get getColumns { return <GridColumn>[ GridColumn( columnName: 'id', label: Container( padding: const EdgeInsets.symmetric(horizontal: 16.0), alignment: Alignment.center, child: const Text( 'ID', ))), GridColumn( columnName: 'name', label: Container( padding: const EdgeInsets.symmetric(horizontal: 16.0), alignment: Alignment.center, child: const Text('Name'))), GridColumn( columnName: 'designation', label: Container( padding: const EdgeInsets.symmetric(horizontal: 16.0), alignment: Alignment.center, child: const Text( 'Designation', overflow: TextOverflow.ellipsis, ))), GridColumn( columnName: 'salary', label: Container( padding: const EdgeInsets.symmetric(horizontal: 16.0), alignment: Alignment.center, child: const Text('Salary'))), ]; }
In DataGridSource:
class EmployeeDataSource extends DataGridSource { EmployeeDataSource(List<Employee> employees) { buildDataGridRow(employees); }
void buildDataGridRow(List<Employee> employeeData) { dataGridRow = employeeData.map<DataGridRow>((employee) { return DataGridRow(cells: [ DataGridCell<int>(columnName: 'id', value: employee.id), DataGridCell<String>(columnName: 'name', value: employee.name), DataGridCell<String>( columnName: 'designation', value: employee.designation), DataGridCell<int>(columnName: 'salary', value: employee.salary), ]); }).toList(); } … } |
Sample Link: https://www.syncfusion.com/downloads/support/directtrac/general/ze/sample-426386132
We hope this helps. Please let us know if you need any further assistance with this.
Regards,
Tamilarasan
Thank you Tamilarasan,
The Datagrid is working very well, it's only the export to PDF that is not working. I've included the dataSource code below as well as the Grid code from the DataGrid itself.
One thing that I do not have currently is the "HELPER" methods that you are referencing in the code sample as well as the "HELPER" function call in my onclick event. The helper function looks like it works for EXCEL files but perhaps I'm reading it incorrectly.
This is the line of code that you have that I'm referring it - I do not have this in my call:
await helper.saveAndLaunchFile(bytes, 'DataGrid.pdf');
This seems to call this function:
// ignore_for_file: avoid_web_libraries_in_flutter
import 'dart:async';
import 'dart:convert';
import 'dart:html';
///To save the excel sheet in the web platform.
Future<void> saveAndLaunchFile(List<int> bytes, String fileName) async {
AnchorElement(
rel='nofollow' href:
'data:application/octet-stream;charset=utf-16le;base64,${base64.encode(bytes)}')
..setAttribute('download', fileName)
..click();
}
Below is my datasource and my grid columns:
class PendingEventDataSource extends DataGridSource {
/// CTOR
/// This takes a List collection of ScheduleEvents that are marked as 'Pending'
PendingEventDataSource(List<SchedulerEvent> pendingEvents) {
dataGridRows = pendingEvents.map<DataGridRow>(
(inputPendingEvent) {
return DataGridRow(
cells: [
/// map the columns to the value as each row is built
DataGridCell(columnName: 'Requested Location', value: inputPendingEvent.cemeteryLocation),
DataGridCell(columnName: 'fname', value: inputPendingEvent.fname),
DataGridCell(columnName: 'lname', value: inputPendingEvent.lname),
DataGridCell(columnName: 'created', value: DateFormat(kQptDisplayDateTimeFormatUS).format(inputPendingEvent.createdDate!)),
],
);
},
).toList();
}
/// these will be tied to the rows from the Pending Events
late List<DataGridRow> dataGridRows;
/// override the rows property and assign a field to it
/// the dataGridRows was declared just above this call
/// Fetches the rows available for data population.
/// Also, it is used to fetch the corresponding data object to process the selection.
@override
List<DataGridRow> get rows => dataGridRows;
/// This defines the widget to build for each cell of data as the Rows are built
/// row.getCells is the main method
/// Map each cell with a "widget" and pass a function that returns a Container (or whatever)
/// Fetches the widget for each cell with DataGridRowAdapter
@override
DataGridRowAdapter? buildRow(DataGridRow row) {
return DataGridRowAdapter(
color: kQptColorLightGrey200,
cells: row.getCells().map<Widget>((inputPendingEventCellData) {
return Container(
alignment: Alignment.center,
child: Center(
child: FittedBox(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
inputPendingEventCellData.value.toString(),
),
),
)),
);
}).toList());
}
}
Columns:
SfDataGrid(
key: sfDataGridKey,
controller: _dataGridController,
selectionMode: SelectionMode.single,
source: _pendingEventDataSource = PendingEventDataSource(pendingEvents),
columnWidthMode: ColumnWidthMode.fill,
columns: [
GridColumn(
columnName: 'Requested Location',
label: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: const Text(
'Requested Location',
style: TextStyle(color: Colors.white),
),
),
),
GridColumn(
columnName: 'fname',
label: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: const Text(
'First Name',
style: TextStyle(color: Colors.white),
),
),
),
GridColumn(
columnName: 'lname',
label: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: const Text(
'Last Name',
style: TextStyle(color: Colors.white),
),
),
),
GridColumn(
columnName: 'created',
label: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: const Text(
'Created',
style: TextStyle(color: Colors.white),
),
),
),
],
Tamilarasan,
Thanks for your help - is there a way to not download the file?
Meaning is it possible to simply open the PDF document and then take an action like print?
Hi Martin,
As per your requirement, you can achieve it on the Web platform using the html.dart package. We have prepared a sample for that. In that sample, you can export the DataGrid content with a button click. It directly loads the PDF document in the new window without downloading it. You can now print or download the document after exporting it. Please check the following sample and code snippet.
import 'dart:html' as html;
@override void initState() { super.initState(); employees = getEmployeeData(); _employeeDataSource = EmployeeDataSource(employees); }
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('SfDatagrid Demo')), body: Column(children: [ const Padding(padding: EdgeInsets.only(top: 30)), SizedBox( child: ElevatedButton.icon( onPressed: () async { final PdfDocument document = _key.currentState!.exportToPdfDocument();
final List<int> data = document.saveSync(); Uint8List bytes = Uint8List.fromList(data);
final url = html.Url.createObjectUrl( html.Blob([bytes], 'application/pdf')); html.window.open(url, '_blank');
document.dispose(); }, icon: const Icon(Icons.print), label: const Text('Export DataGrid to PDF'), )), Expanded( child: Card( margin: const EdgeInsets.all(30), child: SfDataGrid( source: _employeeDataSource, key: _key, columns: getColumns, columnWidthMode: ColumnWidthMode.fill), )) ])); } } |
Sample: https://www.syncfusion.com/downloads/support/directtrac/general/ze/sample-154064527
We hope this helps. Please let us know if you need any further assistance.
Regards,
Tamilarasan
If this response is helpful, please consider Accepting it as the solution so that other members can locate it more quickly.
Tamilarasan Thank you!
That works very well.
I'll need to conditionally load dart:html as this will ultimately need to work for iOS, Android and Web - but I'll dig into how to do that (new to Flutter).
Have a good weekend!
--
Hi Martin,
We are glad that the provided response meets your requirement. Please let us know if you require further assistance. As always, we are happy to help you out.
Regards,
Tamilarasan