Flutter应用中实现Excel转PDF及表格预览功能需求
Flutter Excel生成、PDF转换及只读预览实现方案
核心依赖包
在pubspec.yaml中添加以下依赖:
dependencies: excel: ^2.0.0 path_provider: ^2.1.2 pdf: ^3.10.0 flutter_pdfview: ^1.3.2 # 如需直接预览Excel,添加excel_viewer: ^1.0.0
1. 生成Excel文件(支持单表/全工作簿导出)
先创建工作簿并填充数据,再根据用户选择导出指定工作表或整个工作簿:
import 'package:excel/excel.dart'; import 'package:path_provider/path_provider.dart'; import 'dart:io'; // 全局Excel实例,存储工作簿数据 final Excel _excelInstance = Excel.createExcel(); // 初始化示例数据(实际业务中替换为你的数据) void initExcelData() { // 创建第一个工作表并填充数据 final salesSheet = _excelInstance['销售数据']; salesSheet.appendRow(['日期', '产品名称', '销量', '销售额']); salesSheet.appendRow(['2024-05-01', '智能手机', '120', '480000']); salesSheet.appendRow(['2024-05-02', '平板电脑', '80', '240000']); // 创建第二个工作表 final stockSheet = _excelInstance['库存数据']; stockSheet.appendRow(['产品名称', '库存数量', '仓库位置']); stockSheet.appendRow(['智能手机', '500', 'A区']); stockSheet.appendRow(['平板电脑', '300', 'B区']); } // 导出Excel文件 // targetSheet: 传入工作表名称则导出单表,不传则导出整个工作簿 Future<File> exportExcel({String? targetSheet}) async { final appDir = await getApplicationDocumentsDirectory(); final filePath = '${appDir.path}/business_data.xlsx'; if (targetSheet != null && _excelInstance.tables.containsKey(targetSheet)) { // 仅导出指定工作表:创建新工作簿并复制目标表 final singleSheetExcel = Excel.createExcel(); singleSheetExcel.copySheet(_excelInstance, targetSheet); return File(filePath)..writeAsBytesSync(singleSheetExcel.save()!); } else { // 导出整个工作簿 return File(filePath)..writeAsBytesSync(_excelInstance.save()!); } }
2. Excel转PDF
解析Excel内容,用pdf包构建格式规整的PDF文档,同样支持单表/全工作簿转换:
import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; Future<File> convertExcelToPdf(File excelFile, {String? targetSheet}) async { final appDir = await getApplicationDocumentsDirectory(); final pdfFilePath = '${appDir.path}/business_data.pdf'; // 读取并解析Excel文件 final excelBytes = excelFile.readAsBytesSync(); final parsedExcel = Excel.decodeBytes(excelBytes); // 确定要转换的工作表集合 final targetSheets = targetSheet != null ? [parsedExcel.tables[targetSheet]!] : parsedExcel.tables.values.toList(); final pdfDoc = pw.Document(); for (final sheet in targetSheets) { // 构建PDF表格行 final pdfRows = <pw.TableRow>[]; // 添加表头行 pdfRows.add(pw.TableRow( decoration: pw.BoxDecoration(color: PdfColors.grey200), children: sheet.rows.first.map((cell) => pw.Text( cell?.value?.toString() ?? '', style: pw.TextStyle(fontWeight: pw.FontWeight.bold), )).toList(), )); // 添加数据行 for (final row in sheet.rows.skip(1)) { pdfRows.add(pw.TableRow( children: row.map((cell) => pw.Text(cell?.value?.toString() ?? '')).toList(), )); } // 将工作表添加为PDF页面 pdfDoc.addPage( pw.Page( pageFormat: PdfPageFormat.a4, build: (context) => pw.Padding( padding: const pw.EdgeInsets.all(16), child: pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ pw.Text( sheet.name, style: pw.TextStyle(fontSize: 20, fontWeight: pw.FontWeight.bold), ), pw.SizedBox(height: 16), pw.Table( border: pw.TableBorder.all(width: 0.5), children: pdfRows, ), ], ), ), ), ); } // 保存PDF文件 final pdfBytes = await pdfDoc.save(); return File(pdfFilePath)..writeAsBytesSync(pdfBytes); }
3. 只读预览功能
方案一:PDF预览(推荐,天然只读)
使用flutter_pdfview实现PDF文件的只读预览:
import 'package:flutter_pdfview/flutter_pdfview.dart'; import 'package:flutter/material.dart'; void previewPdfFile(File pdfFile, BuildContext context) { Navigator.push( context, MaterialPageRoute( builder: (_) => Scaffold( appBar: AppBar(title: const Text('文件预览')), body: PDFView( filePath: pdfFile.path, enableSwipe: true, swipeHorizontal: true, autoSpacing: false, pageFling: false, // 禁用所有编辑相关操作,确保只读 onRender: (_) {}, onError: (error) {}, onPageError: (_, __) {}, onViewCreated: (_) {}, ), ), ), ); }
方案二:直接预览Excel(只读模式)
如果需要直接展示Excel格式,使用excel_viewer包并开启只读模式:
import 'package:excel_viewer/excel_viewer.dart'; import 'package:flutter/material.dart'; void previewExcelFile(File excelFile, BuildContext context) { Navigator.push( context, MaterialPageRoute( builder: (_) => ExcelViewer( filePath: excelFile.path, readOnly: true, // 强制开启只读,禁止编辑 ), ), ); }
4. 整合导出选项UI
在界面中添加单选按钮让用户选择导出模式,触发导出、转换和预览流程:
import 'package:flutter/material.dart'; class ExportPage extends StatefulWidget { const ExportPage({super.key}); @override State<ExportPage> createState() => _ExportPageState(); } class _ExportPageState extends State<ExportPage> { String? _exportMode; // 'current' 表示当前工作表,'all' 表示整个工作簿 String _currentSheetName = '销售数据'; // 替换为当前显示的工作表名称 @override void initState() { super.initState(); initExcelData(); // 初始化Excel数据 } Future<void> _handleExport() async { if (_exportMode == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('请选择导出模式')), ); return; } // 导出Excel final excelFile = await exportExcel( targetSheet: _exportMode == 'current' ? _currentSheetName : null, ); // 转换为PDF final pdfFile = await convertExcelToPdf( excelFile, targetSheet: _exportMode == 'current' ? _currentSheetName : null, ); // 预览PDF(或替换为previewExcelFile预览Excel) previewPdfFile(pdfFile, context); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('数据导出')), body: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('选择导出模式:', style: TextStyle(fontSize: 16)), RadioListTile<String>( title: const Text('导出当前显示的工作表'), value: 'current', groupValue: _exportMode, onChanged: (value) => setState(() => _exportMode = value), ), RadioListTile<String>( title: const Text('导出整个工作簿'), value: 'all', groupValue: _exportMode, onChanged: (value) => setState(() => _exportMode = value), ), const SizedBox(height: 24), ElevatedButton( onPressed: _handleExport, child: const Text('导出并预览'), ), ], ), ), ); } }
内容的提问来源于stack exchange,提问作者mohamed alaa
相关产品推荐
相关产品推荐

