12

我正在开发一个 iOS/Android/Web 应用程序,在该应用程序中,我使用此处的 pdf 插件根据用户输入生成 PDF:https ://pub.dev/packages/pdf 。在 iOS 和 Android 上,我可以访问文件系统,所以我可以先保存生成的 pdf,然后使用任何 pdf 显示插件打开它。

但是,据我所知,对于 Flutter web,无法访问任何类型的文件系统。我见过的所有 pdf 渲染插件都假定 pdf 文件要么存储在文件中,要么存储在网络上。我在看:

  • flutter_full_pdf_viewer:^1.0.6
  • flutter_pdf_viewer:^0.6.1
  • pdf_render: ^0.57.2

如何在flutter web上显示这个应用程序生成的pdf?有没有办法将 Uint8List 数据欺骗为文件,或者可能有另一种方法来解决这个问题?(我能想到的解决这个问题的唯一方法是在生成文件后将文件上传到网络以显示它,但这似乎有点过头了。)

4

4 回答 4

21

我们可以在没有额外插件的情况下做到这一点,因为网络浏览器本身可以显示(或下载)pdf文档。

为 pdf 2.0.0 和更新版本更新

由于方法save现在返回 Future,按钮处理程序变得异步(当然你可以使用FurureBuilder或其他东西)。

import 'package:flutter/material.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:universal_html/html.dart' as html;

class PdfLabPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final pdf = pw.Document();
    pdf.addPage(pw.Page(
        pageFormat: PdfPageFormat.a4,
        build: (pw.Context context) {
          return pw.Center(
            child: pw.Text('Hello World'),
          );
        }));

    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: () async {
                final bytes = await pdf.save();
                final blob = html.Blob([bytes], 'application/pdf');
                final url = html.Url.createObjectUrlFromBlob(blob);
                html.window.open(url, '_blank');
                html.Url.revokeObjectUrl(url);
              },
              child: Text('Open'),
            ),
            ElevatedButton(
              onPressed: () async {
                final bytes = await pdf.save();
                final blob = html.Blob([bytes], 'application/pdf');
                final url = html.Url.createObjectUrlFromBlob(blob);
                final anchor = html.AnchorElement()
                  ..href = url
                  ..style.display = 'none'
                  ..download = 'some_name.pdf';
                html.document.body?.children.add(anchor);
                anchor.click();
                html.document.body?.children.remove(anchor);
                html.Url.revokeObjectUrl(url);
              },
              child: Text('Download'),
            ),
          ],
        ),
      ),
    );
  }
}

原始答案

import 'package:flutter/material.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:universal_html/html.dart' as html;

class PdfDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final pdf = pw.Document();
    pdf.addPage(pw.Page(
        pageFormat: PdfPageFormat.a4,
        build: (pw.Context context) {
          return pw.Center(
            child: pw.Text("Hello World"),
          );
        }));
    final bytes = pdf.save();
    final blob = html.Blob([bytes], 'application/pdf');

    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          children: <Widget>[
            RaisedButton(
              child: Text("Open"),
              onPressed: () {
                final url = html.Url.createObjectUrlFromBlob(blob);
                html.window.open(url, "_blank");
                html.Url.revokeObjectUrl(url);
              },
            ),
            RaisedButton(
              child: Text("Download"),
              onPressed: () {
                final url = html.Url.createObjectUrlFromBlob(blob);
                final anchor =
                    html.document.createElement('a') as html.AnchorElement
                      ..href = url
                      ..style.display = 'none'
                      ..download = 'some_name.pdf';
                html.document.body.children.add(anchor);
                anchor.click();
                html.document.body.children.remove(anchor);
                html.Url.revokeObjectUrl(url);
              },
            ),
          ],
          mainAxisAlignment: MainAxisAlignment.center,
        ),
      ),
    );
  }
}
于 2020-04-23T19:58:40.993 回答
6

2021 年 2 月 12 日更新:

经过多轮谷歌搜索和深入研究文档,我已经开始工作了。以下是要点:

1- 无需<app_directory>/web/index.html像我之前指出的那样将任何 javascript 代码插入文件中。对不起!我正在学习颤振/飞镖...

2- 我必须创建一个Future<html.Blob>方法(请看一下myGetBlobPdfContent() async {...})来处理 html/pdf 内容,保存(await pdf.save())并返回一个Blob值。我不是说它与“不完整的未来”有关的合适的人,在 pdf 的东西上扔了一个空点......

3- 此外,这些 RaisedButton 的onPressed方法已更改为异步,以允许将 pdf 内容作为 Blob 值 ( createObjectUrlFromBlob( await myGetBlobPdfContent());) 获取。

4-最后,我收到了警告消息:Helvetica has no Unicode support see https://github.com/DavBfr/dart_pdf/wiki/Fonts-Management,所以我安装了自定义字体,如本食谱说明,在这个例子中,我设置了 Arial ttf ( data = await rootBundle.load("fonts/arial.ttf");),这些警告消息消失了。

5- 这是我pubspec.yaml的片段:

...
dependencies:
  flutter:
    sdk: flutter
  pdf: ^2.1.0
  universal_html: ^1.2.4

...

fonts:
  - family: Arial
    fonts:
      - asset: fonts/arial.ttf
      - asset: fonts/arialbd.ttf
        weight: 700
      - asset: fonts/ariali.ttf
        style: italic
  ...

6-还有一个最小的工作示例(完整main.dart文件):

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:universal_html/html.dart' as html;

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: PdfDemo(),
    );
  }
}

