Flutter生成PDF文档插件flutter_to_pdf的使用

发布于 1周前 作者 yuanlaile 来自 Flutter

Flutter生成PDF文档插件flutter_to_pdf的使用

简介

FlutterToPDF 是一个可以将任何 Flutter 小部件导出为 PDF 文档的包,完全用 Dart 语言编写。它提供了多种功能,包括导出整个 PDF 文档、单个页面或特定的小部件,并允许你自定义导出选项,如页面格式、文本字段和复选框的样式。

安装

添加依赖

你可以通过运行以下命令来安装 flutter_to_pdf

$ flutter pub add flutter_to_pdf

这会在你的 pubspec.yaml 文件中添加如下内容(并隐式运行 flutter pub get):

dependencies:
  flutter_to_pdf: ^0.2.2

或者,如果你的编辑器支持 flutter pub get,请查阅相关文档以获取更多信息。

导入包

在 Dart 代码中使用以下语句导入 flutter_to_pdf 包:

import 'package:flutter_to_pdf/flutter_to_pdf.dart';

功能特性

  • 导出到 PDF 文档
  • 导出到 PDF 页面
  • 导出到 PDF 小部件
  • 导出选项:包括页面格式、文本字段和复选框的选项

使用方法

基本用法

  1. 创建一个 ExportDelegate 实例。
  2. 使用 ExportFrame 包裹你想导出的小部件,并提供 frameIdexportDelegate
  3. 调用相应的导出函数(如 exportToPdfDocumentexportToPdfPageexportToPdfWidget),并传入 frameId

示例代码

下面是一个完整的示例应用程序,展示了如何使用 flutter_to_pdf 插件将问卷表单和图表导出为 PDF 文档。

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_charts/flutter_charts.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_to_pdf/flutter_to_pdf.dart';

void main() => runApp(const Demo());

class Demo extends StatefulWidget {
  const Demo({super.key});

  @override
  State<Demo> createState() => _DemoState();
}

class _DemoState extends State<Demo> {
  final ExportDelegate exportDelegate = ExportDelegate(
    ttfFonts: {
      'LoveDays': 'assets/fonts/LoveDays-Regular.ttf',
      'OpenSans': 'assets/fonts/OpenSans-Regular.ttf',
    },
  );

  Future<void> saveFile(document, String name) async {
    final Directory dir = await getApplicationDocumentsDirectory();
    final File file = File('${dir.path}/$name.pdf');

    await file.writeAsBytes(await document.save());
    debugPrint('Saved exported PDF at: ${file.path}');
  }

  String currentFrameId = 'questionaireDemo';

  @override
  Widget build(BuildContext context) => MaterialApp(
        theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
          useMaterial3: true,
          tabBarTheme: const TabBarTheme(
            labelColor: Colors.black87,
          ),
        ),
        home: DefaultTabController(
          length: 2,
          child: Scaffold(
            key: GlobalKey<ScaffoldState>(),
            appBar: AppBar(
              backgroundColor: Theme.of(context).colorScheme.primary,
              title: const Text('Flutter to PDF - Demo'),
              bottom: TabBar(
                indicator: const UnderlineTabIndicator(),
                tabs: const [
                  Tab(
                    icon: Icon(Icons.question_answer),
                    text: 'Questionnaire',
                  ),
                  Tab(
                    icon: Icon(Icons.ssid_chart),
                    text: 'Charts & Custom Paint',
                  ),
                ],
                onTap: (int value) {
                  setState(() {
                    currentFrameId =
                        value == 0 ? 'questionaireDemo' : 'captureWrapperDemo';
                  });
                },
              ),
            ),
            bottomSheet: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                TextButton(
                  onPressed: () async {
                    final ExportOptions overrideOptions = ExportOptions(
                      textFieldOptions: TextFieldOptions.uniform(
                        interactive: false,
                      ),
                      checkboxOptions: CheckboxOptions.uniform(
                        interactive: false,
                      ),
                    );
                    final pdf = await exportDelegate.exportToPdfDocument(
                        currentFrameId,
                        overrideOptions: overrideOptions);
                    saveFile(pdf, 'static-example');
                  },
                  child: const Row(
                    children: [
                      Text('Export as static'),
                      Icon(Icons.save_alt_outlined),
                    ],
                  ),
                ),
                TextButton(
                  onPressed: () async {
                    final pdf = await exportDelegate
                        .exportToPdfDocument(currentFrameId);
                    saveFile(pdf, 'interactive-example');
                  },
                  child: const Row(
                    children: [
                      Text('Export as interactive'),
                      Icon(Icons.save_alt_outlined),
                    ],
                  ),
                ),
              ],
            ),
            body: TabBarView(
              children: [
                ExportFrame(
                  frameId: 'questionaireDemo',
                  exportDelegate: exportDelegate,
                  child: const QuestionnaireExample(),
                ),
                ExportFrame(
                  frameId: 'captureWrapperDemo',
                  exportDelegate: exportDelegate,
                  child: const CaptureWrapperExample(),
                ),
              ],
            ),
          ),
        ),
      );
}

