You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 17:48:18