class PdfDemo extends StatelessWidget {
  Future<html.Blob> myGetBlobPdfContent() async {
    var data = await rootBundle.load("fonts/arial.ttf");
    var myFont = pw.Font.ttf(data);
    var myStyle = pw.TextStyle(font: myFont, fontSize: 36.0);

    final pdf = pw.Document();
    pdf.addPage(pw.Page(
        pageFormat: PdfPageFormat.a4,
        build: (pw.Context context) {
          return pw.Center(
            child: pw.Text(
              "Hello World",
              style: myStyle,
            ),
            // child: pw.Text("Hello World", style: myStyle),
          );
        }));
    final bytes = await pdf.save();
    html.Blob blob = html.Blob([bytes], 'application/pdf');
    return blob;
  }

  @override
  Widget build(BuildContext context) {
    myGetBlobPdfContent();
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          children: <Widget>[
            RaisedButton(
              child: Text("Open"),
              onPressed: () async {
                final url = html.Url.createObjectUrlFromBlob(
                    await myGetBlobPdfContent());
                html.window.open(url, "_blank");
                html.Url.revokeObjectUrl(url);
              },
            ),
            RaisedButton(
              child: Text("Download"),
              onPressed: () async {
                final url = html.Url.createObjectUrlFromBlob(
                    await myGetBlobPdfContent());
                final anchor =
                    html.document.createElement('a') as html.AnchorElement
                      ..href = url
                      ..style.display = 'none'
                      ..download = 'some_name.pdf';
                html.document.body.children.add(anchor);
                anchor.click();
                html.document.body.children.remove(anchor);
                html.Url.revokeObjectUrl(url);
              },
            ),
          ],
          mainAxisAlignment: MainAxisAlignment.center,
        ),
      ),
    );
  }
}

7- 最后,PDF 生成(和下载)按预期工作:

在此处输入图像描述

希望这可以帮助。

初稿(仅保留信息日志)

@Spatz 的答案可能会解决网络平台的任何 pdf 问题。我不知道为什么它没有按预期工作,至少对于我的设置(见下文)。

我的意思是网页尝试打开 pdf 并引发错误:Failed to load PDF document.

在此处输入图像描述

此外,该示例some_name.pdf成功下载了 pdf 文件 ( ),但当我尝试打开 pdf 文件 ( error: file corrupted) 时失败。

我看到这篇文章谈到了将 javascript 代码插入 <app_directory>/web/index.html 文件。我已经插入了这些行,也没有成功。

我使用的源代码是一个main.dart文件:

import 'package:flutter/material.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:universal_html/html.dart' as html;

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: PdfDemo(),
    );
  }
}

class PdfDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final pdf = pw.Document();
    pdf.addPage(pw.Page(
        pageFormat: PdfPageFormat.a4,
        build: (pw.Context context) {
          return pw.Center(
            child: pw.Text("Hello World"),
          );
        }));
    final bytes = pdf.save();
    final blob = html.Blob([bytes], 'application/pdf');

    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          children: <Widget>[
            RaisedButton(
              child: Text("Open"),
              onPressed: () {
                final url = html.Url.createObjectUrlFromBlob(blob);
                html.window.open(url, "_blank");
                html.Url.revokeObjectUrl(url);
              },
            ),
            RaisedButton(
              child: Text("Download"),
              onPressed: () {
                final url = html.Url.createObjectUrlFromBlob(blob);
                final anchor =
                    html.document.createElement('a') as html.AnchorElement
                      ..href = url
                      ..style.display = 'none'
                      ..download = 'some_name.pdf';
                html.document.body.children.add(anchor);
                anchor.click();
                html.document.body.children.remove(anchor);
                html.Url.revokeObjectUrl(url);
              },
            ),
          ],
          mainAxisAlignment: MainAxisAlignment.center,
        ),
      ),
    );
  }
}

我的设置是:

  • Flutter(Channel beta,1.26.0-17.3.pre,在 Microsoft Windows [版本 10.0.19042.746] 上,语言环境 en-150)
  • 安卓设备安卓工具链开发(安卓SDK版本30.0.3)
  • Chrome - 为网络开发
  • Android Studio(版本 4.1.0)

谢谢 :)

于 2021-02-10T15:30:42.557 回答
1

为了解决“无法打开 PDF 文件”...这是由于文件格式无效...在现有代码中添加以下两行:

 var  data = await pdf.save();
 Uint8List bytes = Uint8List.fromList(data);

完整的代码如下:

  var  data = await pdf.save();
    Uint8List bytes = Uint8List.fromList(data);
    final blob = html.Blob([bytes], 'application/pdf');
    final url = html.Url.createObjectUrlFromBlob(blob);
    final anchor =
    html.document.createElement('a') as html.AnchorElement
      ..href = url
      ..style.display = 'none'
      ..download = 'some_name.pdf';

    html.document.body.children.add(anchor);
    anchor.click();
    html.document.body.children.remove(anchor);
    html.Url.revokeObjectUrl(url);
于 2021-08-03T10:03:10.780 回答
0

我在上面的代码中遇到了问题:

            final url = html.Url.createObjectUrlFromBlob(
                await myGetBlobPdfContent());
            html.window.open(url, "_blank");
            html.Url.revokeObjectUrl(url);

只能在本地主机上工作,但不能在 WEB HOSTING 中工作。请帮助。

于 2022-02-04T16:10:31.690 回答