class QuestionnaireExample extends StatefulWidget {
  const QuestionnaireExample({super.key});

  @override
  State<QuestionnaireExample> createState() => _QuestionnaireExampleState();
}

class _QuestionnaireExampleState extends State<QuestionnaireExample> {
  final firstNameController = TextEditingController();
  final lastNameController = TextEditingController();
  final dateOfBirthController = TextEditingController();
  final placeOfBirthController = TextEditingController();
  final countryOfBirthController = TextEditingController();

  bool acceptLorem = false;
  bool monday = false;
  bool tuesday = false;
  bool wednesday = false;
  bool thursday = false;
  bool friday = false;
  bool saturday = false;
  bool sunday = false;

  @override
  Widget build(BuildContext context) => Padding(
        padding: const EdgeInsets.symmetric(horizontal: 4.0),
        child: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                crossAxisAlignment: CrossAxisAlignment.end,
                children: [
                  const Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Dunef UG (haftungsbeschränkt)'),
                      Text(
                        'Questionnaire',
                        style: TextStyle(
                          fontSize: 25,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ],
                  ),
                  if (MediaQuery.of(context).size.width > 425)
                    const Row(
                      children: [
                        Text(
                          'Date: ',
                          style: TextStyle(color: Colors.grey),
                        ),
                        Text('04.04.2023'),
                      ],
                    ),
                  SizedBox(
                    height: 50,
                    child: Image.asset('assets/logo.png'),
                  ),
                ],
              ),
              const SizedBox(height: 16),
              Row(
                children: [
                  Container(
                    height: 100,
                    width: 100,
                    decoration: const BoxDecoration(
                      shape: BoxShape.circle,
                      image: DecorationImage(
                        image: NetworkImage('http://i.pravatar.cc/100'),
                      ),
                    ),
                  ),
                  const SizedBox(width: 16),
                  Expanded(child: buildNameFields(MediaQuery.of(context).size.width)),
                ],
              ),
              const SizedBox(height: 16),
              buildBirthFields(MediaQuery.of(context).size.width),
              const Padding(
                padding: EdgeInsets.symmetric(vertical: 16),
                child: Divider(),
              ),
              const Text(
                'Lorem ipsum dolor sit amet...',
                style: TextStyle(fontFamily: 'LoveDays'),
              ),
              const SizedBox(height: 8),
              Row(
                children: [
                  Checkbox(
                    key: const Key('acceptLorem'),
                    value: acceptLorem,
                    onChanged: (newValue) => setState(() {
                      acceptLorem = newValue ?? false;
                    }),
                  ),
                  const SizedBox(width: 8),
                  const Text(
                    'I hereby accept the terms of the Lorem Ipsum.',
                    style: TextStyle(fontFamily: 'OpenSans'),
                  ),
                ],
              ),
              const Padding(
                padding: EdgeInsets.symmetric(vertical: 16),
                child: Divider(),
              ),
              const Padding(
                padding: EdgeInsets.only(bottom: 8.0),
                child: Text(
                  'Please select your availability:',
                  style: TextStyle(
                    fontSize: 20,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ),
              Table(
                children: [
                  const TableRow(
                    children: [
                      Center(child: Text('Monday', maxLines: 1)),
                      Center(child: Text('Tuesday', maxLines: 1)),
                      Center(child: Text('Wednesday', maxLines: 1)),
                      Center(child: Text('Thursday', maxLines: 1)),
                      Center(child: Text('Friday', maxLines: 1)),
                      Center(child: Text('Saturday', maxLines: 1)),
                      Center(child: Text('Sunday', maxLines: 1)),
                    ],
                  ),
                  TableRow(
                    children: [
                      Checkbox(
                        key: const Key('monday'),
                        value: monday,
                        onChanged: (newValue) => setState(() {
                          monday = newValue ?? false;
                        }),
                      ),
                      Checkbox(
                        key: const Key('tuesday'),
                        value: tuesday,
                        onChanged: (newValue) => setState(() {
                          tuesday = newValue ?? false;
                        }),
                      ),
                      Checkbox(
                        key: const Key('wednesday'),
                        value: wednesday,
                        onChanged: (newValue) => setState(() {
                          wednesday = newValue ?? false;
                        }),
                      ),
                      Checkbox(
                        key: const Key('thursday'),
                        value: thursday,
                        onChanged: (newValue) => setState(() {
                          thursday = newValue ?? false;
                        }),
                      ),
                      Checkbox(
                        key: const Key('friday'),
                        value: friday,
                        onChanged: (newValue) => setState(() {
                          friday = newValue ?? false;
                        }),
                      ),
                      Checkbox(
                        key: const Key('saturday'),
                        value: saturday,
                        onChanged: (newValue) => setState(() {
                          saturday = newValue ?? false;
                        }),
                      ),
                      Checkbox(
                        key: const Key('sunday'),
                        value: sunday,
                        onChanged: (newValue) => setState(() {
                          sunday = newValue ?? false;
                        }),
                      ),
                    ],
                  ),
                ],
              ),
              const Padding(
                padding: EdgeInsets.symmetric(vertical: 16),
                child: Divider(),
              ),
              const Text(
                'Thank you for filling out our questionnaire...',
                style: TextStyle(
                  fontSize: 20,
                  fontWeight: FontWeight.bold,
                ),
              ),
              const SizedBox(height: 50),
            ],
          ),
        ),
      );

  Widget buildNameFields(double screenWidth) {
    List<Widget> children = [
      TextField(
        key: const Key('firstName'),
        controller: firstNameController,
        decoration: const InputDecoration(
            labelText: 'First name', border: OutlineInputBorder()),
      ),
      TextField(
        key: const Key('lastName'),
        controller: lastNameController,
        decoration: const InputDecoration(
            labelText: 'Last name', border: OutlineInputBorder()),
      ),
    ];

    if (screenWidth > 480) {
      return SizedBox(
        height: 50,
        child: Row(
          children: children
              .map((Widget e) => Expanded(
                      child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 5.0),
                    child: e,
                  )))
              .toList(),
        ),
      );
    } else {
      return Column(
        children: children
            .map((Widget e) => Padding(
                  padding: const EdgeInsets.only(bottom: 8.0),
                  child: SizedBox(
                    height: 50,
                    child: e,
                  ),
                ))
            .toList(),
      );
    }
  }

  Widget buildBirthFields(double screenWidth) {
    List<Widget> children = [
      TextField(
        key: const Key('dateOfBirth'),
        controller: dateOfBirthController,
        decoration: const InputDecoration(
            labelText: 'DateOfBirth', border: OutlineInputBorder()),
      ),
      TextField(
        key: const Key('placeOfBrith'),
        controller: placeOfBirthController,
        decoration: const InputDecoration(
            labelText: 'Place of birth', border: OutlineInputBorder()),
      ),
      TextField(
        key: const Key('countryOfBirth'),
        controller: countryOfBirthController,
        decoration: const InputDecoration(
            labelText: 'Country of birth', border: OutlineInputBorder()),
      ),
    ];

    if (screenWidth > 480) {
      return SizedBox(
        height: 50,
        child: Row(
          children: children
              .map((Widget e) => Expanded(
                      child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 5.0),
                    child: e,
                  )))
              .toList(),
        ),
      );
    } else {
      return Column(
        children: children
            .map((Widget e) => Padding(
                  padding: const EdgeInsets.only(bottom: 8.0),
                  child: SizedBox(
                    height: 50,
                    child: e,
                  ),
                ))
            .toList(),
      );
    }
  }
}

