Flutter web - 显示应用程序中生成的 pdf 文件。(Uint8List 格式)

Wil*_*ill 11 pdf file flutter flutter-web uint8list

我正在开发一个 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 数据欺骗为文件,或者可能是另一种方法来解决这个问题?(我能想到的唯一解决方法是在生成文件后将文件上传到网络以显示它,但这似乎有点过分了。)

Spa*_*atz 19

我们可以在没有额外插件的情况下做到这一点,因为网络浏览器本身可以显示(或下载)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'),
            ),
          ],
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

原答案

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,
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)


Ale*_*eia 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
  ...
Run Code Online (Sandbox Code Playgroud)

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,
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

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,
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

我的设置是:

  • Flutter(Channel beta,1.26.0-17.3.pre,在 Microsoft Windows [版本 10.0.19042.746],区域设置 en-150)
  • Android 设备的 Android 工具链开发(Android SDK 版本 30.0.3)
  • Chrome - 为网络开发
  • Android Studio(版本 4.1.0)

谢谢 :)