class CaptureWrapperExample extends StatelessWidget {
  const CaptureWrapperExample({super.key});

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Column(
        children: [
          SizedBox(
            height: 400,
            width: 400,
            child: CaptureWrapper(
              key: const Key('Chart'),
              child: buildChart(),
            ),
          ),
          CaptureWrapper(
            key: const Key('CustomPaint'),
            child: CustomPaint(
              size: const Size(300, 300),
              painter: HousePainter(),
            ),
          ),
        ],
      ),
    );
  }

  Widget buildChart() {
    LabelLayoutStrategy? xContainerLabelLayoutStrategy;
    ChartData chartData;
    ChartOptions chartOptions = const ChartOptions();
    // Example shows a mix of positive and negative data values.
    chartData = ChartData(
      dataRows: const [
        [2000.0, 1800.0, 2200.0, 2300.0, 1700.0, 1800.0],
        [1100.0, 1000.0, 1200.0, 800.0, 700.0, 800.0],
        [0.0, 100.0, -200.0, 150.0, -100.0, -150.0],
        [-800.0, -400.0, -300.0, -400.0, -200.0, -250.0],
      ],
      xUserLabels: const ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
      dataRowsLegends: const [
        'Big Corp',
        'Medium Corp',
        'Print Shop',
        'Bar',
      ],
      chartOptions: chartOptions,
    );
    var lineChartContainer = LineChartTopContainer(
      chartData: chartData,
      xContainerLabelLayoutStrategy: xContainerLabelLayoutStrategy,
    );
    return LineChart(
      painter: LineChartPainter(
        lineChartContainer: lineChartContainer,
      ),
    );
  }
}

class HousePainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.brown
      ..style = PaintingStyle.fill;

    // Draw the house body
    const body = Rect.fromLTWH(50, 100, 200, 200);
    canvas.drawRect(body, paint);

    // Draw the roof
    final roofPath = Path()
      ..moveTo(150, 20)
      ..lineTo(280, 100)
      ..lineTo(20, 100)
      ..close();
    paint.color = Colors.red;
    canvas.drawPath(roofPath, paint);

    // Draw the door
    const door = Rect.fromLTWH(125, 230, 50, 70);
    paint.color = Colors.black;
    canvas.drawRect(door, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

此示例展示了如何创建一个包含问卷表单和图表的应用程序,并提供了两个按钮用于将当前视图导出为静态或交互式的 PDF 文件。希望这个例子能帮助你更好地理解和使用 flutter_to_pdf 插件!


更多关于Flutter生成PDF文档插件flutter_to_pdf的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter生成PDF文档插件flutter_to_pdf的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个使用 flutter_to_pdf 插件在 Flutter 中生成 PDF 文档的示例代码。这个插件允许你在 Flutter 应用中创建和操作 PDF 文件。

首先,确保你已经在 pubspec.yaml 文件中添加了 flutter_to_pdf 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_to_pdf: ^4.0.0  # 请检查最新版本号

然后运行 flutter pub get 来获取依赖。

接下来是一个完整的示例代码,展示如何使用 flutter_to_pdf 插件生成一个简单的 PDF 文档:

import 'package:flutter/material.dart';
import 'package:flutter_to_pdf/flutter_to_pdf.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter PDF Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: _generatePdf,
            child: Text('Generate PDF'),
          ),
        ),
      ),
    );
  }

  Future<void> _generatePdf() async {
    // 获取应用文档目录
    final directory = await getApplicationDocumentsDirectory();
    final path = directory.path + '/sample.pdf';

    // 创建 PDF 文档
    final pdf = await FlutterToPdf.createPdf(
      builder: (PdfDocument.Page page) {
        page.drawText('Hello, Flutter PDF!', with: PdfCanvas(page), at: PdfPoint(100, 800));
        page.drawText('This is a sample PDF document.', with: PdfCanvas(page), at: PdfPoint(100, 750));
      },
    );

    // 保存 PDF 到文件
    final file = File(path);
    await file.writeAsBytes(pdf);

    // 打开 PDF 文件(在 Android 和 iOS 上可能需要不同的处理方式)
    if (Platform.isAndroid) {
      // Android 上使用文件选择器打开
      await FlutterToPdf.openPdf(path: path);
    } else if (Platform.isIOS) {
      // iOS 上使用 UIActivityViewController 分享
      await FlutterToPdf.sharePdf(bytes: pdf, filename: 'sample.pdf');
    }
  }
}

代码解释

  1. 依赖导入

    • flutter_to_pdf 用于生成 PDF 文档。
    • path_provider 用于获取应用的文档目录路径。
  2. 主应用结构

    • 使用 MaterialAppScaffold 创建一个简单的用户界面。
    • 在中心位置放置一个按钮,点击按钮时调用 _generatePdf 方法。
  3. 生成 PDF

    • 使用 getApplicationDocumentsDirectory 获取应用的文档目录路径。
    • 使用 FlutterToPdf.createPdf 方法创建一个 PDF 文档。在 builder 回调中,你可以使用 PdfCanvasPdfPoint 等类来绘制内容。
    • 将生成的 PDF 保存到文件中。
  4. 打开或分享 PDF

    • 根据平台的不同,使用 FlutterToPdf.openPdfFlutterToPdf.sharePdf 方法打开或分享生成的 PDF 文件。

这个示例展示了如何使用 flutter_to_pdf 插件生成一个简单的 PDF 文档,并在设备上打开或分享它。你可以根据需要进一步自定义 PDF 的内容和样式。

回到顶部